]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Merge branch 'master' into 553_find_api
authorDaniel Imms <tyriar@tyriar.com>
Fri, 23 Jun 2017 02:37:18 +0000 (19:37 -0700)
committerGitHub <noreply@github.com>
Fri, 23 Jun 2017 02:37:18 +0000 (19:37 -0700)
1  2 
src/SelectionManager.ts
src/xterm.js

diff --combined src/SelectionManager.ts
index 0151427c0caf18fd31e39b4fac31a1c293c09390,b3316ae6cdad574ac79e867f3f64c491cf44de6a..a99cfa09234751e3f145e649c89519c442a7f7a1
@@@ -2,13 -2,13 +2,14 @@@
   * @license MIT
   */
  
+ import * as Mouse from './utils/Mouse';
+ import * as Browser from './utils/Browser';
  import { CharMeasure } from './utils/CharMeasure';
  import { CircularList } from './utils/CircularList';
  import { EventEmitter } from './EventEmitter';
- import * as Mouse from './utils/Mouse';
  import { ITerminal } from './Interfaces';
  import { SelectionModel } from './SelectionModel';
 +import { translateBufferLineToString } from './utils/BufferLine';
  
  /**
   * The number of pixels the mouse needs to be above or below the viewport in
@@@ -180,14 -180,16 +181,19 @@@ export class SelectionManager extends E
      this._buffer = buffer;
    }
  
 +  public get selectionStart(): [number, number] { return this._model.finalSelectionStart; }
 +  public get selectionEnd(): [number, number] { return this._model.finalSelectionEnd; }
 +
    /**
     * Gets whether there is an active text selection.
     */
    public get hasSelection(): boolean {
-     return !!this._model.finalSelectionStart && !!this._model.finalSelectionEnd;
+     const start = this._model.finalSelectionStart;
+     const end = this._model.finalSelectionEnd;
+     if (!start || !end) {
+       return false;
+     }
+     return start[0] !== end[0] || start[1] !== end[1];
    }
  
    /**
      // Get first row
      const startRowEndCol = start[1] === end[1] ? end[0] : null;
      let result: string[] = [];
 -    result.push(this._translateBufferLineToString(this._buffer.get(start[1]), true, start[0], startRowEndCol));
 +    result.push(translateBufferLineToString(this._buffer.get(start[1]), true, start[0], startRowEndCol));
  
      // Get middle rows
      for (let i = start[1] + 1; i <= end[1] - 1; i++) {
        const bufferLine = this._buffer.get(i);
 -      const lineText = this._translateBufferLineToString(bufferLine, true);
 +      const lineText = translateBufferLineToString(bufferLine, true);
        if (bufferLine.isWrapped) {
          result[result.length - 1] += lineText;
        } else {
      // Get final row
      if (start[1] !== end[1]) {
        const bufferLine = this._buffer.get(end[1]);
 -      const lineText = this._translateBufferLineToString(bufferLine, true, 0, end[0]);
 +      const lineText = translateBufferLineToString(bufferLine, true, 0, end[0]);
        if (bufferLine.isWrapped) {
          result[result.length - 1] += lineText;
        } else {
      this.refresh();
    }
  
 -  /**
 -   * Translates a buffer line to a string, with optional start and end columns.
 -   * Wide characters will count as two columns in the resulting string. This
 -   * function is useful for getting the actual text underneath the raw selection
 -   * position.
 -   * @param line The line being translated.
 -   * @param trimRight Whether to trim whitespace to the right.
 -   * @param startCol The column to start at.
 -   * @param endCol The column to end at.
 -   */
 -  private _translateBufferLineToString(line: any, trimRight: boolean, startCol: number = 0, endCol: number = null): string {
 -    // TODO: This function should live in a buffer or buffer line class
 -
 -    // Get full line
 -    let lineString = '';
 -    let widthAdjustedStartCol = startCol;
 -    let widthAdjustedEndCol = endCol;
 -    for (let i = 0; i < line.length; i++) {
 -      const char = line[i];
 -      lineString += char[LINE_DATA_CHAR_INDEX];
 -      // Adjust start and end cols for wide characters if they affect their
 -      // column indexes
 -      if (char[LINE_DATA_WIDTH_INDEX] === 0) {
 -        if (startCol >= i) {
 -          widthAdjustedStartCol--;
 -        }
 -        if (endCol >= i) {
 -          widthAdjustedEndCol--;
 -        }
 -      }
 -    }
 -
 -    // Calculate the final end col by trimming whitespace on the right of the
 -    // line if needed.
 -    let finalEndCol = widthAdjustedEndCol || line.length;
 -    if (trimRight) {
 -      const rightWhitespaceIndex = lineString.search(/\s+$/);
 -      if (rightWhitespaceIndex !== -1) {
 -        finalEndCol = Math.min(finalEndCol, rightWhitespaceIndex);
 -      }
 -      // Return the empty string if only trimmed whitespace is selected
 -      if (finalEndCol <= widthAdjustedStartCol) {
 -        return '';
 -      }
 -    }
 -
 -    return lineString.substring(widthAdjustedStartCol, finalEndCol);
 -  }
 -
    /**
     * Queues a refresh, redrawing the selection on the next opportunity.
+    * @param isNewSelection Whether the selection should be registered as a new
+    * selection on Linux.
     */
-   public refresh(): void {
+   public refresh(isNewSelection?: boolean): void {
+     // Queue the refresh for the renderer
      if (!this._refreshAnimationFrame) {
        this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh());
      }
+     // If the platform is Linux and the refresh call comes from a mouse event,
+     // we need to update the selection for middle click to paste selection.
+     if (Browser.isLinux && isNewSelection) {
+       const selectionText = this.selectionText;
+       if (selectionText.length) {
+         this.emit('newselection', this.selectionText);
+       }
+     }
    }
  
    /**
      }
  
      this._addMouseDownListeners();
-     this.refresh();
+     this.refresh(true);
    }
  
    /**
      if (!previousSelectionEnd ||
          previousSelectionEnd[0] !== this._model.selectionEnd[0] ||
          previousSelectionEnd[1] !== this._model.selectionEnd[1]) {
-       this.refresh();
+       this.refresh(true);
      }
    }
  
      return charIndex;
    }
  
 +  public setSelection(col: number, row: number, length: number): void {
 +    this._model.clearSelection();
 +    this._removeMouseDownListeners();
 +    this._model.selectionStart = [col, row];
 +    this._model.selectionStartLength = length;
 +    this.refresh();
 +  }
 +
    /**
     * Gets positional information for the word at the coordinated specified.
     * @param coords The coordinates to get the word at.
     */
    private _getWordAt(coords: [number, number]): IWordPosition {
      const bufferLine = this._buffer.get(coords[1]);
 -    const line = this._translateBufferLineToString(bufferLine, false);
 +    const line = translateBufferLineToString(bufferLine, false);
  
      // Get actual index, taking into consideration wide characters
      let endIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);
diff --combined src/xterm.js
index dc57cc211cd2b4fbc03d4abebecd50bc5b4a17da,a16a4ac778396f0828809ae3cd6980d8c4410562..374037886717ae952931ef8dfe0fd2132d6f4d32
@@@ -13,7 -13,7 +13,7 @@@
  import { CompositionHelper } from './CompositionHelper';
  import { EventEmitter } from './EventEmitter';
  import { Viewport } from './Viewport';
- import { rightClickHandler, pasteHandler, copyHandler } from './handlers/Clipboard';
+ import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard';
  import { CircularList } from './utils/CircularList';
  import { C0 } from './EscapeSequences';
  import { InputHandler } from './InputHandler';
@@@ -26,7 -26,6 +26,7 @@@ import * as Browser from './utils/Brows
  import * as Mouse from './utils/Mouse';
  import { CHARSETS } from './Charsets';
  import { getRawByteCoords } from './utils/Mouse';
 +import { translateBufferLineToString } from './utils/BufferLine';
  
  /**
   * Terminal Emulation References:
@@@ -168,7 -167,7 +168,7 @@@ function Terminal(options) 
    this.queue = '';
    this.scrollTop = 0;
    this.scrollBottom = this.rows - 1;
-   this.customKeydownHandler = null;
+   this.customKeyEventHandler = null;
    this.cursorBlinkInterval = null;
  
    // modes
@@@ -538,9 -537,11 +538,11 @@@ Terminal.prototype.initGlobal = functio
    on(this.textarea, 'paste', pasteHandlerWrapper);
    on(this.element, 'paste', pasteHandlerWrapper);
  
+   // Handle right click context menus
    if (term.browser.isFirefox) {
+     // Firefox doesn't appear to fire the contextmenu event on right click
      on(this.element, 'mousedown', event => {
-       if (ev.button == 2) {
+       if (event.button == 2) {
          rightClickHandler(event, this.textarea, this.selectionManager);
        }
      });
        rightClickHandler(event, this.textarea, this.selectionManager);
      });
    }
+   // Move the textarea under the cursor when middle clicking on Linux to ensure
+   // middle click to paste selection works. This only appears to work in Chrome
+   // at the time is writing.
+   if (term.browser.isLinux) {
+     // Use auxclick event over mousedown the latter doesn't seem to work. Note
+     // that the regular click event doesn't fire for the middle mouse button.
+     on(this.element, 'auxclick', event => {
+       if (event.button === 1) {
+         moveTextAreaUnderMouseCursor(event, this.textarea, this.selectionManager);
+       }
+     });
+   }
  };
  
  /**
@@@ -648,8 -662,7 +663,7 @@@ Terminal.prototype.open = function(pare
    this.viewportScrollArea.classList.add('xterm-scroll-area');
    this.viewportElement.appendChild(this.viewportScrollArea);
  
-   // Create the selection container. This needs to be added before the
-   // rowContainer as the selection must be below the text.
+   // Create the selection container.
    this.selectionContainer = document.createElement('div');
    this.selectionContainer.classList.add('xterm-selection');
    this.element.appendChild(this.selectionContainer);
    this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);
    this.renderer = new Renderer(this);
    this.selectionManager = new SelectionManager(this, this.lines, this.rowContainer, this.charMeasure);
-   this.selectionManager.on('refresh', data => this.renderer.refreshSelection(data.start, data.end));
+   this.selectionManager.on('refresh', data => {
+     this.renderer.refreshSelection(data.start, data.end);
+   });
+   this.selectionManager.on('newselection', text => {
+     // If there's a new selection, put it into the textarea, focus and select it
+     // in order to register it as a selection on the OS. This event is fired
+     // only on Linux to enable middle click to paste selection.
+     this.textarea.value = text;
+     this.textarea.focus();
+     this.textarea.select();
+   });
    this.on('scroll', () => this.selectionManager.refresh());
    this.viewportElement.addEventListener('scroll', () => this.selectionManager.refresh());
  
@@@ -1306,15 -1329,27 +1330,27 @@@ Terminal.prototype.writeln = function(d
  };
  
  /**
-  * Attaches a custom keydown handler which is run before keys are processed, giving consumers of
-  * xterm.js ultimate control as to what keys should be processed by the terminal and what keys
-  * should not.
+  * DEPRECATED: only for backward compatibility. Please use attachCustomKeyEventHandler() instead.
   * @param {function} customKeydownHandler The custom KeyboardEvent handler to attach. This is a
   *   function that takes a KeyboardEvent, allowing consumers to stop propogation and/or prevent
   *   the default action. The function returns whether the event should be processed by xterm.js.
   */
  Terminal.prototype.attachCustomKeydownHandler = function(customKeydownHandler) {
-   this.customKeydownHandler = customKeydownHandler;
+   let message = 'attachCustomKeydownHandler() is DEPRECATED and will be removed soon. Please use attachCustomKeyEventHandler() instead.';
+   console.warn(message);
+   this.attachCustomKeyEventHandler(customKeydownHandler);
+ }
+ /**
+  * Attaches a custom key event handler which is run before keys are processed, giving consumers of
+  * xterm.js ultimate control as to what keys should be processed by the terminal and what keys
+  * should not.
+  * @param {function} customKeypressHandler The custom KeyboardEvent handler to attach. This is a
+  *   function that takes a KeyboardEvent, allowing consumers to stop propogation and/or prevent
+  *   the default action. The function returns whether the event should be processed by xterm.js.
+  */
+ Terminal.prototype.attachCustomKeyEventHandler = function(customKeyEventHandler) {
+   this.customKeyEventHandler = customKeyEventHandler;
  }
  
  /**
@@@ -1413,7 -1448,7 +1449,7 @@@ Terminal.prototype.selectAll = function
   * @param {KeyboardEvent} ev The keydown event to be handled.
   */
  Terminal.prototype.keyDown = function(ev) {
-   if (this.customKeydownHandler && this.customKeydownHandler(ev) === false) {
+   if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) {
      return false;
    }
  
@@@ -1778,6 -1813,10 +1814,10 @@@ Terminal.prototype.setgCharset = functi
  Terminal.prototype.keyPress = function(ev) {
    var key;
  
+   if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) {
+     return false;
+   }
    this.cancel(ev);
  
    if (ev.charCode) {
    this.showCursor();
    this.handler(key);
  
-   return false;
+   return true;
  };
  
  /**
@@@ -2236,10 -2275,10 +2276,10 @@@ Terminal.prototype.reverseIndex = funct
  Terminal.prototype.reset = function() {
    this.options.rows = this.rows;
    this.options.cols = this.cols;
-   var customKeydownHandler = this.customKeydownHandler;
+   var customKeyEventHandler = this.customKeyEventHandler;
    var cursorBlinkInterval = this.cursorBlinkInterval;
    Terminal.call(this, this.options);
-   this.customKeydownHandler = customKeydownHandler;
+   this.customKeyEventHandler = customKeyEventHandler;
    this.cursorBlinkInterval = cursorBlinkInterval;
    this.refresh(0, this.rows - 1);
    this.viewport.syncScrollArea();
@@@ -2386,7 -2425,6 +2426,7 @@@ function keys(obj) 
   * Expose
   */
  
 +Terminal.translateBufferLineToString = translateBufferLineToString;
  Terminal.EventEmitter = EventEmitter;
  Terminal.inherits = inherits;