Bladeren bron

CDB: Add ctrl selection and added some qol improvements

lviguier 6 maanden geleden
bovenliggende
commit
bd3152274d
6 gewijzigde bestanden met toevoegingen van 436 en 265 verwijderingen
  1. 28 4
      bin/cdb.css
  2. 17 4
      bin/cdb.less
  3. 1 1
      hide/comp/cdb/Cell.hx
  4. 254 125
      hide/comp/cdb/Cursor.hx
  5. 121 126
      hide/comp/cdb/Editor.hx
  6. 15 5
      hide/comp/cdb/Table.hx

+ 28 - 4
bin/cdb.css

@@ -12,7 +12,7 @@
   cursor: pointer;
 }
 .refviewer a:hover {
-  color: #4772b3;
+  color: #2c5d87;
 }
 .cdb-view.cdb-diff .cdb-sheet th,
 .cdb-view.cdb-diff th.start {
@@ -25,24 +25,48 @@
   outline: none;
 }
 .cdb:focus .cdb-sheet td.cursorView {
-  outline: 1px solid #ccc;
+  border: 2px solid #ccc !important;
 }
 .cdb:focus .cdb-sheet tr.cursorLine {
   background-color: #262d35;
 }
 .cdb:focus .cdb-sheet tr.cursorLine td.start,
 .cdb:focus .cdb-sheet tr.cursorLine td.t_readonly {
-  background-color: #313a48;
+  color: #aaaaaa;
+  background-color: #1f427a;
 }
 .cdb:focus .cdb-sheet tr.selected,
 .cdb:focus .cdb-sheet td.selected {
   background-color: #313a48;
 }
+.cdb:focus .cdb-sheet tr.selected.top td.start,
+.cdb:focus .cdb-sheet td.selected.top td.start {
+  border-top: 2px solid #ccc;
+}
+.cdb:focus .cdb-sheet tr.selected.bot td.start,
+.cdb:focus .cdb-sheet td.selected.bot td.start {
+  border-bottom: 2px solid #ccc;
+}
 .cdb:focus .cdb-sheet tr.selected td.start,
 .cdb:focus .cdb-sheet td.selected td.start,
 .cdb:focus .cdb-sheet tr.selected td.t_readonly,
 .cdb:focus .cdb-sheet td.selected td.t_readonly {
-  background-color: #313a48;
+  border-left: 2px solid #ccc;
+  border-right: 2px solid #ccc;
+  color: #aaaaaa;
+  background-color: #0b57d0;
+}
+.cdb:focus .cdb-sheet td.selected.top {
+  border-top: 1px solid #ccc;
+}
+.cdb:focus .cdb-sheet td.selected.right {
+  border-right: 1px solid #ccc;
+}
+.cdb:focus .cdb-sheet td.selected.bot {
+  border-bottom: 1px solid #ccc;
+}
+.cdb:focus .cdb-sheet td.selected.left {
+  border-left: 1px solid #ccc;
 }
 .cdb .cdb-sheet {
   width: 100%;

+ 17 - 4
bin/cdb.less

@@ -35,24 +35,37 @@
 	}
 
 	&:focus .cdb-sheet {
-
 		td.cursorView {
-			outline : 1px solid #ccc;
+			border: 2px solid #ccc !important;
 		}
 
 		tr.cursorLine {
 			background-color: #262d35;
 			td.start, td.t_readonly {
-				background-color: #313a48;
+				color: rgb(170, 170, 170);
+				background-color: #1f427a;
 			}
 		}
 
 		tr.selected, td.selected {
 			background-color : #313a48;
+
+			&.top td.start { border-top: 2px solid #ccc; }
+			&.bot td.start { border-bottom: 2px solid #ccc; }
 			td.start, td.t_readonly {
-				background-color: #313a48;
+				border-left: 2px solid #ccc;
+				border-right: 2px solid #ccc;
+				color: rgb(170, 170, 170);
+				background-color: #0b57d0;
 			}
 		}
+
+		td.selected {
+			&.top { border-top: 1px solid #ccc; }
+			&.right { border-right: 1px solid #ccc; }
+			&.bot { border-bottom: 1px solid #ccc; }
+			&.left { border-left: 1px solid #ccc; }
+		}
 	}
 
 

+ 1 - 1
hide/comp/cdb/Cell.hx

@@ -77,7 +77,7 @@ class Cell {
 			root.classList.add("t_loc");
 
 		elementHtml.addEventListener("click", function(e) {
-			editor.cursor.clickCell(this, e.shiftKey);
+			editor.cursor.clickCell(this, e.shiftKey, e.ctrlKey);
 			e.stopPropagation();
 		});
 

+ 254 - 125
hide/comp/cdb/Cursor.hx

@@ -1,20 +1,26 @@
 package hide.comp.cdb;
 
+typedef Selection = {
+	var x1 : Int;
+	var y1 : Int;
+	var x2 : Int;
+	var y2 : Int;
+	var ?origin : { x: Int, y: Int };
+}
 
 typedef CursorState = {
 	var sheet : String;
 	var x : Int;
 	var y : Int;
-	var select : Null<{ x : Int, y : Int }>;
+	var selection : Array<Selection>;
 }
 
 class Cursor {
-
 	var editor : Editor;
 	public var table : Table;
 	public var x : Int;
 	public var y : Int;
-	public var select : Null<{ x : Int, y : Int }>;
+	public var selection : Array<Selection>;
 	public var onchange : Void -> Void;
 
 	public function new(editor) {
@@ -22,22 +28,6 @@ class Cursor {
 		set();
 	}
 
-	public function setState(state : CursorState, ?table : Table) {
-		if( state == null )
-			set(table);
-		else
-			set(table, state.x, state.y, state.select);
-	}
-
-	public function getState() : CursorState {
-		return table == null ? null : {
-			sheet : table.sheet.getPath(),
-			x : x,
-			y : y,
-			select : Reflect.copy(select)
-		};
-	}
-
 	public function set( ?t:Table, ?x=0, ?y=0, ?sel, update = true ) {
 		if( t != null ) {
 			for( t2 in editor.tables )
@@ -49,7 +39,7 @@ class Cursor {
 		this.table = t;
 		this.x = x;
 		this.y = y;
-		this.select = sel;
+		this.selection = sel;
 		var ch = onchange;
 		if( ch != null ) {
 			onchange = null;
@@ -58,48 +48,6 @@ class Cursor {
 		if( update ) this.update();
 	}
 
-	public function setDefault(line, column) {
-		set(editor.tables[0], column, line);
-	}
-
-	public function getLine() {
-		if( table == null ) return null;
-		return table.lines[y];
-	}
-
-	public function getSelectedLines() {
-		if( table == null || x != -1 )
-			return [];
-		var selected = getSelection();
-		return [for( iy in selected.y1...(selected.y2 + 1) ) table.lines[iy]];
-	}
-
-	public function getCell() {
-		var line = getLine();
-		if( line == null ) return null;
-		return line.cells[x];
-	}
-
-	public function save() {
-		if( table == null ) return null;
-		return { sheet : table.sheet, x : x, y : y, select : select == null ? null : { x : select.x, y : select.y} };
-	}
-
-	public function load( s ) {
-		if( s == null )
-			return false;
-		var table = null;
-		for( t in editor.tables )
-			if( t.sheet == s.sheet ) {
-				table = t;
-				break;
-			}
-		if( table == null )
-			return false;
-		set(table, s.x, s.y, s.select);
-		return true;
-	}
-
 	public function move( dx : Int, dy : Int, shift : Bool, ctrl : Bool, ?overflow = false ) {
 		if( table == null )
 			table = editor.tables[0];
@@ -107,7 +55,7 @@ class Cursor {
 			if( dy != 0 ) {
 				if( table == null )
 					return;
-				if( select == null )
+				if( selection == null )
 					editor.moveLine(getLine(), dy);
 				else
 					editor.moveLines(getSelectedLines(), dy);
@@ -141,10 +89,22 @@ class Cursor {
 			dy = allLines.index(new Element(targetLine)) - allLines.index(line.element);
 		}
 
-		if( !shift )
-			select = null;
-		else if( select == null )
-			select = { x : x, y : y };
+		// Allow area selection while moving cursor with arrows and holding shift
+		if (shift) {
+			if (selection != null && selection[selection.length - 1].origin != null) {
+				var prev = selection[selection.length - 1];
+				selection = [ { x1: Std.int(hxd.Math.min(prev.origin.x, x + dx)), y1: Std.int(hxd.Math.min(prev.origin.y, y + dy)),
+					x2: Std.int(hxd.Math.max(prev.origin.x, x + dx)), y2: Std.int(hxd.Math.max(prev.origin.y, y + dy)),
+					origin: prev.origin }];
+			}
+			else {
+				addElementToSelection(line.table, line, x + dx, y + dy, true, false);
+				selection[selection.length -1].origin = { x: x, y: y };
+			}
+		}
+		else
+			selection = null;
+
 		var minX = table.displayMode == Table ? -1 : 0;
 		var maxX = table.columns.length;
 		var maxY = table.lines.length;
@@ -179,105 +139,274 @@ class Cursor {
 		update();
 	}
 
+	public function setState(state : CursorState, ?table : Table) {
+		if( state == null )
+			set(table);
+		else
+			set(table, state.x, state.y, state.selection);
+	}
+
+	public function getState() : CursorState {
+		return table == null ? null : {
+			sheet : table.sheet.getPath(),
+			x : x,
+			y : y,
+			selection : selection?.copy()
+		};
+	}
+
+	public function setDefault(line, column) {
+		set(editor.tables[0], column, line);
+	}
+
+	public function getLine() {
+		if( table == null ) return null;
+		return table.lines[y];
+	}
+
+	public function getCell() {
+		var line = getLine();
+		if( line == null ) return null;
+		return line.cells[x];
+	}
+
+	public function save() {
+		if( table == null ) return null;
+		return { sheet : table.sheet, x : x, y : y, selection : selection };
+	}
+
+	public function load( s ) {
+		if( s == null )
+			return false;
+		var table = null;
+		for( t in editor.tables )
+			if( t.sheet == s.sheet ) {
+				table = t;
+				break;
+			}
+		if( table == null )
+			return false;
+		set(table, s.x, s.y, s.selection);
+		return true;
+	}
+
 	public function hide() {
 		var elt = editor.element;
 		elt.find(".selected").removeClass("selected");
 		elt.find(".cursorView").removeClass("cursorView");
 		elt.find(".cursorLine").removeClass("cursorLine");
+		elt.find(".top").removeClass("top");
+		elt.find(".left").removeClass("left");
+		elt.find(".right").removeClass("right");
+		elt.find(".bot").removeClass("bot");
 	}
 
 	public function update() {
-		var elt = editor.element;
 		hide();
 		if( table == null )
 			return;
-		if( y < 0 ) {
-			y = 0;
-			select = null;
-		}
-		if( y >= table.lines.length ) {
-			y = table.lines.length - 1;
-			select = null;
-		}
-		var max = table.sheet.props.isProps || table.columns == null ? 1 : table.columns.length;
-		if( x >= max ) {
-			x = max - 1;
-			select = null;
-		}
 		var line = getLine();
 		if( line == null )
 			return;
 		if( x < 0 ) {
 			line.element.addClass("selected");
-			if( select != null ) {
-				var cy = y;
-				while( select.y != cy ) {
-					if( select.y > cy ) cy++ else cy--;
-					table.lines[cy].element.addClass("selected");
-				}
-			}
+			line.element.addClass("top");
+			line.element.addClass("bot");
 		} else {
 			var c = line.cells[x];
 			if( c != null ){
 				c.elementHtml.classList.add("cursorView");
 				c.elementHtml.parentElement.classList.add("cursorLine");
 			}
-			if( select != null ) {
-				var s = getSelection();
-				for( y in s.y1...s.y2 + 1 ) {
-					var l = table.lines[y];
-					for( x in s.x1...s.x2+1)
-						l.cells[x].elementHtml.classList.add("selected");
+		}
+
+		// Add selection style to cell and lines that are in a selected area
+		if (selection != null) {
+			for (s in selection) {
+				for (c in getSelectedCells()) {
+					var cellX = c.columnIndex;
+					var cellY = c.line.index;
+					var el = c.elementHtml;
+					el.classList.add("selected");
+					if (cellY == s.y1)
+						el.classList.add("top");
+					if (cellX == s.x1)
+						el.classList.add("left");
+					if (cellX == s.x2)
+						el.classList.add("right");
+					if (cellY == s.y2)
+						el.classList.add("bot");
+				}
+
+				if (s.x1 == -1) {
+					for (l in getSelectedLines()) {
+						var el = l.element;
+						el.addClass("selected");
+						if (l.index == s.y1)
+							el.addClass("top");
+						if (l.index == s.y2)
+							el.addClass("bot");
+					}
 				}
 			}
 		}
+
 		var e = line.element.get(0);
 		if( e != null ) untyped e.scrollIntoViewIfNeeded();
 	}
 
-	public function getSelection() {
-		if( table == null )
+	// Get selected area with line in it. Otherwise return null
+	public function getSelectedAreaIncludingLine(line : Line) {
+		if (selection == null)
 			return null;
-		var x1 = if( x < 0 ) 0 else x;
-		var x2 = if( x < 0 ) table.columns.length-1 else if( select != null ) select.x else x1;
-		var y1 = y;
-		var y2 = if( select != null ) select.y else y1;
-		if( x2 < x1 ) {
-			var tmp = x2;
-			x2 = x1;
-			x1 = tmp;
+		for (s in selection) {
+			if (line.index >= s.y1 && line.index <= s.y2)
+				return s;
 		}
-		if( y2 < y1 ) {
-			var tmp = y2;
-			y2 = y1;
-			y1 = tmp;
+
+		return null;
+	}
+
+	// Get selected area with cell in it. Otherwise return null
+	public function getSelectedAreaIncludingCell(cell : Cell) {
+		if (selection == null)
+			return null;
+		for (s in selection) {
+			if (cell.line.index >= s.y1 && cell.line.index <= s.y2 &&
+				cell.columnIndex >= s.x1 && cell.columnIndex <= s.x2)
+				return s;
 		}
-		return { x1 : x1, x2 : x2, y1 : y1, y2 : y2 };
+
+		return null;
 	}
 
+	public function getLinesFromSelection(sel : Selection) {
+		if (sel == null)
+			return null;
+		return [for( iy in sel.y1...(sel.y2 + 1) ) table.lines[iy]];
+	}
 
-	public function clickLine( line : Line, shiftKey = false ) {
-		var sheet = line.table.sheet;
-		if( shiftKey && this.table == line.table && x < 0 ) {
-			select = { x : -1, y : line.index };
-			update();
-		} else {
-			editor.pushCursorState();
-			set(line.table, -1, line.index);
-			line.table.showSeparator(line);
+	public function getCellsFromSelection(sel : Selection) {
+		if (sel == null)
+			return null;
+
+		var cells = [];
+		if (sel.x1 == -1) {
+			for (y in sel.y1...(sel.y2 + 1)) {
+				for (x in 0...(table.lines[y].cells.length)) {
+					cells.push(table.lines[y].cells[x]);
+				}
+			}
 		}
+		else {
+			for (y in sel.y1...(sel.y2 + 1)) {
+				for (x in sel.x1...(sel.x2 + 1)) {
+					cells.push(table.lines[y].cells[x]);
+				}
+			}
+		}
+
+		return cells;
 	}
 
-	public function clickCell( cell : Cell, shiftKey = false ) {
+	public function getSelectedLines() {
+		var lines = [];
+		if (selection == null)
+			return lines;
+
+		for (s in selection) {
+			var tmp = getLinesFromSelection(s);
+			if (tmp == null)
+				continue;
+
+			lines = lines.concat(tmp);
+		}
+
+		return lines;
+	}
+
+	public function getSelectedCells() {
+		var cells = [];
+		if (selection == null)
+			return cells;
+
+		for (s in selection) {
+			var tmp = getCellsFromSelection(s);
+			if (tmp == null)
+				continue;
+
+			cells = cells.concat(tmp);
+		}
+
+		return cells;
+	}
+
+
+	public function clickLine( line : Line, shiftKey = false, ctrlKey = false ) {
+		addElementToSelection(line.table, line, -1, line.index, shiftKey, ctrlKey);
+		set(line.table, -1, line.index, this.selection);
+	}
+
+	public function clickCell( cell : Cell, shiftKey = false, ctrlKey = false ) {
 		var xIndex = cell.table.displayMode == Table ? cell.columnIndex : 0;
-		if( shiftKey && table == cell.table ) {
-			select = { x : xIndex, y : cell.line.index };
-			update();
-		} else {
+		addElementToSelection(cell.table, cell.line, xIndex, cell.line.index, shiftKey, ctrlKey);
+		set(cell.table, xIndex, cell.line.index, this.selection);
+	}
+
+	public function addElementToSelection(table: Table, line: Line, xIndex : Int, yIndex: Int, shift: Bool = false, ctrl: Bool = false) {
+		var p1 = new h3d.Vector(x, y, 0);
+		var p2 = new h3d.Vector(xIndex, yIndex, 0);
+		if (this.table == table) {
+			if (shift) {
+				var prev = selection != null && selection.length >= 1 ? selection[selection.length - 1] : null;
+				if (prev != null && prev.origin != null)
+					p1 = new h3d.Vector(prev.origin.x, prev.origin.y, 0);
+				selection = [];
+				selection.push({ x1: Std.int(hxd.Math.min(p1.x, p2.x)), x2: Std.int(hxd.Math.max(p1.x, p2.x)),
+					 y1: Std.int(hxd.Math.min(p1.y, p2.y)), y2: Std.int(hxd.Math.max(p1.y, p2.y)),
+					origin: prev != null && prev.origin != null ? prev.origin : {x: x, y: y} });
+				updateSelection();
+				update();
+			}
+			else if(ctrl) {
+				if (selection == null) {
+					selection = [];
+					selection.push({ x1: x, x2: x, y1: y, y2: y });
+				}
+				selection.push({ x1: xIndex, x2: xIndex, y1: yIndex, y2: yIndex });
+				updateSelection();
+				update();
+			}
+			else {
+				selection = [{ x1: xIndex, x2: xIndex, y1: yIndex, y2: yIndex }];
+				update();
+			}
+		}
+		else {
+			table.showSeparator(line);
 			editor.pushCursorState();
-			set(cell.table, xIndex, cell.line.index);
-			cell.table.showSeparator(cell.line);
 		}
 	}
 
+	// Ensure each cell in selection is here only once
+	public function updateSelection() {
+		// Is s1 containing s2
+		function isContaining(s1 : Selection, s2: Selection) {
+			return s2.x1 >= s1.x1 && s2.x1 <= s1.x2 && s2.y1 >= s1.y1 && s2.y1 <= s1.y2 &&
+			s2.x2 >= s1.x1 && s2.x2 <= s1.x2 && s2.y2 >= s1.y1 && s2.y2 <= s1.y2;
+		}
+
+		var idx = selection.length;
+		while(idx-- > 0) {
+			var s = selection[idx];
+
+			for (idx2 in 0...selection.length) {
+				var s2 = selection[idx2];
+				if (s2 == s) continue;
+				if (isContaining(s, s2))
+					selection.remove(s2);
+			}
+
+		}
+	}
 }

+ 121 - 126
hide/comp/cdb/Editor.hx

@@ -155,8 +155,8 @@ class Editor extends Component {
 				sub.cell.elementHtml.click();
 				return;
 			}
-			if( cursor.select != null ) {
-				cursor.select = null;
+			if( cursor.selection != null ) {
+				cursor.selection = null;
 				cursor.update();
 			}
 		});
@@ -399,87 +399,6 @@ class Editor extends Component {
 			cursor.update();
 	}
 
-	function onCopy() {
-		var sel = cursor.getSelection();
-		if( sel == null )
-			return;
-		var data = [];
-		var isProps = (cursor.table.displayMode != Table);
-		var schema;
-		function saveValue(out, obj, c) {
-			var form = @:privateAccess formulas.getFormulaNameFromValue(obj, c);
-			if( form != null ) {
-				Reflect.setField(out, c.name+"__f", form);
-				return;
-			}
-
-			var v = Reflect.field(obj, c.name);
-			if( v != null )
-				Reflect.setField(out, c.name, v);
-		}
-		if( isProps ) {
-			schema = [];
-			var out = {};
-			for( y in sel.y1...sel.y2+1 ) {
-				var line = cursor.table.lines[y];
-				var obj = line.obj;
-				var c = line.columns[0];
-
-				saveValue(out, obj, c);
-				schema.push(c);
-			}
-			data.push(out);
-
-		} else {
-			for( y in sel.y1...sel.y2+1 ) {
-				var obj = cursor.table.lines[y].obj;
-				var out = {};
-				for( x in sel.x1...sel.x2+1 ) {
-					var c = cursor.table.columns[x];
-					saveValue(out, obj, c);
-
-				}
-				data.push(out);
-			}
-			schema = [for( x in sel.x1...sel.x2+1 ) cursor.table.columns[x]];
-		}
-
-		// In case we only have one value, just copy the cell value
-		if (data.length == 1 && Reflect.fields(data[0]).length == 1) {
-			var colName = Reflect.fields(data[0])[0];
-			var col = cursor.table.columns.find((c) -> c.name == colName);
-			if (col == null)
-				throw "unknown column";
-
-			// if we are a property or a list, fallback to the default case
-			if (col.type != TProperties && col.type != TList) {
-				var escape = switch(col.type) {
-					case TGradient, TCurve:
-						true;
-					default:
-						false;
-				};
-
-				var str = cursor.table.sheet.colToString(col, Reflect.field(data[0], colName), escape);
-
-				clipboard = {
-					data : data,
-					text : str,
-					schema : schema,
-				};
-
-				ide.setClipboard(str);
-				return;
-			}
-		}
-		// copy many values at once
-		clipboard = {
-			data : data,
-			text : Std.string([for( o in data ) cursor.table.sheet.objToString(o,true)]),
-			schema : schema,
-		};
-		ide.setClipboard(clipboard.text);
-	}
 
 	function stringToCol(str : String) : Null<Int> {
 		str = str.toUpperCase();
@@ -550,6 +469,89 @@ class Editor extends Component {
 			table.refresh();
 	}
 
+	function onCopy() {
+		if( cursor.selection == null )
+			return;
+		var data = [];
+		var isProps = (cursor.table.displayMode != Table);
+		var schema = [];
+		function saveValue(out, obj, c) {
+			var form = @:privateAccess formulas.getFormulaNameFromValue(obj, c);
+			if( form != null ) {
+				Reflect.setField(out, c.name+"__f", form);
+				return;
+			}
+
+			var v = Reflect.field(obj, c.name);
+			if( v != null )
+				Reflect.setField(out, c.name, v);
+		}
+		if( isProps ) {
+			var out = {};
+			for (sel in cursor.selection) {
+				for( y in sel.y1...sel.y2+1 ) {
+					var line = cursor.table.lines[y];
+					var obj = line.obj;
+					var c = line.columns[0];
+
+					saveValue(out, obj, c);
+					schema.push(c);
+				}
+				data.push(out);
+			}
+		} else {
+			for (sel in cursor.selection) {
+				for( y in sel.y1...sel.y2+1 ) {
+					var obj = cursor.table.lines[y].obj;
+					var out = {};
+					for( x in sel.x1...sel.x2+1 ) {
+						var c = cursor.table.columns[x];
+						saveValue(out, obj, c);
+
+					}
+					data.push(out);
+				}
+				schema = [for( x in sel.x1...sel.x2+1 ) cursor.table.columns[x]];
+			}
+		}
+
+		// In case we only have one value, just copy the cell value
+		if (data.length == 1 && Reflect.fields(data[0]).length == 1) {
+			var colName = Reflect.fields(data[0])[0];
+			var col = cursor.table.columns.find((c) -> c.name == colName);
+			if (col == null)
+				throw "unknown column";
+
+			// if we are a property or a list, fallback to the default case
+			if (col.type != TProperties && col.type != TList) {
+				var escape = switch(col.type) {
+					case TGradient, TCurve:
+						true;
+					default:
+						false;
+				};
+
+				var str = cursor.table.sheet.colToString(col, Reflect.field(data[0], colName), escape);
+
+				clipboard = {
+					data : data,
+					text : str,
+					schema : schema,
+				};
+
+				ide.setClipboard(str);
+				return;
+			}
+		}
+		// copy many values at once
+		clipboard = {
+			data : data,
+			text : Std.string([for( o in data ) cursor.table.sheet.objToString(o,true)]),
+			schema : schema,
+		};
+		ide.setClipboard(clipboard.text);
+	}
+
 	function onPaste() {
 		var text = ide.getClipboard();
 
@@ -565,21 +567,6 @@ class Editor extends Component {
 		var toRefresh : Array<Cell> = [];
 
 		var isProps = (cursor.table.displayMode != Table);
-		var x1 = cursor.x;
-		var y1 = cursor.y;
-		var x2 = cursor.select == null ? x1 : cursor.select.x;
-		var y2 = cursor.select == null ? y1 : cursor.select.y;
-		if( x1 > x2 ) {
-			var tmp = x1;
-			x1 = x2;
-			x2 = tmp;
-		}
-		if( y1 > y2 ) {
-			var tmp = y1;
-			y1 = y2;
-			y2 = tmp;
-		}
-
 		if( clipboard == null || text != clipboard.text ) {
 			if( cursor.x < 0 || cursor.y < 0 ) return;
 			function parseText(text, type : cdb.Data.ColumnType) : Dynamic {
@@ -629,7 +616,7 @@ class Editor extends Component {
 			if( isProps ) {
 				var line = cursor.getLine();
 				toRefresh.push(cursor.getCell());
-				var col = line.columns[x1];
+				var col = line.columns[cursor.x];
 				var p = Editor.getColumnProps(col);
 
 				if( !cursor.table.canEditColumn(col.name) || p.copyPasteImmutable)
@@ -644,21 +631,18 @@ class Editor extends Component {
 				Reflect.setField(obj, col.name, value);
 			} else {
 				beginChanges();
-				for( x in x1...x2+1 ) {
-					var col = columns[x];
-					var p = Editor.getColumnProps(col);
-					if( !cursor.table.canEditColumn(col.name) || p.copyPasteImmutable)
-						continue;
-					var lines = y1 == y2 ? [text] : text.split("\n");
-					for( y in y1...y2+1 ) {
-						var text = lines[y - y1];
-						if( text == null ) text = lines[lines.length - 1];
-						var value = parseText(text, col.type);
-						if( value == null ) continue;
-						var obj = sheet.lines[y];
+				var col = columns[cursor.x];
+				var p = Editor.getColumnProps(col);
+				if( cursor.table.canEditColumn(col.name) && !p.copyPasteImmutable) {
+					var lines = cursor.y == cursor.y ? [text] : text.split("\n");
+					var text = lines[0];
+					if( text == null ) text = lines[lines.length - 1];
+					var value = parseText(text, col.type);
+					if( value != null ) {
+						var obj = sheet.lines[cursor.y];
 						formulas.removeFromValue(obj, col);
 						Reflect.setField(obj, col.name, value);
-						toRefresh.push(allLines[y].cells[x]);
+						toRefresh.push(allLines[cursor.y].cells[cursor.x]);
 					}
 				}
 			}
@@ -718,8 +702,8 @@ class Editor extends Component {
 				Reflect.setField(destObj, destCol.name, v);
 		}
 
-		var posX = x1 < 0 ? 0 : x1;
-		var posY = y1 < 0 ? 0 : y1;
+		var posX = cursor.x < 0 ? 0 : cursor.x;
+		var posY = cursor.y < 0 ? 0 : cursor.y;
 		var data = clipboard.data;
 		if( data.length == 0 )
 			return;
@@ -754,8 +738,8 @@ class Editor extends Component {
 			}
 		} else {
 			beginChanges();
-			if( data.length == 1 && y1 != y2 )
-				data = [for( i in y1...y2+1 ) data[0]];
+			if( data.length == 1 && cursor.y != cursor.y )
+				data = [data[0]];
 			for( obj1 in data ) {
 				if( posY == sheet.lines.length ) {
 					if( !cursor.table.canInsert() ) break;
@@ -796,11 +780,14 @@ class Editor extends Component {
 	}
 
 	function onDelete() {
-		var sel = cursor.getSelection();
-		if( sel == null )
+		if( cursor.selection == null )
 			return;
 
-		delete(sel.x1, sel.x2, sel.y1, sel.y2);
+		beginChanges();
+		cursor.selection.sort((el1, el2) -> { return el1.y1 == el2.y1 ? 0 : el1.y1 < el2.y1 ? 1 : -1; });
+		for (s in cursor.selection)
+			delete(s.x1, s.x2, s.y1, s.y2);
+		endChanges();
 	}
 
 	function delete(x1 : Int, x2 : Int, y1 : Int, y2 : Int) {
@@ -2215,14 +2202,19 @@ class Editor extends Component {
 	function moveLines(lines : Array<Line>, delta : Int) {
 		if( lines.length == 0 || !lines[0].table.canInsert() || delta == 0 )
 			return;
-		var selDiff: Null<Int> = cursor.select == null ? null : cursor.select.y - cursor.y;
 		beginChanges();
+		var newSelection = [{
+			x1: -1,
+			y1: lines[0].index + delta,
+			x2: -1,
+			y2: lines[lines.length - 1].index + delta
+		}];
+
 		lines.sort((a, b) -> { return (a.index - b.index) * delta * -1; });
-		for( l in lines ) {
+		for( l in lines )
 			moveLine(l, delta);
-		}
-		if (selDiff != null && hxd.Math.iabs(selDiff) == lines.length - 1)
-			cursor.set(cursor.table, cursor.x, cursor.y, {x: cursor.x, y: cursor.y + selDiff});
+
+		cursor.set(cursor.table, cursor.x, cursor.y, newSelection);
 		endChanges();
 	}
 
@@ -2325,8 +2317,11 @@ class Editor extends Component {
 				focus();
 			}, keys : config.get("key.duplicate") },
 			{ label : "Delete", click : function() {
-				var sel = cursor.getSelection();
-				delete(sel.x1, sel.x2, sel.y1, sel.y2);
+				beginChanges();
+				cursor.selection.sort((el1, el2) -> { return el1.y1 == el2.y1 ? 0 : el1.y1 < el2.y1 ? 1 : -1; });
+				for (s in cursor.selection)
+					delete(s.x1, s.x2, s.y1, s.y2);
+				endChanges();
 			} },
 			{ label : "Separator", enabled : !sheet.props.hide, checked : sepIndex >= 0, click : function() {
 				beginChanges();

+ 15 - 5
hide/comp/cdb/Table.hx

@@ -198,7 +198,7 @@ class Table extends Component {
 					e.preventDefault();
 					return;
 				}
-				editor.cursor.clickLine(line, e.shiftKey);
+				editor.cursor.clickLine(line, e.shiftKey, e.ctrlKey);
 			});
 			#if js
 			var headEl = head.get(0);
@@ -228,9 +228,19 @@ class Table extends Component {
 				ide.unregisterUpdate(updateDrag);
 				previewDrop.hide();
 				if (e.dataTransfer.dropEffect == "none") return false;
-				var pickedLine = getPickedLine(e);
-				if (pickedLine != null) {
-					editor.moveLine(line, pickedLine.index - line.index, true);
+
+				var dropTarget = getPickedLine(e);
+				if (dropTarget == null)
+					return false;
+
+				var selection = editor.cursor.getSelectedAreaIncludingLine(line);
+				if (selection != null) {
+					editor.moveLines(editor.cursor.getLinesFromSelection(selection), selection.y1 > dropTarget.index ? dropTarget.index - selection.y1 : dropTarget.index - selection.y2);
+					return true;
+				}
+
+				if (dropTarget != null) {
+					editor.moveLine(line, dropTarget.index - line.index, true);
 					return true;
 				}
 
@@ -810,7 +820,7 @@ class Table extends Component {
 
 			th.contextmenu(function(e) {
 				editor.popupColumn(this, c, cell);
-				editor.cursor.clickCell(cell, false);
+				editor.cursor.clickCell(cell, false, false);
 				e.preventDefault();
 			});
 		}