Gridarta Editor
JEditTextArea.java
Go to the documentation of this file.
1 /*
2  * JEditTextArea.java - jEdit's text component
3  * Copyright (C) 1999 Slava Pestov
4  * Copyright (C) 2000-2015 The Gridarta Developers.
5  *
6  * You may use and modify this package for any purpose. Redistribution is
7  * permitted, in both source and binary form, provided that this notice
8  * remains intact in all source distributions of this package.
9  */
10 
11 package net.sf.gridarta.textedit.textarea;
12 
13 import java.awt.AWTEvent;
14 import java.awt.Adjustable;
15 import java.awt.Font;
16 import java.awt.datatransfer.Clipboard;
17 import java.awt.datatransfer.DataFlavor;
18 import java.awt.datatransfer.StringSelection;
19 import java.awt.datatransfer.UnsupportedFlavorException;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.ActionListener;
22 import java.awt.event.AdjustmentEvent;
23 import java.awt.event.AdjustmentListener;
24 import java.awt.event.ComponentAdapter;
25 import java.awt.event.ComponentEvent;
26 import java.awt.event.FocusEvent;
27 import java.awt.event.FocusListener;
28 import java.awt.event.InputEvent;
29 import java.awt.event.KeyEvent;
30 import java.awt.event.MouseAdapter;
31 import java.awt.event.MouseEvent;
32 import java.awt.event.MouseMotionListener;
33 import java.awt.event.MouseWheelEvent;
34 import java.io.IOException;
35 import java.lang.reflect.Field;
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.Method;
38 import java.util.HashSet;
39 import java.util.Set;
40 import javax.swing.JComponent;
41 import javax.swing.JPopupMenu;
42 import javax.swing.JScrollBar;
43 import javax.swing.KeyStroke;
44 import javax.swing.SwingUtilities;
45 import javax.swing.Timer;
46 import javax.swing.event.DocumentEvent;
47 import javax.swing.event.DocumentListener;
48 import javax.swing.text.BadLocationException;
49 import javax.swing.text.Document;
50 import javax.swing.text.Segment;
51 import javax.swing.undo.AbstractUndoableEdit;
52 import javax.swing.undo.CannotRedoException;
53 import javax.swing.undo.CannotUndoException;
54 import javax.swing.undo.UndoableEdit;
55 import org.apache.log4j.Category;
56 import org.apache.log4j.Logger;
57 import org.jetbrains.annotations.NotNull;
58 import org.jetbrains.annotations.Nullable;
59 
90 public class JEditTextArea extends JComponent {
91 
95  private static final Category LOG = Logger.getLogger(JEditTextArea.class);
96 
100  private static final long serialVersionUID = 1L;
101 
105  @NotNull
106  private String unmodifiedText = "";
107 
108  @Nullable
110 
111  @NotNull
112  private static final Timer CARET_TIMER = new Timer(500, new CaretBlinker());
113 
114  static {
115  CARET_TIMER.setInitialDelay(500);
116  CARET_TIMER.start();
117  }
118 
119  @NotNull
120  private final TextAreaPainter painter;
121 
122  @Nullable
123  private final JPopupMenu popup;
124 
125  @NotNull
126  private final TextAreaCaret caret;
127 
128  @NotNull
129  private final JScrollBar vertical = new JScrollBar(Adjustable.VERTICAL);
130 
131  @NotNull
132  private final JScrollBar horizontal = new JScrollBar(Adjustable.HORIZONTAL);
133 
134  private boolean scrollBarsInitialized;
135 
136  @NotNull
137  private final InputHandler inputHandler;
138 
139  @NotNull
140  private final SyntaxDocument document;
141 
142  @NotNull
144 
145  @NotNull
147 
151  @NotNull
152  private final TextAreaConfig config;
153 
160  public JEditTextArea(@NotNull final TextAreaDefaults defaults, @NotNull final SyntaxDocument document, final boolean paintInvalid) {
161  // Enable the necessary events
162  enableEvents(AWTEvent.KEY_EVENT_MASK);
163 
164  // Initialize some misc. stuff
165  caret = new TextAreaCaret(defaults.getCaretVisible(), defaults.getCaretBlinks());
166  selection = new TextAreaSelection(document);
167  config = new TextAreaConfig(defaults.getEditable(), defaults.getElectricScroll());
168  painter = new TextAreaPainter(this, selection, caret, defaults, brackets, config, paintInvalid);
169  painter.recalculateVisibleLines();
170 
171  // Initialize the GUI
172  setLayout(new ScrollLayout(this));
173  add(ScrollLayout.CENTER, painter);
174  add(ScrollLayout.RIGHT, vertical);
175  add(ScrollLayout.BOTTOM, horizontal);
176 
177  // Add some event listeners
178  vertical.addAdjustmentListener(new AdjustHandler());
179  horizontal.addAdjustmentListener(new AdjustHandler());
180  painter.addComponentListener(new ComponentHandler());
181  final MouseHandler mouseHandler = new MouseHandler();
182  painter.addMouseListener(mouseHandler);
183  painter.addMouseWheelListener(mouseHandler);
184  painter.addMouseMotionListener(new DragHandler());
185  addFocusListener(new FocusHandler());
186 
187  // Load the defaults
188  inputHandler = defaults.getInputHandler();
189  this.document = document;
190  document.addDocumentListener(new DocumentHandler());
191  select(0, 0);
193 
194  popup = defaults.getPopup();
195 
196  // free tab key from the focus traversal manager
198 
199  // We don't seem to get the initial focus event?
200  focusedComponent = this;
201  }
202 
229  try {
230  // preparing the key set first, this should be harmless
231  final Set<KeyStroke> forwardTraversalKeys = new HashSet<>();
232  forwardTraversalKeys.add(KeyStroke.getKeyStroke("control TAB"));
233 
234  // here we try to access java.awt.KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS
235  final Field field = Class.forName("java.awt.KeyboardFocusManager").getField("FORWARD_TRAVERSAL_KEYS");
236  final Integer value = field.getInt(field); // store the value of this field
237 
238  for (final Method method : getClass().getMethods()) {
239  // here we try to find the method "setFocusTraversalKeys", and execute it if found
240  if (method.getName().equalsIgnoreCase("setFocusTraversalKeys")) {
241  method.invoke(this, value, forwardTraversalKeys);
242  // System.err.println("freeTabKeyFromFocusTraversal() succeeded!");
243  }
244  }
245  } catch (final ClassNotFoundException ignored) {
246  // ignore
247  } catch (final IllegalAccessException ignored) {
248  // ignore
249  } catch (final InvocationTargetException ignored) {
250  // ignore
251  } catch (final NoSuchFieldException ignored) {
252  // ignore
253  }
254  }
255 
260  @Override
261  public void setFont(@NotNull final Font font) {
262  painter.setFont(font);
263  }
264 
268  @NotNull
270  return inputHandler;
271  }
272 
277  public void setCaretVisible(final boolean caretVisible) {
278  caret.setCaretVisible(caretVisible);
279  painter.invalidateSelectedLines();
280  }
281 
285  public void blinkCaret() {
286  if (caret.blinkCaret()) {
287  painter.invalidateSelectedLines();
288  }
289  }
290 
295  public int getElectricScroll() {
296  return config.getElectricScroll();
297  }
298 
304  public void updateScrollBars() {
305  final int visibleLines = getVisibleLines();
306  if (visibleLines != 0) {
307  vertical.setValues(painter.getFirstLine(), visibleLines, 0, selection.getLineCount());
308  //vertical.setUnitIncrement(2);
309  vertical.setUnitIncrement(1); // scroll one line per click
310  vertical.setBlockIncrement(visibleLines);
311  }
312 
313  final int width = painter.getWidth();
314  if (width != 0) {
315  horizontal.setValues(-painter.getHorizontalOffset(), width, 0, width * 5);
316  //horizontal.setUnitIncrement(painter.getFontMetrics().charWidth('w'));
317  horizontal.setUnitIncrement(painter.getDefaultCharWidth());
318  horizontal.setBlockIncrement(width / 2);
319  }
320  }
321 
325  public int getFirstLine() {
326  return painter.getFirstLine();
327  }
328 
333  public void setFirstLine(final int firstLine) {
334  if (!painter.setFirstLine(firstLine)) {
335  return;
336  }
337 
338  if (firstLine != vertical.getValue()) {
340  }
341  painter.repaint();
342  }
343 
347  public int getVisibleLines() {
348  return painter.getVisibleLines();
349  }
350 
356  public void setHorizontalOffset(final int horizontalOffset) {
357  if (!painter.setHorizontalOffset(horizontalOffset)) {
358  return;
359  }
360 
361  if (horizontalOffset != horizontal.getValue()) {
363  }
364 
365  painter.repaint();
366  }
367 
373  public void setOrigin(final int firstLine, final int horizontalOffset) {
374  boolean changed = false;
375 
376  if (painter.setHorizontalOffset(horizontalOffset)) {
377  changed = true;
378  }
379 
380  if (painter.setFirstLine(firstLine)) {
381  changed = true;
382  }
383 
384  if (changed) {
386  painter.repaint();
387  }
388  }
389 
394  public void scrollToCaret() {
395  final int line = selection.getCaretLine();
396  final int lineStart = selection.getLineStartOffset(line);
397  final int offset = Math.max(0, Math.min(selection.getLineLength(line) - 1, selection.getCaretPosition() - lineStart));
398 
399  scrollTo(line, offset);
400  }
401 
407  public void setEditingFocus() {
408  try {
409  requestFocus();
410  setCaretVisible(true);
411  focusedComponent = this;
412  setCaretPosition(0); // set caret to 0, 0 coordinates
413  } catch (final NullPointerException e) {
414  LOG.error("Null Pointer Exception in JEditTextArea.setEditingFocus()");
415  }
416  }
417 
424  public void scrollTo(final int line, final int offset) {
425  // visibleLines == 0 before the component is realized
426  // we can't do any proper scrolling then, so we have
427  // this hack...
428  final int visibleLines = getVisibleLines();
429  final int electricScroll = config.getElectricScroll();
430  if (visibleLines == 0) {
431  setFirstLine(Math.max(0, line - electricScroll));
432  return;
433  }
434 
435  int newFirstLine = painter.getFirstLine();
436  final int horizontalOffset = painter.getHorizontalOffset();
437  int newHorizontalOffset = horizontalOffset;
438 
439  if (line < newFirstLine + electricScroll) {
440  newFirstLine = Math.max(0, line - electricScroll);
441  } else if (line + electricScroll >= newFirstLine + visibleLines) {
442  newFirstLine = (line - visibleLines) + electricScroll + 1;
443  if (newFirstLine + visibleLines >= selection.getLineCount()) {
444  newFirstLine = selection.getLineCount() - visibleLines;
445  }
446  if (newFirstLine < 0) {
447  newFirstLine = 0;
448  }
449  }
450 
451  final int x = painter.offsetToX2(line, offset);
452  final int width = painter.getFontMetrics().charWidth('w');
453 
454  if (x < 0) {
455  newHorizontalOffset = Math.min(0, horizontalOffset - x + width + 5);
456  } else if (x + width >= painter.getWidth()) {
457  newHorizontalOffset = horizontalOffset + (painter.getWidth() - x) - width - 5;
458  }
459 
460  setOrigin(newFirstLine, newHorizontalOffset);
461  }
462 
467  public int lineToY(final int line) {
468  return painter.lineToY(line);
469  }
470 
477  public int offsetToX(final int line, final int offset) {
478  return painter.offsetToX(line, offset);
479  }
480 
488  public int offsetToX2(final int line, final int offset) {
489  return painter.offsetToX2(line, offset);
490  }
491 
497  public int xToOffset(final int line, final int x) {
498  return painter.xToOffset(line, x);
499  }
500 
504  @NotNull
505  public Document getDocument() {
506  return selection.getDocument();
507  }
508 
513  public int getDocumentLength() {
514  return selection.getDocumentLength();
515  }
516 
520  public int getLineCount() {
521  return selection.getLineCount();
522  }
523 
530  public int getLineStartOffset(final int line) {
531  return selection.getLineStartOffset(line);
532  }
533 
540  public int getLineEndOffset(final int line) {
541  return selection.getLineEndOffset(line);
542  }
543 
547  @NotNull
548  public String getText() {
549  return selection.getText();
550  }
551 
555  public void setText(@NotNull final String text) {
556  selection.setText(text);
557  }
558 
565  @Nullable
566  public String getText(final int start, final int len) {
567  return selection.getText(start, len);
568  }
569 
577  public void getText(final int start, final int len, @NotNull final Segment segment) {
578  selection.getText(start, len, segment);
579  }
580 
586  @NotNull
587  public CharSequence getLineText(final int lineIndex) {
588  return selection.getLineText(lineIndex);
589  }
590 
594  public int getSelectionStart() {
595  return selection.getSelectionStart();
596  }
597 
601  public int getSelectionEnd() {
602  return selection.getSelectionEnd();
603  }
604 
610  public int getCaretPosition() {
611  return selection.getCaretPosition();
612  }
613 
617  public int getCaretLine() {
618  return selection.getCaretLine();
619  }
620 
626  public int getMarkPosition() {
627  return selection.getMarkPosition();
628  }
629 
636  public void setCaretPosition(final int caret) {
637  select(caret, caret);
638  }
639 
643  public void selectAll() {
645  }
646 
654  public void select(final int start, final int end) {
655  final int newStart;
656  final int newEnd;
657  final boolean newBias;
658  if (start <= end) {
659  newStart = start;
660  newEnd = end;
661  newBias = false;
662  } else {
663  newStart = end;
664  newEnd = start;
665  newBias = true;
666  }
667 
668  if (newStart < 0 || newEnd > getDocumentLength()) {
669  throw new IllegalArgumentException("Bounds out of range: " + newStart + ", " + newEnd);
670  }
671 
672  // If the new position is the same as the old, we don't
673  // do all this crap, however we still do the stuff at
674  // the end (clearing magic position, scrolling)
675  if (newStart != selection.getSelectionStart() || newEnd != selection.getSelectionEnd() || newBias != selection.getBiasLeft()) {
676  final int newStartLine = selection.getLineOfOffset(newStart);
677  final int newEndLine = selection.getLineOfOffset(newEnd);
678 
679  if (painter.isBracketHighlightEnabled()) {
680  final int oldBracketLine = brackets.getBracketLine();
681  if (oldBracketLine != -1) {
682  painter.invalidateLine(oldBracketLine);
683  }
685  final int newBracketLine = brackets.getBracketLine();
686  if (newBracketLine != -1) {
687  painter.invalidateLine(newBracketLine);
688  }
689  }
690 
691  painter.invalidateLineRange(selection.getSelectionStartLine(), selection.getSelectionEndLine());
692  painter.invalidateLineRange(newStartLine, newEndLine);
693 
695  selection.setSelection(newStart, newEnd, newStartLine, newEndLine, newBias);
696  }
697 
698  // When the user is typing, etc, we don't want the caret
699  // to blink
700  caret.setBlink(true);
701  CARET_TIMER.restart();
702 
703  selection.disableSelectionIfEmpty();
704 
705  config.setMagicCaret(-1); // Clear the `magic' caret position used by up/down
706 
707  scrollToCaret();
708  }
709 
713  @Nullable
714  public String getSelectedText() {
715  return selection.getSelectedText();
716  }
717 
722  public void setSelectedText(@NotNull final String selectedText) {
723  if (!config.isEditable()) {
724  throw new InternalError("Text component read only");
725  }
726 
727  selection.setSelectedText(selectedText);
728 
729  setCaretPosition(selection.getSelectionEnd());
730  }
731 
735  public boolean isEditable() {
736  return config.isEditable();
737  }
738 
743  public int getMagicCaretPosition() {
744  return config.getMagicCaret();
745  }
746 
752  public void setMagicCaretPosition(final int magicCaret) {
753  config.setMagicCaret(magicCaret);
754  }
755 
763  public void overwriteSetSelectedText(@NotNull final String str) {
764  // Don't overstrike if there is a selection
765  if (!config.isOverwrite() || selection.getSelectionStart() != selection.getSelectionEnd()) {
766  setSelectedText(str);
767  return;
768  }
769 
770  // Don't overstrike if we're on the end of
771  // the line
772  final int caret = selection.getCaretPosition();
773  final int caretLineEnd = selection.getLineEndOffset(selection.getCaretLine());
774  if (caretLineEnd - caret <= str.length()) {
775  setSelectedText(str);
776  return;
777  }
778 
780 
781  try {
782  document.remove(caret, str.length());
783  document.insertString(caret, str, null);
784  } catch (final BadLocationException bl) {
785  bl.printStackTrace();
786  } finally {
788  }
789  }
790 
795  public boolean isOverwriteEnabled() {
796  return config.isOverwrite();
797  }
798 
803  public void setOverwriteEnabled(final boolean overwrite) {
804  config.setOverwrite(overwrite);
805  painter.invalidateSelectedLines();
806  }
807 
811  public boolean isSelectionRectangular() {
812  return selection.isSelectionRectangular();
813  }
814 
820  public void setSelectionRectangular(final boolean rectangleSelect) {
821  selection.setSelectionRectangular(rectangleSelect);
822  painter.invalidateSelectedLines();
823  }
824 
829  public void cut() {
830  if (config.isEditable()) {
831  copy();
832  setSelectedText("");
833  }
834  }
835 
839  public void copy() {
840  if (selection.getSelectionStart() != selection.getSelectionEnd()) {
841  final Clipboard clipboard = getToolkit().getSystemClipboard();
842 
843  final String selection = this.selection.getSelectedText();
844 
845  final int repeatCount = inputHandler.getRepeatCount();
846  final StringBuilder buf = new StringBuilder();
847  for (int i = 0; i < repeatCount; i++) {
848  buf.append(selection);
849  }
850 
851  clipboard.setContents(new StringSelection(buf.toString()), null);
852  }
853  }
854 
858  public void paste() {
859  if (config.isEditable()) {
860  final Clipboard clipboard = getToolkit().getSystemClipboard();
861  try {
862  // The MacOS MRJ doesn't convert \r to \n,
863  // so do it here
864  final String selection = ((String) clipboard.getContents(this).getTransferData(DataFlavor.stringFlavor)).replace('\r', '\n');
865 
866  final int repeatCount = inputHandler.getRepeatCount();
867  final StringBuilder buf = new StringBuilder();
868  for (int i = 0; i < repeatCount; i++) {
869  buf.append(selection);
870  }
871  setSelectedText(buf.toString());
872  } catch (final IOException e) {
873  getToolkit().beep();
874  LOG.error("Clipboard does not contain a string");
875  } catch (final UnsupportedFlavorException e) {
876  getToolkit().beep();
877  LOG.error("Clipboard does not contain a string");
878  }
879  }
880  }
881 
886  @Override
887  public void removeNotify() {
888  super.removeNotify();
889  if (focusedComponent == this) {
890  focusedComponent = null;
891  }
892  }
893 
898  @Override
899  public void processKeyEvent(@NotNull final KeyEvent e) {
900  switch (e.getID()) {
901  case KeyEvent.KEY_TYPED:
902  inputHandler.keyTyped(e);
903  break;
904 
905  case KeyEvent.KEY_PRESSED:
906  inputHandler.keyPressed(e);
907  break;
908 
909  case KeyEvent.KEY_RELEASED:
910  inputHandler.keyReleased(e);
911  break;
912  }
913 
914  super.processKeyEvent(e);
915  }
916 
917  private void updateBracketHighlight(final int newCaretPosition) {
918  if (newCaretPosition == 0) {
919  brackets.clear();
920  return;
921  }
922 
923  try {
924  final int offset = TextUtilities.findMatchingBracket(document, newCaretPosition - 1);
925  if (offset != -1) {
926  final int bracketLine = selection.getLineOfOffset(offset);
927  final int bracketPosition = offset - selection.getLineStartOffset(bracketLine);
928  brackets.set(bracketPosition, bracketLine);
929  return;
930  }
931  } catch (final BadLocationException bl) {
932  bl.printStackTrace();
933  }
934 
935  brackets.clear();
936  }
937 
938  private void documentChanged(@NotNull final DocumentEvent evt) {
939  final DocumentEvent.ElementChange ch = evt.getChange(document.getDefaultRootElement());
940 
941  final int count;
942  if (ch == null) {
943  count = 0;
944  } else {
945  count = ch.getChildrenAdded().length - ch.getChildrenRemoved().length;
946  }
947 
948  final int line = selection.getLineOfOffset(evt.getOffset());
949  if (count == 0) {
950  painter.invalidateLine(line);
951  } else {
952  final int firstLine = painter.getFirstLine();
953  if (line < firstLine) {
954  // do magic stuff
955  setFirstLine(firstLine + count);
956  // end of magic stuff
957  } else {
958  painter.invalidateLineRange(line, firstLine + getVisibleLines());
960  }
961  }
962  }
963 
970  public boolean isModified() {
971  return !unmodifiedText.equals(selection.getText());
972  }
973 
977  public void resetModified() {
978  unmodifiedText = selection.getText();
979  }
980 
981  private static class CaretBlinker implements ActionListener {
982 
983  @Override
984  public void actionPerformed(@NotNull final ActionEvent e) {
985  if (focusedComponent != null && focusedComponent.hasFocus()) {
986  focusedComponent.blinkCaret();
987  }
988  }
989 
990  }
991 
992  private class AdjustHandler implements AdjustmentListener {
993 
994  @Override
995  public void adjustmentValueChanged(@NotNull final AdjustmentEvent e) {
996  if (!scrollBarsInitialized) {
997  return;
998  }
999 
1000  // If this is not done, mousePressed events accumulate
1001  // and the result is that scrolling doesn't stop after
1002  // the mouse is released
1003  SwingUtilities.invokeLater(new Runnable() {
1004 
1005  @Override
1006  public void run() {
1007  if (e.getAdjustable() == vertical) {
1008  setFirstLine(vertical.getValue());
1009  } else {
1010  setHorizontalOffset(-horizontal.getValue());
1011  }
1012  }
1013 
1014  });
1015  }
1016 
1017  }
1018 
1019  private class ComponentHandler extends ComponentAdapter {
1020 
1021  @Override
1022  public void componentResized(@NotNull final ComponentEvent e) {
1023  painter.recalculateVisibleLines();
1024  scrollBarsInitialized = true;
1025  }
1026 
1027  }
1028 
1029  private class DocumentHandler implements DocumentListener {
1030 
1031  @Override
1032  public void insertUpdate(@NotNull final DocumentEvent e) {
1033  documentChanged(e);
1034 
1035  final int offset = e.getOffset();
1036  final int length = e.getLength();
1037  final int selectionStart = selection.getSelectionStart();
1038  final int selectionEnd = selection.getSelectionEnd();
1039 
1040  final int newStart;
1041  if (selectionStart > offset || (selectionStart == selectionEnd && selectionStart == offset)) {
1042  newStart = selectionStart + length;
1043  } else {
1044  newStart = selectionStart;
1045  }
1046 
1047  final int newEnd;
1048  if (selectionEnd >= offset) {
1049  newEnd = selectionEnd + length;
1050  } else {
1051  newEnd = selectionEnd;
1052  }
1053 
1054  select(newStart, newEnd);
1055  }
1056 
1057  @Override
1058  public void removeUpdate(@NotNull final DocumentEvent e) {
1059  documentChanged(e);
1060 
1061  final int offset = e.getOffset();
1062  final int length = e.getLength();
1063  final int selectionStart = selection.getSelectionStart();
1064  final int selectionEnd = selection.getSelectionEnd();
1065 
1066  final int newStart;
1067  if (selectionStart > offset) {
1068  if (selectionStart > offset + length) {
1069  newStart = selectionStart - length;
1070  } else {
1071  newStart = offset;
1072  }
1073  } else {
1074  newStart = selectionStart;
1075  }
1076 
1077  final int newEnd;
1078  if (selectionEnd > offset) {
1079  if (selectionEnd > offset + length) {
1080  newEnd = selectionEnd - length;
1081  } else {
1082  newEnd = offset;
1083  }
1084  } else {
1085  newEnd = selectionEnd;
1086  }
1087 
1088  select(newStart, newEnd);
1089  }
1090 
1091  @Override
1092  public void changedUpdate(@NotNull final DocumentEvent e) {
1093  }
1094 
1095  }
1096 
1097  private class DragHandler implements MouseMotionListener {
1098 
1099  @Override
1100  public void mouseDragged(@NotNull final MouseEvent e) {
1101  if (popup != null && popup.isVisible()) {
1102  return;
1103  }
1104 
1105  setSelectionRectangular((e.getModifiers() & InputEvent.CTRL_MASK) != 0);
1106  select(selection.getMarkPosition(), painter.xyToOffset(e.getX(), e.getY()));
1107  }
1108 
1109  @Override
1110  public void mouseMoved(@NotNull final MouseEvent e) {
1111  }
1112 
1113  }
1114 
1115  private class FocusHandler implements FocusListener {
1116 
1117  @Override
1118  public void focusGained(@NotNull final FocusEvent e) {
1119  setCaretVisible(true);
1120  focusedComponent = JEditTextArea.this;
1121  }
1122 
1123  @Override
1124  public void focusLost(@NotNull final FocusEvent e) {
1125  setCaretVisible(false);
1126  focusedComponent = null;
1127  }
1128 
1129  }
1130 
1134  private class MouseHandler extends MouseAdapter {
1135 
1136  @Override
1137  public void mousePressed(@NotNull final MouseEvent e) {
1138  requestFocus();
1139 
1140  // Focus events not fired sometimes?
1141  setCaretVisible(true);
1142  focusedComponent = JEditTextArea.this;
1143 
1144  if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0 && popup != null) {
1145  popup.show(painter, e.getX(), e.getY());
1146  return;
1147  }
1148 
1149  final int line = painter.yToLine(e.getY());
1150  final int offset = xToOffset(line, e.getX());
1151  final int dot = selection.getLineStartOffset(line) + offset;
1152 
1153  switch (e.getClickCount()) {
1154  case 1:
1155  doSingleClick(e, dot);
1156  break;
1157  case 2:
1158  doDoubleClick(line, offset, dot);
1159  break;
1160  case 3:
1161  doTripleClick(line);
1162  break;
1163  }
1164  }
1165 
1166  @Override
1167  public void mouseWheelMoved(@NotNull final MouseWheelEvent e) {
1168  final int diff;
1169  if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
1170  diff = e.getUnitsToScroll() * vertical.getUnitIncrement(1);
1171  } else {
1172  diff = e.getWheelRotation() * vertical.getBlockIncrement();
1173  }
1174  vertical.setValue(vertical.getValue() + diff);
1175  }
1176 
1177  private void doSingleClick(@NotNull final InputEvent evt, final int dot) {
1178  if ((evt.getModifiers() & InputEvent.SHIFT_MASK) == 0) {
1179  setCaretPosition(dot);
1180  } else {
1181  selection.setSelectionRectangular((evt.getModifiers() & InputEvent.CTRL_MASK) != 0);
1182  select(selection.getMarkPosition(), dot);
1183  }
1184  }
1185 
1186  private void doDoubleClick(final int line, final int offset, final int dot) {
1187  // Ignore empty lines
1188  if (selection.getLineLength(line) == 0) {
1189  return;
1190  }
1191 
1192  try {
1193  int bracket = TextUtilities.findMatchingBracket(document, Math.max(0, dot - 1));
1194  if (bracket != -1) {
1195  int mark = selection.getMarkPosition();
1196  // Hack
1197  if (bracket > mark) {
1198  bracket++;
1199  mark--;
1200  }
1201  select(mark, bracket);
1202  return;
1203  }
1204  } catch (final BadLocationException bl) {
1205  bl.printStackTrace();
1206  }
1207 
1208  // Ok, it's not a bracket... select the word
1209  final CharSequence lineText = selection.getLineText(line);
1210  char ch = lineText.charAt(Math.max(0, offset - 1));
1211 
1212  String noWordSep = (String) document.getProperty("noWordSep");
1213  if (noWordSep == null) {
1214  noWordSep = "";
1215  }
1216 
1217  // If the user clicked on a non-letter char,
1218  // we select the surrounding non-letters
1219  final boolean selectNoLetter = !Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1;
1220 
1221  int wordStart = 0;
1222 
1223  for (int i = offset - 1; i >= 0; i--) {
1224  ch = lineText.charAt(i);
1225  if (selectNoLetter ^ (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)) {
1226  wordStart = i + 1;
1227  break;
1228  }
1229  }
1230 
1231  int wordEnd = lineText.length();
1232  for (int i = offset; i < lineText.length(); i++) {
1233  ch = lineText.charAt(i);
1234  if (selectNoLetter ^ (!Character.isLetterOrDigit(ch) && noWordSep.indexOf(ch) == -1)) {
1235  wordEnd = i;
1236  break;
1237  }
1238  }
1239 
1240  final int lineStart = selection.getLineStartOffset(line);
1241  select(lineStart + wordStart, lineStart + wordEnd);
1242 
1243  /*
1244  String lineText = getLineText(line);
1245  String noWordSep = (String)document.getProperty("noWordSep");
1246  int wordStart = TextUtilities.findWordStart(lineText, offset, noWordSep);
1247  int wordEnd = TextUtilities.findWordEnd(lineText, offset, noWordSep);
1248 
1249  int lineStart = getLineStartOffset(line);
1250  select(lineStart + wordStart, lineStart + wordEnd);
1251  */
1252  }
1253 
1254  private void doTripleClick(final int line) {
1255  select(selection.getLineStartOffset(line), selection.getLineEndOffset(line) - 1);
1256  }
1257 
1258  }
1259 
1260  private class CaretUndo extends AbstractUndoableEdit {
1261 
1265  private static final long serialVersionUID = 1L;
1266 
1267  private int start;
1268 
1269  private int end;
1270 
1271  private CaretUndo(final int start, final int end) {
1272  this.start = start;
1273  this.end = end;
1274  }
1275 
1276  @Override
1277  public boolean isSignificant() {
1278  return false;
1279  }
1280 
1281  @NotNull
1282  @Override
1283  public String getPresentationName() {
1284  return "caret move";
1285  }
1286 
1287  @Override
1288  public void undo() throws CannotUndoException {
1289  super.undo();
1290 
1291  select(start, end);
1292  }
1293 
1294  @Override
1295  public void redo() throws CannotRedoException {
1296  super.redo();
1297 
1298  select(start, end);
1299  }
1300 
1301  @Override
1302  public boolean addEdit(@NotNull final UndoableEdit anEdit) {
1303  if (anEdit instanceof CaretUndo) {
1304  final CaretUndo caretUndo = (CaretUndo) anEdit;
1305  start = caretUndo.start;
1306  end = caretUndo.end;
1307  caretUndo.die();
1308 
1309  return true;
1310  } else {
1311  return false;
1312  }
1313  }
1314 
1315  }
1316 
1317 }
void invalidateSelectedLines()
Repaints the lines containing the selection.
void setSelectedText(@NotNull final String selectedText)
Replaces the selection with the specified text.
void setCaretVisible(final boolean caretVisible)
Sets if the caret should be visible.
boolean isModified()
Return whether the text content has been modified from the "unmodified" state.
int getFirstLine()
Returns the line displayed at the text area&#39;s origin.
void clear()
Clears the highlighted bracket.
void setSelectionRectangular(final boolean rectangleSelect)
Sets if the selection should be rectangular.
A document implementation that can be tokenized by the syntax highlighting system.
int lineToY(final int line)
Converts a line index to a y co-ordinate.
final void setFont(@NotNull final Font font)
void setEditingFocus()
Sets the focus to this TextArea, so this component is instantly registered for key press events...
int getMarkPosition()
Returns the mark position.
int getSelectionEnd()
Returns the selection end offset.
boolean isOverwrite()
Returns whether overwrite mode is active.
int offsetToX(final int line, final int offset)
Converts an offset in a line into an x co-ordinate.
int getCaretPosition()
Returns the caret position.
void processKeyEvent(@NotNull final KeyEvent e)
Forwards key events directly to the input handler.
int getSelectionStart()
Returns the selection start offset.
int getSelectionStart()
Returns the selection start offset.
void setHorizontalOffset(final int horizontalOffset)
Sets the horizontal offset of drawn lines.
FontMetrics getFontMetrics()
Returns the font metrics used by this component.
void recalculateVisibleLines()
Recalculates the number of visible lines.
void updateScrollBars()
Updates the state of the scroll bars.
int getSelectionEnd()
Returns the selection end offset.
static final long serialVersionUID
Serial Version UID.
static int findMatchingBracket(final Document doc, final int offset)
Returns the offset of the bracket matching the one at the specified offset of the document...
int getLineCount()
Returns the number of lines in the document.
void setText(@NotNull final String text)
Sets the entire text of this text area.
int getDocumentLength()
Returns the length of the document.
int getLineLength(final int line)
Returns the length of the specified line.
int getHorizontalOffset()
Returns the horizontal offset of drawn lines.
void setOrigin(final int firstLine, final int horizontalOffset)
A fast way of changing both the first line and horizontal offset.
void paste()
Inserts the clipboard contents into the text.
boolean isBracketHighlightEnabled()
Returns whether bracket highlighting is enabled.
boolean setHorizontalOffset(final int horizontalOffset)
An input handler converts the user&#39;s key strokes into concrete actions.
String getText(final int start, final int len)
Returns the specified substring of the document.
int xyToOffset(final int x, final int y)
Converts a point to an offset, from the start of the text.
int getDefaultCharWidth()
This works only for fonts with fixed line height.
void setOverwrite(final boolean overwrite)
Sets whether overwrite mode is active.
CharSequence getLineText(final int lineIndex)
Returns the text on the specified line.
JEditTextArea(@NotNull final TextAreaDefaults defaults, @NotNull final SyntaxDocument document, final boolean paintInvalid)
Creates a new JEditTextArea with the specified settings.
int xToOffset(final int line, final int x)
Converts an x co-ordinate to an offset within a line.
int getLineCount()
Returns the number of lines in the document.
InputHandler getInputHandler()
Returns the input handler.
boolean addEdit(@NotNull final UndoableEdit anEdit)
void updateBracketHighlight(final int newCaretPosition)
void setSelection(final int newStart, final int newEnd, final int newStartLine, final int newEndLine, final boolean newBias)
void selectAll()
Selects all text in the document.
SyntaxDocument getDocument()
Returns the document this text area is editing.
boolean isEditable()
Returns true if this text area is editable, false otherwise.
int offsetToX2(final int line, final int offset)
Converts an offset in a line into an x co-ordinate.
static void beginCompoundEdit()
Starts a compound edit that can be undone in one operation.
int yToLine(final int y)
Converts a y co-ordinate to a line index.
void setText(@NotNull final String text)
Sets the entire text of this text area.
static final Category LOG
The Logger for printing log messages.
void setFont(@NotNull final Font font)
Set the TextArea font.
int getLineStartOffset(final int line)
Returns the start offset of the specified line.
int getSelectionStartLine()
Returns the selection start line.
void select(final int start, final int end)
Selects from the start offset to the end offset.
String getSelectedText()
Returns the selected text, or null if no selection is active.
void scrollToCaret()
Ensures that the caret is visible by scrolling the text area if necessary.
void invalidateLineRange(final int firstLine, final int lastLine)
Marks a range of lines as needing a repaint.
boolean isSelectionRectangular()
Returns true if the selection is rectangular, false otherwise.
int getCaretPosition()
Returns the caret position.
void setMagicCaretPosition(final int magicCaret)
Sets the `magic&#39; caret position.
CharSequence getLineText(final int lineIndex)
Returns the text on the specified line.
static void endCompoundEdit()
Ends a compound edit that can be undone in one operation.
void setSelectionRectangular(final boolean rectangleSelect)
Sets if the selection should be rectangular.
final TextAreaConfig config
The TextAreaConfig for this instance.
void doSingleClick(@NotNull final InputEvent evt, final int dot)
String getText(final int start, final int len)
Returns the specified substring of the document.
int getElectricScroll()
Returns the number of lines from the top and button of the text area that are always visible...
int getFirstLine()
Returns the line displayed at the text area&#39;s origin.
Document getDocument()
Returns the document this text area is editing.
String getText()
Returns the entire text of this text area.
void resetModified()
Reset the "modified" state.
int offsetToX(final int line, final int offset)
Converts an offset in a line into an x co-ordinate.
void mouseWheelMoved(@NotNull final MouseWheelEvent e)
void doDoubleClick(final int line, final int offset, final int dot)
void removeNotify()
Called by the AWT when this component is removed from it&#39;s parent.
int getLineEndOffset(final int line)
Returns the end offset of the specified line.
int getMagicCaretPosition()
Returns the `magic&#39; caret position.
int lineToY(final int line)
Converts a line index to a y co-ordinate.
int getRepeatCount()
Returns the number of times the next action will be repeated.
void copy()
Places the selected text into the clipboard.
void overwriteSetSelectedText(@NotNull final String str)
Similar to.
void documentChanged(@NotNull final DocumentEvent evt)
Maintains information about the highlighted pairs of brackets.
boolean isSelectionRectangular()
Returns true if the selection is rectangular, false otherwise.
boolean isOverwriteEnabled()
Returns whether overwrite mode is active.
void setFirstLine(final int firstLine)
Sets the line displayed at the text area&#39;s origin without updating the scroll bars.
void cut()
Deletes the selected text from the text area and places it into the clipboard.
void setCaretPosition(final int caret)
Sets the caret position.
int xToOffset(final int line, final int x)
Converts an x co-ordinate to an offset within a line.
void setCaretVisible(final boolean caretVisible)
Sets if the caret should be visible.
String unmodifiedText
The text contents in the last "unmodified" state.
void getText(final int start, final int len, @NotNull final Segment segment)
Copies the specified substring of the document into a segment.
int getLineStartOffset(final int line)
Returns the start offset of the specified line.
int getLineEndOffset(final int line)
Returns the end offset of the specified line.
void freeTabKeyFromFocusTraversal()
In JDKs above 1.4, the tab key is used for focus traversal.
int getVisibleLines()
Returns the number of lines visible in this text area.
static void addUndoableEdit(@NotNull final UndoableEdit edit)
Adds an undoable edit to this document&#39;s undo list.
void adjustmentValueChanged(@NotNull final AdjustmentEvent e)
void setOverwriteEnabled(final boolean overwrite)
Sets whether overwrite mode is active.
Miscellaneous configuration settings for JEditTextArea.
Class with several utility functions used by the text area component.
int getBracketLine()
Returns the line of the highlighted bracket (the bracket matching the one before the caret)...
int getSelectionEndLine()
Returns the selection end line.
int getLineOfOffset(final int offset)
Returns the line containing the specified offset.
void scrollTo(final int line, final int offset)
Ensures that the specified line and offset is visible by scrolling the text area if necessary...
void set(final int bracketPosition, final int bracketLine)
Sets the highlighted bracket.
int getDocumentLength()
Returns the length of the document.
String getSelectedText()
Returns the selected text, or null if no selection is active.
int offsetToX2(final int line, final int offset)
Converts an offset in a line into an x co-ordinate.
Encapsulates default settings for a text area.
void setSelectedText(@NotNull final String selectedText)
Replaces the selection with the specified text.
void invalidateLine(final int line)
Marks a line as needing a repaint.