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

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