The following script moves selected consecutive rows in one of four ways described in the question. It is triggered by custom menu items (Custom > Move rows...) which is created by onOpen
function whenever the spreadsheet is opened. Unfortunately, there is no way to assign keyboard shortcuts to script functions.
The structure of functions is simple: insert rows, copy the range (including formulas and formatting), delete old copy of the range. The helper function getData
eliminates the need to repeat the same data-gathering in each of four functions.
function onOpen() {
var menu = [{name: "Move rows up", functionName: "moveUp"}, {name: "Move rows down", functionName: "moveDown"}, {name: "Move rows to top", functionName: "moveTop"}, {name: "Move rows to bottom", functionName: "moveBottom"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Custom", menu);
}
function moveDown(){
var d = getData();
d.sheet.insertRowsAfter(d.row+d.height, d.height);
d.selection.copyTo(d.sheet.getRange(d.row+d.height+1, 1, d.height, d.width));
d.sheet.deleteRows(d.row, d.height);
}
function moveBottom(){
var d = getData();
d.sheet.insertRowsAfter(d.last, d.height);
d.selection.copyTo(d.sheet.getRange(d.last+1, 1, d.height, d.width));
d.sheet.deleteRows(d.row, d.height);
}
function moveUp(){
var d = getData();
d.sheet.insertRowsBefore(d.row-1, d.height);
d.sheet.getRange(d.row+d.height, 1, d.height, d.width).copyTo(d.sheet.getRange(d.row-1, 1, d.height, d.width));
d.sheet.deleteRows(d.row+d.height, d.height);
}
function moveTop(){
var d = getData();
d.sheet.insertRowsBefore(1, d.height);
d.sheet.getRange(d.row+d.height, 1, d.height, d.width).copyTo(d.sheet.getRange(1, 1, d.height, d.width));
d.sheet.deleteRows(d.row+d.height, d.height);
}
function getData() {
var d = {};
d.sheet = SpreadsheetApp.getActiveSheet();
d.selection = d.sheet.getActiveRange();
d.row = d.selection.getRowIndex();
d.height = d.selection.getHeight();
d.width = d.selection.getWidth();
d.last = d.sheet.getLastRow();
return d;
}