diff --git a/.gitignore b/.gitignore index a48b0737..73d2a922 100644 --- a/.gitignore +++ b/.gitignore @@ -254,4 +254,8 @@ lib/_npm/ *.pyc .serve_files_secret \.DS_Store -playground/*.js* \ No newline at end of file +playground/*.js* + +# Exclude JetBrains IDE files +*.iml +.idea diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 28a804d8..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 2eaf920f..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/typescript-compiler.xml b/.idea/typescript-compiler.xml deleted file mode 100644 index 9b1ae4e6..00000000 --- a/.idea/typescript-compiler.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index a8418f64..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/webide.iml b/.idea/webide.iml deleted file mode 100644 index 24643cc3..00000000 --- a/.idea/webide.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index e32fb2b2..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,394 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - toggle - - - - - - - - - - - - - false - - false - false - - - true - DEFINITION_ORDER - - - - - - - - - - - - - - - Vue - - - - - VueDuplicateTag - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - project - - - true - - - - DIRECTORY - - false - - - - - - - - - 1492290653407 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/css/HexViewer.css b/css/HexViewer.css index 3a67e60e..06fddb6a 100644 --- a/css/HexViewer.css +++ b/css/HexViewer.css @@ -12,6 +12,7 @@ .hexViewer .header .hex .c8 { margin-left: 17px } .hexViewer .selected { background:#0076e0; color:white } +.hexViewer .cursor { background: #00ccff; } .hexViewer .asciicell { padding: 2px 0 } .hexViewer .hexRow { height:21px; } diff --git a/src/HexViewer.ts b/src/HexViewer.ts index 1160b01c..269ef897 100644 --- a/src/HexViewer.ts +++ b/src/HexViewer.ts @@ -100,11 +100,10 @@ export class HexViewer { private content: JQuery; private contentOuter: JQuery; - public mouseDownOffset: number; private canDeselect: boolean; - public selectionStart: number = -1; public selectionEnd: number = -1; + public selectionCursor: number = -1; public onSelectionChanged: (() => void); private isRecursive: boolean; @@ -125,15 +124,18 @@ export class HexViewer { if ("dataOffset" in cell) { if (e.type === "mousedown") { this.canDeselect = this.selectionStart === cell.dataOffset && this.selectionEnd === cell.dataOffset; - this.mouseDownOffset = cell.dataOffset; this.content.on("mousemove", evt => this.cellMouseAction(evt)); - this.setSelection(cell.dataOffset, cell.dataOffset); + if (e.shiftKey) { + this.shiftCursor(cell.dataOffset); + } else { + this.setCursor(cell.dataOffset); + } } else if (e.type === "mousemove") { - this.setSelection(this.mouseDownOffset, cell.dataOffset); + this.shiftCursor(cell.dataOffset); this.canDeselect = false; } - else if (e.type === "mouseup" && this.canDeselect && this.mouseDownOffset === cell.dataOffset) + else if (e.type === "mouseup" && this.canDeselect && this.selectionStart === cell.dataOffset) this.deselect(); this.contentOuter.focus(); @@ -177,12 +179,12 @@ export class HexViewer { e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : null; if (selDiff === null) return; - - var newSel = this.selectionStart + selDiff; - if (newSel < 0) newSel = 0; - else if (newSel >= this.dataProvider.length) newSel = this.dataProvider.length - 1; - - this.setSelection(newSel); + var newSel = this.selectionCursor + selDiff; + if (e.shiftKey) { + this.shiftCursor(newSel); + } else { + this.setCursor(newSel); + } return false; }); } @@ -256,8 +258,10 @@ export class HexViewer { hexCell.cell.dataOffset = asciiCell.dataOffset = dataOffset; var isSelected = this.selectionStart <= dataOffset && dataOffset <= this.selectionEnd; - $(hexCell.cell).toggleClass("selected", isSelected); - $(asciiCell).toggleClass("selected", isSelected); + var isCursor = dataOffset === this.selectionCursor; + var targets = [hexCell.cell, asciiCell]; + $(targets).toggleClass("selected", isSelected); + $(targets).toggleClass("cursor", isCursor); var skipInt = 0; for (var level = 0; level < this.maxLevel; level++) { @@ -265,8 +269,16 @@ export class HexViewer { var intIn = int && int.start <= dataOffset && dataOffset <= int.end; var intStart = intIn && int.start === dataOffset; var intEnd = intIn && int.end === dataOffset; - hexCell.levels[level].className = `l${this.maxLevel - 1 - level} ${((intBaseIdx + intIdx) % 2 === 0) ? "even" : "odd"}` + - (intIn ? ` m${level}` : "") + (intStart ? " start" : "") + (intEnd ? " end" : "") + (isSelected ? " selected" : ""); + var classes = [ + `l${this.maxLevel - 1 - level}`, + (intBaseIdx + intIdx) % 2 === 0 ? "even" : "odd", + ]; + if (intIn) classes.push(`m${level}`); + if (intStart) classes.push("start"); + if (intEnd) classes.push("end"); + if (isSelected) classes.push("selected"); + if (isCursor) classes.push("cursor"); + hexCell.levels[level].className = classes.join(" "); if (intEnd) skipInt++; @@ -291,16 +303,49 @@ export class HexViewer { } public deselect() { - this.setSelection(-1, -1); + this.setCursor(-1); } - public setSelection(start: number, end?: number) { + public setCursor (cursor: number) { + this.selectionCursor = cursor; + this.setSelection( + this.selectionCursor + ); + } + + public shiftCursor (cursor: number) { + var start = this.selectionStart; + var end = this.selectionEnd; + if (this.selectionCursor === end) { + end = cursor; + } else if (this.selectionCursor === start) { + start = cursor; + } + this.selectionCursor = cursor; + this.setSelection( + Math.max(0, start), + end + ); + } + + public setSelection(start: number, end?: number, cursor?: number) { if (this.isRecursive) return; end = end || start; var oldStart = this.selectionStart, oldEnd = this.selectionEnd; - this.selectionStart = start < end ? start : end; - this.selectionEnd = Math.min(start < end ? end : start, this.dataProvider.length - 1); + this.selectionStart = Math.min(start, end); + this.selectionEnd = Math.min( + Math.max(start, end), + this.dataProvider.length - 1 + ); + + // is cursor out of bounds because called externally? + if ( + this.selectionCursor < this.selectionStart + || this.selectionCursor > this.selectionEnd + ) cursor = this.selectionEnd; + if (cursor) this.selectionCursor = cursor; + if (this.selectionStart !== oldStart || this.selectionEnd !== oldEnd) { this.isRecursive = true; try { @@ -322,4 +367,4 @@ export class HexViewer { } } } -} \ No newline at end of file +} diff --git a/src/v1/app.ts b/src/v1/app.ts index 8fbdff78..661f1c24 100644 --- a/src/v1/app.ts +++ b/src/v1/app.ts @@ -207,7 +207,11 @@ class AppController { onHexViewerSelectionChanged() { //console.log("setSelection", ui.hexViewer.selectionStart, ui.hexViewer.selectionEnd); - localStorage.setItem("selection", JSON.stringify({ start: this.ui.hexViewer.selectionStart, end: this.ui.hexViewer.selectionEnd })); + localStorage.setItem("selection", JSON.stringify({ + start: this.ui.hexViewer.selectionStart, + end: this.ui.hexViewer.selectionEnd, + cursor: this.ui.hexViewer.selectionCursor + })); var start = this.ui.hexViewer.selectionStart; var hasSelection = start !== -1; @@ -215,7 +219,7 @@ class AppController { this.refreshSelectionInput(); if (this.ui.parsedDataTreeHandler && hasSelection && !this.selectedInTree) { - var intervals = this.ui.parsedDataTreeHandler.intervalHandler.searchRange(this.ui.hexViewer.mouseDownOffset || start); + var intervals = this.ui.parsedDataTreeHandler.intervalHandler.searchRange(this.ui.hexViewer.selectionCursor || start); if (intervals.items.length > 0) { //console.log("selected node", intervals[0].id); this.blockRecursive = true; @@ -281,7 +285,11 @@ $(() => { app.inputReady.then(() => { var storedSelection = JSON.parse(localStorage.getItem("selection")); if (storedSelection) - app.ui.hexViewer.setSelection(storedSelection.start, storedSelection.end); + app.ui.hexViewer.setSelection( + storedSelection.start, + storedSelection.end, + storedSelection.cursor + ); }); var editDelay = new Delayed(500);