]> git.proxmox.com Git - mirror_xterm.js.git/blame - src/SelectionManager.ts
Handle copy
[mirror_xterm.js.git] / src / SelectionManager.ts
CommitLineData
70fda994
DI
1/**
2 * @license MIT
3 */
4
5import { CharMeasure } from './utils/CharMeasure';
6import { CircularList } from './utils/CircularList';
b594407c 7import { EventEmitter } from './EventEmitter';
70fda994 8import * as Mouse from './utils/Mouse';
ad3ae67e 9import { ITerminal } from './Interfaces';
70fda994 10
b594407c
DI
11export class SelectionManager extends EventEmitter {
12 // TODO: Create a SelectionModel
70fda994
DI
13 private _selectionStart: [number, number];
14 private _selectionEnd: [number, number];
15
70fda994
DI
16 private _mouseMoveListener: EventListener;
17
ad3ae67e
DI
18 constructor(
19 private _terminal: ITerminal,
20 private _buffer: CircularList<any>,
21 private _rowContainer: HTMLElement,
22 private _selectionContainer: HTMLElement,
23 private _charMeasure: CharMeasure
24 ) {
b594407c 25 super();
70fda994
DI
26 this._attachListeners();
27 }
28
29 private _attachListeners() {
30 this._mouseMoveListener = event => this._onMouseMove(<MouseEvent>event);
31
32 this._buffer.on('trim', amount => this._onTrim(amount));
33 this._rowContainer.addEventListener('mousedown', event => this._onMouseDown(event));
34 this._rowContainer.addEventListener('mouseup', event => this._onMouseUp(event));
35 }
36
37 public get selectionText(): string {
38 if (!this._selectionStart || !this._selectionEnd) {
39 return null;
40 }
32b34cbe
DI
41 // TODO: Handle first row start position properly
42 const startRowEndCol = this._selectionStart[1] === this._selectionEnd[1] ? this._selectionEnd[0] : null;
43 let result: string[] = [];
44 result.push(this._translateBufferLineToString(this._buffer.get(this._selectionStart[1]), this._selectionStart[0], startRowEndCol));
45 for (let i = this._selectionStart[1] + 1; i <= this._selectionEnd[1] - 1; i++) {
46 result.push(this._translateBufferLineToString(this._buffer.get(i)));
47 }
48 if (this._selectionStart[1] !== this._selectionEnd[1]) {
49 result.push(this._translateBufferLineToString(this._buffer.get(this._selectionEnd[1]), 0, this._selectionEnd[1]));
50 }
51 console.log('result: ' + result);
52 return result.join('\n');
53 }
54
55 private _translateBufferLineToString(line: any, startCol: number = 0, endCol: number = null): string {
56 // TODO: This function should live in a buffer or buffer line class
57 endCol = endCol || line.length
58 let result = '';
59 for (let i = startCol; i < endCol; i++) {
60 result += line[i][1];
61 }
62 // TODO: Trim line?
63 return result;
64 // TODO: Handle the double-width character case
70fda994
DI
65 }
66
207c4cf9
DI
67 /**
68 * Redraws the selection.
69 */
70 public refresh(): void {
b594407c
DI
71 // TODO: Figure out when to refresh the selection vs when to refresh the viewport
72 this.emit('refresh', { start: this._selectionStart, end: this._selectionEnd });
207c4cf9
DI
73 }
74
75 /**
76 * Handle the buffer being trimmed, adjust the selection position.
77 * @param amount The amount the buffer is being trimmed.
78 */
70fda994 79 private _onTrim(amount: number) {
b36d8780
DI
80 // TODO: Somehow map the selection coordinates with the list that is constantly being trimmed
81 // Maybe we need an ID in the CircularList that starts from 0 for the first entry and increments
32b34cbe 82 // console.log('trimmed: ' + amount);
207c4cf9
DI
83
84 // Adjust the selection position based on the trimmed amount.
85 this._selectionStart[0] -= amount;
86 this._selectionEnd[0] -= amount;
87
88 // The selection has moved off the buffer, clear it.
89 if (this._selectionEnd[0] < 0) {
90 this._selectionStart = null;
91 this._selectionEnd = null;
92 this.refresh();
93 return;
94 }
95
96 // If the selection start is trimmed, ensure the start column is 0.
97 if (this._selectionStart[0] < 0) {
98 this._selectionStart[1] = 0;
99 }
70fda994
DI
100 }
101
32b34cbe
DI
102 // TODO: Handle splice/shiftElements in the buffer (just clear the selection?)
103
b36d8780 104 private _getMouseBufferCoords(event: MouseEvent) {
ad3ae67e
DI
105 const coords = Mouse.getCoords(event, this._rowContainer, this._charMeasure);
106 // Convert to 0-based
107 coords[0]--;
108 coords[1]--;
109 // Convert viewport coords to buffer coords
110 coords[1] += this._terminal.ydisp;
111 return coords;
b36d8780
DI
112 }
113
70fda994 114 private _onMouseDown(event: MouseEvent) {
b36d8780 115 this._selectionStart = this._getMouseBufferCoords(event);
70fda994 116 if (this._selectionStart) {
ad3ae67e 117 this._selectionEnd = null;
70fda994 118 this._rowContainer.addEventListener('mousemove', this._mouseMoveListener);
ad3ae67e 119 this.refresh();
70fda994
DI
120 }
121 }
122
123 private _onMouseMove(event: MouseEvent) {
b36d8780 124 this._selectionEnd = this._getMouseBufferCoords(event);
207c4cf9
DI
125 // TODO: Only draw here if the selection changes
126 this.refresh();
70fda994
DI
127 }
128
129 private _onMouseUp(event: MouseEvent) {
32b34cbe
DI
130 // console.log('mouseup');
131 // console.log('start', this._selectionStart);
132 // console.log('end', this._selectionEnd);
70fda994
DI
133 if (!this._selectionStart) {
134 return;
135 }
136 this._rowContainer.removeEventListener('mousemove', this._mouseMoveListener);
137 }
138}