source: trunk/src/sce/component/SCEPaneUI.java @ 1475

Last change on this file since 1475 was 1475, checked in by stefan, 3 years ago

made code Java8 compatible

File size: 29.9 KB
Line 
1package sce.component;
2
3import jlatexeditor.Doc;
4import jlatexeditor.gproperties.GProperties;
5import sce.codehelper.CodeCompletion;
6import sce.codehelper.CodeHelperPane;
7import sce.codehelper.LineBreakListener;
8import sce.quickhelp.QuickHelp;
9import sce.quickhelp.QuickHelpPane;
10import sce.syntaxhighlighting.BracketHighlighting;
11import sun.swing.DefaultLookup;
12
13import javax.swing.*;
14import javax.swing.plaf.ComponentUI;
15import java.awt.*;
16import java.awt.datatransfer.Clipboard;
17import java.awt.datatransfer.DataFlavor;
18import java.awt.datatransfer.Transferable;
19import java.awt.event.*;
20import java.awt.image.BufferedImage;
21import java.util.ArrayList;
22
23/**
24 * SourceCodeEditor UI.
25 *
26 * @author Jörg Endrullis
27 * @author Stefan Endrullis
28 */
29public class SCEPaneUI extends ComponentUI implements KeyListener, MouseListener, MouseMotionListener {
30  // properties
31  private SCEPane pane = null;
32  private SCEDocument document = null;
33  private SCECaret caret = null;
34  private Clipboard selectionClipboard = Toolkit.getDefaultToolkit().getSystemSelection();
35
36  // quick help
37  QuickHelpPane quickHelpPane = null;
38
39  // auto completion
40  CodeHelperPane codeHelperPane = null;
41
42  // line break listener
43  LineBreakListener lineBreakListener = null;
44
45  // last mouse click
46  long lastMouseClick = 0;
47
48  // transparent image for hiding mouse cursor
49  private Cursor defaultCursor;
50  private Cursor invisibleCursor;
51  private boolean mouseIsVisible = true;
52
53  // text highlight
54  private SCETextHighlight overHighlight = null;
55
56  public SCEPaneUI(SCEPane pane) {
57    this.pane = pane;
58    this.document = pane.getDocument();
59    this.caret = pane.getCaret();
60
61    pane.setLayout(null);
62
63    // KeyListener
64    pane.addKeyListener(this);
65    pane.setFocusable(true);
66    pane.setFocusCycleRoot(false);
67    pane.setFocusTraversalKeysEnabled(false);
68
69    // MouseListener
70    pane.addMouseListener(this);
71    pane.addMouseMotionListener(this);
72
73    // hiding mouse cursor
74    defaultCursor = pane.getCursor(); {
75      Dimension dim = Toolkit.getDefaultToolkit().getBestCursorSize(0,0);
76      BufferedImage image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
77      invisibleCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "InvisibleCursor");
78    }
79
80    installUI(pane);
81  }
82
83  /**
84   * Removes the selected text from the document.
85   */
86  public void removeSelection() {
87    if (document.removeSelection()) {
88      caret.setSelectionMark();
89    }
90  }
91
92  /**
93   * Sets the code helper for the source code editor.
94   *
95   * @param codeCompletion code helper
96   */
97  public void setCodeHelper(CodeCompletion codeCompletion) {
98    // remove old code helper
99    if (codeHelperPane != null) codeHelperPane.destroy();
100
101    // add new code helper
102    if (codeCompletion != null) {
103      // UI key listener shall be at the end of the processor queue
104      pane.removeKeyListener(this);
105
106      // create auto completion list
107      codeHelperPane = new CodeHelperPane(pane);
108      codeHelperPane.setVisible(false);
109      codeHelperPane.setCodeCompletion(codeCompletion);
110
111      // UI key listener shall be at the end of the processor queue
112      pane.addKeyListener(this);
113    }
114  }
115
116  /**
117   * Sets the tab completion for the source code editor.
118   *
119   * @param tabCompletion tab completion
120   */
121  public void setTabCompletion(CodeCompletion tabCompletion) {
122    if (codeHelperPane == null) return;
123    codeHelperPane.setTabCompletion(tabCompletion);
124  }
125
126  /**
127   * Sets the code helper for the source code editor.
128   *
129   * @param quickHelp quick help
130   */
131  public void setQuickHelp(QuickHelp quickHelp) {
132    // remove old quick help
133    if (quickHelpPane != null) quickHelpPane.destroy();
134
135    // add new quick help
136    if (quickHelp != null) {
137      quickHelpPane = new QuickHelpPane(pane);
138      quickHelpPane.setVisible(false);
139      quickHelpPane.setQuickHelp(quickHelp);
140    }
141  }
142
143  /**
144   * Sets the listener for line breaks.
145   *
146   * @param lineBreakListener listener for line breaks
147   */
148  public void setLineBreakListener(LineBreakListener lineBreakListener) {
149    this.lineBreakListener = lineBreakListener;
150  }
151
152  /**
153   * Scrolls the visible rectangle.
154   *
155   * @param rows the number of rows to scroll
156   * @return visible rectangle
157   */
158  public Rectangle scrollVisibleRect(int rows) {
159    Rectangle visibleRect = pane.getVisibleRect();
160    visibleRect.translate(0, rows * pane.getLineHeight());
161    pane.scrollRectToVisible(visibleRect);
162
163    return visibleRect;
164  }
165
166  // KeyListener Methods
167
168  public void keyTyped(KeyEvent e) {
169    if (e.isConsumed()) return;
170
171    //if (allowedChars.indexOf(e.getKeyChar()) == -1) return;
172    if (e.isControlDown() || e.isAltDown() || e.isMetaDown()) return;
173    if (Character.isISOControl(e.getKeyChar())) return;
174
175    if (document.hasSelection()) removeSelection();
176    document.insert(e.getKeyChar() + "", caret.getRow(), caret.getColumn());
177  }
178
179  public synchronized void keyPressed(KeyEvent e) {
180    if (e.isConsumed()) return;
181
182    if(mouseIsVisible && GProperties.getBoolean("editor.hide_mouse_during_typing")) {
183      pane.setCursor(invisibleCursor);
184      mouseIsVisible = false;
185    }
186
187    int keyCode = e.getKeyCode();
188
189    // ignore meta key
190    if (e.isMetaDown()) return;
191
192    // is control down?
193    if (e.isControlDown() && !e.isAltDown()) {
194      if (!isModifierKey(keyCode)) return;
195    }
196
197    // is alt down?
198    if (e.isAltDown()) {
199      keyAltDown(e);
200      return;
201    }
202
203    // delete and back space
204    if (keyCode == KeyEvent.VK_BACK_SPACE) {
205      if (document.hasSelection()) {
206        removeSelection();
207      } else {
208        if (caret.getRow() == 0 && caret.getColumn() == 0) return;
209        caret.move(SCECaret.DIRECTION_LEFT, false);
210        keyCode = KeyEvent.VK_DELETE;
211      }
212      e.consume();
213    }
214    if (keyCode == KeyEvent.VK_DELETE) {
215      // selection to remove?
216      if (document.hasSelection()) {
217        removeSelection();
218        e.consume();
219        return;
220      }
221
222      // is the caret at the last column of the line?
223      if (caret.getColumn() == document.getRowsModel().getRowLength(caret.getRow())) {
224        document.remove(caret.getRow(), caret.getColumn(), caret.getRow() + 1, 0);
225      } else {
226        document.remove(caret.getRow(), caret.getColumn(), caret.getRow(), caret.getColumn() + 1);
227      }
228      e.consume();
229      return;
230    }
231
232    // navigation keys
233    if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN ||
234        keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT ||
235        keyCode == KeyEvent.VK_PAGE_UP || keyCode == KeyEvent.VK_PAGE_DOWN ||
236        keyCode == KeyEvent.VK_HOME || keyCode == KeyEvent.VK_END) {
237
238      // remove selection if shift not pressed
239      boolean keepSelection = e.isShiftDown();
240
241      // caret movement
242      if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) {
243        int direction = keyCode == KeyEvent.VK_UP ? SCECaret.DIRECTION_UP : SCECaret.DIRECTION_DOWN;
244        caret.move(direction, keepSelection);
245        e.consume();
246      }
247      if (keyCode == KeyEvent.VK_LEFT) {
248        caret.move(SCECaret.DIRECTION_LEFT, keepSelection);
249        e.consume();
250      }
251      if (keyCode == KeyEvent.VK_RIGHT) {
252        caret.move(SCECaret.DIRECTION_RIGHT, keepSelection);
253        e.consume();
254      }
255      if (keyCode == KeyEvent.VK_PAGE_UP || keyCode == KeyEvent.VK_PAGE_DOWN) {
256        int jump = keyCode == KeyEvent.VK_PAGE_UP ? -pane.getVisibleRowsCount() : pane.getVisibleRowsCount();
257        scrollVisibleRect(jump);
258        caret.moveTo(caret.getRow() + jump, caret.getColumn(), keepSelection);
259        e.consume();
260      }
261      if (keyCode == KeyEvent.VK_HOME) {
262        if (caret.getColumn() != 0) {
263          caret.moveTo(caret.getRow(), 0, keepSelection);
264        } else {
265          // move caret to the first non-space character
266          char[] chars = document.getRowsModel().getRow(caret.getRow()).toCharArray();
267          for (int i = 0; i < chars.length; i++) {
268            if (chars[i] != ' ' && chars[i] != '\t') {
269              caret.moveTo(caret.getRow(), i, keepSelection);
270              break;
271            }
272          }
273        }
274        e.consume();
275      }
276      if (keyCode == KeyEvent.VK_END) {
277        int rowLength = document.getRowsModel().getRowLength(caret.getRow());
278        if (caret.getColumn() != rowLength) {
279          caret.moveTo(caret.getRow(), rowLength, keepSelection);
280        } else {
281          // move caret to first non-space character
282          char[] chars = document.getRowsModel().getRow(caret.getRow()).toCharArray();
283          for (int i = rowLength; i > 0; i--) {
284            if (chars[i - 1] != ' ' && chars[i - 1] != '\t') {
285              caret.moveTo(caret.getRow(), i, keepSelection);
286              break;
287            }
288          }
289        }
290        e.consume();
291      }
292    }
293
294    // enter
295    if (keyCode == KeyEvent.VK_ENTER) {
296      if (document.hasSelection()) removeSelection();
297      int row = caret.getRow();
298      int column = caret.getColumn();
299
300      String line = document.getRowsModel().getRowAsString(row, 0, column);
301      document.insert("\n", row, column);
302      // take over indentation of last line
303      int indentCount = 0;
304      while (indentCount < line.length() && line.charAt(indentCount) == ' ') indentCount++;
305      document.insert(line.substring(0, indentCount), row + 1, 0);
306
307      if (lineBreakListener != null) {
308        lineBreakListener.linedWrapped(pane);
309      }
310      e.consume();
311    }
312
313    // tab
314    if (keyCode == KeyEvent.VK_TAB) {
315      if (!document.hasSelection()) {
316        if (!e.isShiftDown()) {
317          document.insert("  ", caret.getRow(), caret.getColumn());
318        } else {
319          int row = caret.getRow();
320          int col = caret.getColumn();
321          removeIndentation(caret.getRow());
322          caret.moveTo(row, Math.max(col - 2, 0), false);
323          caret.removeSelectionMark();
324          pane.repaint();
325        }
326        e.consume();
327        return;
328      } else {
329        SCEDocumentPosition startSel = document.getSelectionStart();
330        SCEDocumentPosition endSel = document.getSelectionEnd();
331
332        int startRow = startSel.getRow();
333        int endRow = endSel.getRow() - (endSel.getColumn() == 0 ? 1 : 0);
334        for (int row = startRow; row <= endRow; row++) {
335          if (e.isShiftDown()) {
336            removeIndentation(row);
337          } else {
338            document.insert("  ", row, 0);
339          }
340        }
341
342        int endCol = endSel.getColumn();
343        if (!e.isShiftDown()) {
344          endCol = endCol == 0 ? endCol : endCol + 2;
345        } else {
346          endCol = Math.max(endCol, 0);
347        }
348        endSel = new SCEDocumentPosition(endSel.getRow(), endCol);
349        caret.moveTo(endSel.getRow(), endCol, false);
350        document.setSelectionRange(startSel, endSel, true);
351      }
352      e.consume();
353    }
354  }
355
356  private void keyAltDown(KeyEvent e) {
357    // alt+enter -> code assistant
358    if (e.getKeyCode() == KeyEvent.VK_ENTER) {
359      pane.callCodeAssistants();
360      e.consume();
361    }
362
363    // alt+backspace -> undo the last edit
364    if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
365      pane.getUndoManager().undo(false);
366      e.consume();
367    }
368  }
369
370  private void removeIndentation(int row) {
371    String rowString = document.getRowsModel().getRowAsString(row);
372    if (rowString.startsWith("  ")) {
373      document.remove(row, 0, row, 2);
374    } else if (rowString.startsWith(" ")) {
375      document.remove(row, 0, row, 1);
376    }
377  }
378
379  private boolean isModifierKey(int keyCode) {
380    return KeyEvent.VK_SHIFT <= keyCode && keyCode <= KeyEvent.VK_ALT;
381  }
382
383  public void keyReleased(KeyEvent e) {
384  }
385
386// UI Installation/De-installation
387
388  public void installUI(JComponent c) {
389    this.pane = (SCEPane) c;
390
391    SourceCodeEditorUI.installInputMap(pane.getSourceCodeEditor());
392    installKeyboardActions();
393  }
394
395  public void uninstallUI(JComponent c) {
396    uninstallKeyboardActions();
397    SourceCodeEditorUI.uninstallInputMap(pane.getSourceCodeEditor());
398
399    this.pane = null;
400  }
401
402  protected void installKeyboardActions() {
403    //InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
404    InputMap km = new ComponentInputMap(pane);
405    //km.put(KeyStroke.getKeyStroke("control shift L"), Actions.JUMP_LEFT);
406
407    SwingUtilities.replaceUIInputMap(pane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km);
408    km = getInputMap(JComponent.WHEN_FOCUSED);
409    SwingUtilities.replaceUIInputMap(pane, JComponent.WHEN_FOCUSED, km);
410
411    pane.getSourceCodeEditor().addKeyListener(new KeyAdapter() {
412      @Override
413      public void keyPressed(KeyEvent e) {
414        System.out.println(e.getKeyCode());
415      }
416    });
417   
418    pane.setActionMap(Actions.getActionMap(pane));
419
420    //LazyActionMap.installLazyActionMap(pane, BasicTabbedPaneUI.class, "SCEPane.actionMap");
421    //updateMnemonics();
422  }
423
424  protected InputMap getInputMap(int condition) {
425    if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
426      return (InputMap) DefaultLookup.get(pane, this, "SCEPane.ancestorInputMap");
427    }
428    else if (condition == JComponent.WHEN_FOCUSED) {
429      return (InputMap) DefaultLookup.get(pane, this, "SCEPane.focusInputMap");
430    }
431    return null;
432  }
433
434  protected void uninstallKeyboardActions() {
435    SwingUtilities.replaceUIActionMap(pane, null);
436    SwingUtilities.replaceUIInputMap(pane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
437    SwingUtilities.replaceUIInputMap(pane, JComponent.WHEN_FOCUSED, null);
438    SwingUtilities.replaceUIInputMap(pane, JComponent.WHEN_IN_FOCUSED_WINDOW, null);
439  }
440
441  public QuickHelpPane getQuickHelpPane() {
442    return quickHelpPane;
443  }
444
445  public static class Actions extends AbstractAction {
446    public static final String MOVE_VIEW_UP             = "move view up";
447    public static final String MOVE_VIEW_UP_SHIFT       = "move view up shift";
448    public static final String MOVE_VIEW_DOWN           = "move view down";
449    public static final String MOVE_VIEW_DOWN_SHIFT     = "move view down shift";
450    public static final String JUMP_RIGHT               = "jump right";
451    public static final String JUMP_RIGHT_SHIFT         = "jump right shift";
452    public static final String JUMP_LEFT                = "jump left";
453    public static final String JUMP_LEFT_SHIFT          = "jump left shift";
454    public static final String JUMP_TO_FRONT            = "jump to front";
455    public static final String JUMP_TO_FRONT_SHIFT      = "jump to front shift";
456    public static final String JUMP_TO_END              = "jump to end";
457    public static final String JUMP_TO_END_SHIFT        = "jump to end shift";
458    public static final String REMOVE_LINE              = "remove line";
459    public static final String REMOVE_LINE_BEFORE_CARET = "remove line before caret";
460    public static final String REMOVE_LINE_BEHIND_CARET = "remove line behind caret";
461    public static final String REMOVE_WORD_BEFORE_CARET = "remove word before caret";
462    public static final String REMOVE_WORD_BEHIND_CARET = "remove word behind caret";
463    public static final String COMPLETE                 = "complete";
464
465    public static final String UNDO                     = "undo";
466    public static final String REDO                     = "redo";
467    public static final String FIND                     = "find";
468    public static final String REPLACE                  = "replace";
469    public static final String FIND_NEXT                = "find next";
470    public static final String FIND_PREVIOUS            = "find previous";
471    public static final String CUT                      = "cut";
472    public static final String COPY                     = "copy";
473    public static final String PASTE                    = "paste";
474    public static final String SELECT_ALL               = "select all";
475    public static final String SELECT_NONE              = "select none";
476    public static final String COMMENT                  = "comment";
477    public static final String UNCOMMENT                = "uncomment";
478
479    private String key;
480    private SCEPane pane;
481
482    Actions(String key, SCEPane pane) {
483      this.key = key;
484      this.pane = pane;
485    }
486
487    public void actionPerformed(ActionEvent e) {
488      SCEPaneUI ui = pane.getPaneUI();
489
490      if (ui == null) return;
491
492      if (key.startsWith(MOVE_VIEW_UP)) {
493        ui.moveViewVertically(-1, key.endsWith(" shift"));
494      } else
495      if (key.startsWith(MOVE_VIEW_DOWN)) {
496        ui.moveViewVertically(1, key.endsWith(" shift"));
497      } else
498      if (key.startsWith(JUMP_LEFT)) {
499        ui.jumpSideways(-1, key.endsWith(" shift"));
500      } else
501      if (key.startsWith(JUMP_RIGHT)) {
502        ui.jumpSideways(1, key.endsWith(" shift"));
503      } else
504      if (key.startsWith(JUMP_TO_FRONT)) {
505        ui.jumpToFront(key.endsWith(" shift"));
506      } else
507      if (key.startsWith(JUMP_TO_END)) {
508        ui.jumpToEnd(key.endsWith(" shift"));
509      } else
510      if (key.equals(REMOVE_LINE)) {
511        ui.removeLine();
512      } else
513      if (key.equals(REMOVE_LINE_BEFORE_CARET)) {
514        ui.removeLineBeforeCaret();
515      } else
516      if (key.equals(REMOVE_LINE_BEHIND_CARET)) {
517        ui.removeLineBehindCaret();
518      } else
519      if (key.equals(REMOVE_WORD_BEFORE_CARET)) {
520        ui.removeWordBeforeCaret();
521      } else
522      if (key.equals(REMOVE_WORD_BEHIND_CARET)) {
523        ui.removeWordBehindCaret();
524      } else
525      if (key.equals(COMPLETE)) {
526        ui.complete();
527      } else
528      if (key.equals(UNDO)) {
529        pane.getUndoManager().undo(false);
530      } else
531      if (key.equals(REDO)) {
532        pane.getUndoManager().redo(false);
533      } else
534      if (key.equals(FIND)) {
535        pane.getSourceCodeEditor().toggleSearch(false);
536      } else
537      if (key.equals(REPLACE)) {
538        pane.getSourceCodeEditor().replace();
539      } else
540      if (key.equals(FIND_NEXT)) {
541        ensureOpenSearch(pane.getSourceCodeEditor());
542        pane.getSourceCodeEditor().getSearch().next(false, true);
543      } else
544      if (key.equals(FIND_PREVIOUS)) {
545        ensureOpenSearch(pane.getSourceCodeEditor());
546        pane.getSourceCodeEditor().getSearch().previous();
547      } else
548      if (key.equals(CUT)) {
549        pane.getSourceCodeEditor().cut();
550      } else
551      if (key.equals(COPY)) {
552        pane.getSourceCodeEditor().copy();
553      } else
554      if (key.equals(PASTE)) {
555        pane.getSourceCodeEditor().paste();
556      } else
557      if (key.equals(SELECT_ALL)) {
558        SCEDocument document = pane.getDocument();
559        document.setSelectionRange(document.createDocumentPosition(0,0), document.createDocumentPosition(document.getRowsModel().getRowsCount()-1, 0), true);
560        pane.repaint();
561      } else
562      if (key.equals(SELECT_NONE)) {
563        SCEDocument document = pane.getDocument();
564        document.setSelectionRange(null, null, true);
565        pane.getCaret().setSelectionMark();
566        pane.repaint();
567      } else
568      if (key.equals(COMMENT)) {
569        SourceCodeEditor editor = pane.getSourceCodeEditor();
570        String lineComment = "//";
571        try {
572          lineComment = ((SourceCodeEditor<? extends Doc>) editor).getResource().getProperty("lineComment");
573        } catch (Throwable ignored) { /* ignore */ }
574        pane.getSourceCodeEditor().lineComment(lineComment);
575      } else
576      if (key.equals(UNCOMMENT)) {
577        SourceCodeEditor editor = pane.getSourceCodeEditor();
578        String[] lineComment = new String[] {"//"};
579        try {
580          String lc = ((SourceCodeEditor<? extends Doc>) editor).getResource().getProperty("lineComment");
581          if(!lc.endsWith(" ")) {
582            lineComment = new String[] {lc};
583          } else {
584            lineComment = new String[] {lc, lc.trim()};
585          }
586        } catch (Throwable ignored) { /* ignore */ }
587        editor.lineUncomment(lineComment);
588      }
589    }
590
591    private void ensureOpenSearch(SourceCodeEditor editor) {
592      if (!editor.getSearch().isVisible()) {
593        editor.search(SCESearch.getLastSearch());
594      }
595    }
596
597    public static ActionMap getActionMap(SCEPane pane) {
598      ActionMap am = new ActionMap();
599      add(am, pane, MOVE_VIEW_UP, MOVE_VIEW_UP_SHIFT, MOVE_VIEW_DOWN, MOVE_VIEW_DOWN_SHIFT,
600        JUMP_LEFT, JUMP_LEFT_SHIFT, JUMP_RIGHT, JUMP_RIGHT_SHIFT, JUMP_TO_FRONT, JUMP_TO_FRONT_SHIFT, JUMP_TO_END, JUMP_TO_END_SHIFT,
601        REMOVE_LINE, REMOVE_LINE_BEFORE_CARET, REMOVE_LINE_BEHIND_CARET, REMOVE_WORD_BEFORE_CARET, REMOVE_WORD_BEHIND_CARET,
602        COMPLETE,
603        UNDO, REDO, FIND, REPLACE, FIND_NEXT, FIND_PREVIOUS, CUT, COPY, PASTE, SELECT_ALL, SELECT_NONE, COMMENT, UNCOMMENT);
604      return am;
605    }
606
607    public static void add(ActionMap am, SCEPane pane, String... commands) {
608      for (String command : commands) {
609        am.put(command, new Actions(command, pane));
610      }
611    }
612  }
613
614  public void moveViewVertically(int direction, boolean isShiftDown) {
615    Rectangle visibleRect = scrollVisibleRect(direction);
616
617    int minRow = visibleRect.y / pane.getLineHeight();
618    int maxRow = (visibleRect.y + visibleRect.height) / pane.getLineHeight();
619
620    if (caret.getRow() < minRow + 2) caret.move(SCECaret.DIRECTION_DOWN, isShiftDown);
621    if (caret.getRow() > maxRow - 2) caret.move(SCECaret.DIRECTION_UP, isShiftDown);
622  }
623
624  /**
625   * Jumps one word to the left or right.
626   * Use direction -1 for left and 1 for right.
627   *
628   * @param direction -1 for left and 1 for right
629   * @param isShiftDown is shift pressed
630   */
631  public void jumpSideways(int direction, boolean isShiftDown) {
632    caret.moveTo(pane.findSplitterPosition(caret.getRow(), caret.getColumn(), direction), isShiftDown);
633  }
634
635  public void jumpToFront(boolean isShiftDown) {
636    caret.moveTo(0, 0, isShiftDown);
637  }
638
639  public void jumpToEnd(boolean isShiftDown) {
640    SCEDocumentRows rowsModel = document.getRowsModel();
641
642    int row = rowsModel.getRowsCount() - 1;
643    int column = rowsModel.getRowLength(row);
644    caret.moveTo(row, column, isShiftDown);
645  }
646
647  public void removeLine() {
648    SCEDocumentRows rowsModel = document.getRowsModel();
649
650    if (caret.getRow() < rowsModel.getRowsCount() - 1) {
651      document.remove(caret.getRow(), 0, caret.getRow() + 1, 0);
652    } else if (caret.getRow() > 0) {
653      document.remove(caret.getRow() - 1, rowsModel.getRowLength(caret.getRow() - 1), caret.getRow(), rowsModel.getRowLength(caret.getRow()));
654      caret.moveTo(caret.getRow() - 1, caret.getColumn(), false);
655    } else {
656      document.remove(0, 0, 0, rowsModel.getRowLength(0));
657      caret.moveTo(0, 0, false);
658    }
659  }
660
661  public void removeLineBeforeCaret() {
662    document.remove(caret.getRow(), 0, caret.getRow(), caret.getColumn());
663  }
664
665  public void removeLineBehindCaret() {
666    document.remove(caret.getRow(), caret.getColumn(), caret.getRow(), document.getRowsModel().getRowLength(caret.getRow()));
667  }
668
669  public void removeWordBeforeCaret() {
670    document.remove(pane.findSplitterPosition(caret.getRow(), caret.getColumn(), -1), caret);
671    pane.clearSelection();
672  }
673
674  public void removeWordBehindCaret() {
675    document.remove(caret, pane.findSplitterPosition(caret.getRow(), caret.getColumn(), 1));
676    pane.clearSelection();
677  }
678
679  public void complete() {
680    if (codeHelperPane != null) {
681      codeHelperPane.callCodeHelperWithCompletion();
682    }
683  }
684
685  // MouseListener methods
686
687  public void mouseClicked(MouseEvent e) {
688  }
689
690  public void mousePressed(MouseEvent e) {
691    SCEDocumentPosition position = pane.viewToModel(e.getX(), e.getY());
692    SCEDocumentRows rowsModel = document.getRowsModel();
693
694    // hide quick help
695    if (quickHelpPane != null && quickHelpPane.isVisible()) {
696      quickHelpPane.setVisible(false);
697    }
698
699    // request focus
700    if (!pane.hasFocus()) pane.requestFocus();
701
702    // mouse button 1
703    if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
704      boolean unselect = e.getClickCount() == 1;
705      boolean select_word = e.getClickCount() == 2;
706      boolean select_line = e.getClickCount() == 3;
707
708      if (unselect || caret.getRow() != position.getRow() || caret.getColumn() != position.getColumn()) {
709        caret.moveTo(position.getRow(), position.getColumn(), e.isShiftDown());
710      }
711
712      // select the word or line
713      if(select_line || select_word) {
714        int left = pane.findSplitterInRow(caret.getRow(), caret.getColumn(), -1);
715        int right = pane.findSplitterInRow(caret.getRow(), caret.getColumn(), 1);
716        SCEDocumentPosition leftPosition = document.createDocumentPosition(caret.getRow(), left);
717        SCEDocumentPosition rightPosition = document.createDocumentPosition(caret.getRow(), right);
718
719        if(select_word) {
720          { // selecting content of brackets
721            SCEDocumentPosition pos = pane.viewToModelRoundOff(e.getX(), e.getY());
722
723            String line = document.getRowsModel().getRowAsString(pos.getRow());
724            char open = pos.getColumn() < line.length() ? line.charAt(pos.getColumn()) : ' ';
725
726            int direction = BracketHighlighting.getDirection(open);
727            if(direction != 0) {
728              char close = BracketHighlighting.getClosingChar(open);
729
730              SCEDocumentPosition closePos = BracketHighlighting.getClosingBracket(document, pos.getRow(), pos.getColumn(), open, close, direction);
731              if(closePos != null) {
732                if(direction < 0) {
733                  leftPosition = closePos;
734                  rightPosition = new SCEDocumentPosition(pos.getRow(), pos.getColumn()+1);
735                }
736                if(direction > 0) {
737                  leftPosition = pos;
738                  rightPosition = new SCEDocumentPosition(closePos.getRow(), closePos.getColumn()+1);
739                }
740              }
741            }
742          }
743
744
745
746          document.setSelectionRange(leftPosition, rightPosition, true);
747        }
748
749        if(select_line) {
750          SCEDocumentPosition startPosition = document.createDocumentPosition(caret.getRow(), 0);
751          SCEDocumentPosition endPosition = document.createDocumentPosition(caret.getRow() + 1, 0);
752          if (caret.getRow() >= rowsModel.getRowsCount() - 1) {
753            endPosition = document.createDocumentPosition(caret.getRow(), rowsModel.getRowLength(caret.getRow()));
754          }
755          document.setSelectionRange(startPosition, endPosition, true);
756        } else {
757          document.setSelectionRange(leftPosition, rightPosition, true);
758        }
759        pane.repaint();
760
761        lastMouseClick = System.currentTimeMillis();
762        return;
763      }
764    } else
765
766    // mouse button 2
767    if ((e.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) {
768      // insert content of select clipboard (middle mouse button under X11)
769      try {
770        caret.moveTo(position.getRow(), position.getColumn(), false);
771        Transferable transfer = selectionClipboard.getContents( null );
772        String data = (String) transfer.getTransferData( DataFlavor.stringFlavor );
773        document.insert(data, caret.getRow(), caret.getColumn());
774      } catch (Exception ignored) {}
775    } else
776
777    // mouse button 3
778    if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) {
779      pane.addTextHighlight(new SCETextHighlight(pane, position, position, Color.red));
780    }
781
782    lastMouseClick = System.currentTimeMillis();
783  }
784
785  public void mouseReleased(MouseEvent e) {
786  }
787
788  public void mouseEntered(MouseEvent e) {
789  }
790
791  public void mouseExited(MouseEvent e) {
792  }
793
794  // MouseMotionListener
795
796  public void mouseDragged(MouseEvent e) {
797    if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
798      if (caret.getSelectionMark() == null) caret.setSelectionMark();
799
800      SCEDocumentPosition position = pane.viewToModel(e.getX(), e.getY());
801      caret.moveTo(position.getRow(), position.getColumn(), true);
802    }
803  }
804
805  public void mouseMoved(MouseEvent e) {
806    if(!mouseIsVisible) {
807      pane.setCursor(defaultCursor);
808      mouseIsVisible = true;
809    }
810
811    SCEDocumentPosition position = pane.viewToModelRoundOff(e.getX(), e.getY());
812
813    SCETextHighlight currentOverHighlight = null; {
814      ArrayList<SCETextHighlight> highlights = pane.getTextHighlights();
815      synchronized (highlights) {
816        for(SCETextHighlight highlight : highlights) {
817          boolean over =
818            highlight.getStartPosition().compareTo(position) <= 0 &&
819            highlight.getEndPosition().compareTo(position) > 0;
820
821          if(over) {
822            currentOverHighlight = highlight;
823            break;
824          }
825        }
826      }
827    }
828
829    if(currentOverHighlight != overHighlight) {
830      if(overHighlight != null &&
831              (currentOverHighlight != null
832              || overHighlight.getStartPosition().getRow()-1 > position.getRow()
833              || overHighlight.getEndPosition().getRow()+1 < position.getRow()))
834      {
835        JComponent actionComponent = overHighlight.getActionComponent();
836        if(actionComponent != null) pane.remove(actionComponent);
837        overHighlight = null;
838      }
839
840      if(currentOverHighlight != null) {
841        overHighlight = currentOverHighlight;
842
843        JComponent actionComponent = overHighlight.getActionComponent();
844        if(actionComponent != null) {
845          Dimension size = actionComponent.getPreferredSize();
846
847
848          Point viewPos;
849          if(!overHighlight.isActionComponentAtEnd()) {
850            SCEPosition start = overHighlight.getStartPosition();
851            viewPos = pane.modelToView(start.getRow(), start.getColumn());
852            viewPos.x -= size.width;
853          } else {
854            SCEPosition end = overHighlight.getEndPosition();
855            viewPos = pane.modelToView(end.getRow(), end.getColumn());
856          }
857          viewPos.y -= (size.height - pane.getLineHeight()) / 2 + 1;
858
859          pane.add(actionComponent);
860          actionComponent.setSize(size);
861          actionComponent.setLocation(viewPos);
862          toVisibleRect(actionComponent, pane);
863        }
864      }
865    }
866  }
867
868  private void toVisibleRect(JComponent component, JComponent parent) {
869    Rectangle visible = parent.getVisibleRect();
870
871    int x = component.getX();
872    int y = component.getY();
873    x = Math.max(x ,visible.x);
874    y = Math.max(y ,visible.y);
875    x = Math.min(x ,visible.x + visible.width - component.getWidth());
876    y = Math.min(y ,visible.y + visible.height - component.getHeight());
877
878    component.setLocation(x,y);
879  }
880
881  public CodeHelperPane getCodeHelperPane() {
882    return codeHelperPane;
883  }
884}
Note: See TracBrowser for help on using the repository browser.