source: other-projects/rsyntax-textarea/src/java/org/fife/ui/rsyntaxtextarea/RSyntaxTextArea.java@ 25584

Last change on this file since 25584 was 25584, checked in by davidb, 12 years ago

Initial cut an a text edit area for GLI that supports color syntax highlighting

File size: 74.1 KB
Line 
1/*
2 * 01/27/2004
3 *
4 * RSyntaxTextArea.java - An extension of RTextArea that adds
5 * the ability to syntax highlight certain programming languages.
6 *
7 * This library is distributed under a modified BSD license. See the included
8 * RSyntaxTextArea.License.txt file for details.
9 */
10package org.fife.ui.rsyntaxtextarea;
11
12import java.awt.Color;
13import java.awt.Component;
14import java.awt.Cursor;
15import java.awt.Font;
16import java.awt.FontMetrics;
17import java.awt.Graphics;
18import java.awt.Graphics2D;
19import java.awt.Point;
20import java.awt.Rectangle;
21import java.awt.RenderingHints;
22import java.awt.Toolkit;
23import java.awt.datatransfer.Clipboard;
24import java.awt.event.ActionEvent;
25import java.awt.event.ActionListener;
26import java.awt.event.InputEvent;
27import java.awt.event.MouseEvent;
28import java.io.File;
29import java.net.MalformedURLException;
30import java.net.URL;
31import java.util.Collections;
32import java.util.HashMap;
33import java.util.List;
34import java.util.Map;
35import java.util.ResourceBundle;
36import javax.swing.JMenu;
37import javax.swing.JPopupMenu;
38import javax.swing.Timer;
39import javax.swing.UIManager;
40import javax.swing.event.CaretEvent;
41import javax.swing.event.EventListenerList;
42import javax.swing.event.HyperlinkEvent;
43import javax.swing.event.HyperlinkListener;
44import javax.swing.text.BadLocationException;
45import javax.swing.text.Document;
46import javax.swing.text.Element;
47import javax.swing.text.Highlighter;
48
49import org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip;
50import org.fife.ui.rsyntaxtextarea.folding.Fold;
51import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
52import org.fife.ui.rsyntaxtextarea.parser.Parser;
53import org.fife.ui.rsyntaxtextarea.parser.ToolTipInfo;
54import org.fife.ui.rtextarea.Gutter;
55import org.fife.ui.rtextarea.RTextArea;
56import org.fife.ui.rtextarea.RTextAreaUI;
57import org.fife.ui.rtextarea.RTextScrollPane;
58import org.fife.ui.rtextarea.RecordableTextAction;
59
60
61
62/**
63 * An extension of <code>RTextArea</code> that adds syntax highlighting
64 * of certain programming languages to its list of features. Languages
65 * currently supported include:
66 *
67 * <table>
68 * <tr>
69 * <td style="vertical-align: top">
70 * <ul>
71 * <li>ActionScript
72 * <li>Assembler (X86)
73 * <li>BBCode
74 * <li>C
75 * <li>C++
76 * <li>CSS
77 * <li>C#
78 * <li>Clojure
79 * <li>Delphi
80 * <li>DTD
81 * <li>Fortran
82 * <li>Groovy
83 * <li>HTML
84 * <li>Java
85 * <li>JavaScript
86 * <li>JSP
87 * </ul>
88 * </td>
89 * <td style="vertical-align: top">
90 * <ul>
91 * <li>LaTeX
92 * <li>Lisp
93 * <li>Lua
94 * <li>Make
95 * <li>MXML
96 * <li>Perl
97 * <li>PHP
98 * <li>Ruby
99 * <li>SAS
100 * <li>Scala
101 * <li>SQL
102 * <li>Tcl
103 * <li>UNIX shell scripts
104 * <li>Windows batch
105 * <li>XML files
106 * </ul>
107 * </td>
108 * </tr>
109 * </table>
110 *
111 * Other added features include:
112 * <ul style="columns: 2 12em; column-gap: 1em">
113 * <li>Code folding
114 * <li>Bracket matching
115 * <li>Auto-indentation
116 * <li>Copy as RTF
117 * <li>Clickable hyperlinks (if the language scanner being used supports it)
118 * <li>A pluggable "parser" system that can be used to implement syntax
119 * validation, spell checking, etc.
120 * </ul>
121 *
122 * It is recommended that you use an instance of
123 * {@link org.fife.ui.rtextarea.RTextScrollPane} instead of a regular
124 * <code>JScrollPane</code> as this class allows you to add line numbers and
125 * bookmarks easily to your text area.
126 *
127 * @author Robert Futrell
128 * @version 2.0.3
129 * @see TextEditorPane
130 */
131public class RSyntaxTextArea extends RTextArea implements SyntaxConstants {
132
133 public static final String ANIMATE_BRACKET_MATCHING_PROPERTY = "RSTA.animateBracketMatching";
134 public static final String ANTIALIAS_PROPERTY = "RSTA.antiAlias";
135 public static final String AUTO_INDENT_PROPERTY = "RSTA.autoIndent";
136 public static final String BRACKET_MATCHING_PROPERTY = "RSTA.bracketMatching";
137 public static final String CLEAR_WHITESPACE_LINES_PROPERTY = "RSTA.clearWhitespaceLines";
138 public static final String CLOSE_CURLY_BRACES_PROPERTY = "RSTA.closeCurlyBraces";
139 public static final String CLOSE_MARKUP_TAGS_PROPERTY = "RSTA.closeMarkupTags";
140 public static final String CODE_FOLDING_PROPERTY = "RSTA.codeFolding";
141 public static final String EOL_VISIBLE_PROPERTY = "RSTA.eolMarkersVisible";
142 public static final String FOCUSABLE_TIPS_PROPERTY = "RSTA.focusableTips";
143 public static final String FRACTIONAL_FONTMETRICS_PROPERTY = "RSTA.fractionalFontMetrics";
144 public static final String HYPERLINKS_ENABLED_PROPERTY = "RSTA.hyperlinksEnabled";
145 public static final String MARK_OCCURRENCES_PROPERTY = "RSTA.markOccurrences";
146 public static final String MARKED_OCCURRENCES_CHANGED_PROPERTY = "RSTA.markedOccurrencesChanged";
147 public static final String PARSER_NOTICES_PROPERTY = "RSTA.parserNotices";
148 public static final String SYNTAX_SCHEME_PROPERTY = "RSTA.syntaxScheme";
149 public static final String SYNTAX_STYLE_PROPERTY = "RSTA.syntaxStyle";
150 public static final String TAB_LINE_COLOR_PROPERTY = "RSTA.tabLineColor";
151 public static final String TAB_LINES_PROPERTY = "RSTA.tabLines";
152 public static final String VISIBLE_WHITESPACE_PROPERTY = "RSTA.visibleWhitespace";
153
154 private static final Color DEFAULT_BRACKET_MATCH_BG_COLOR = new Color(234,234,255);
155 private static final Color DEFAULT_BRACKET_MATCH_BORDER_COLOR = new Color(0,0,128);
156 private static final Color DEFAULT_SELECTION_COLOR = new Color(200,200,255);
157
158 private static final String MSG = "org.fife.ui.rsyntaxtextarea.RSyntaxTextArea";
159
160 private JMenu foldingMenu;
161 private static RecordableTextAction toggleCurrentFoldAction;
162 private static RecordableTextAction collapseAllCommentFoldsAction;
163 private static RecordableTextAction collapseAllFoldsAction;
164 private static RecordableTextAction expandAllFoldsAction;
165
166 /** The key for the syntax style to be highlighting. */
167 private String syntaxStyleKey;
168
169 /** The colors used for syntax highlighting. */
170 private SyntaxScheme syntaxScheme;
171
172 /** Handles code templates. */
173 private static CodeTemplateManager codeTemplateManager;
174
175 /** Whether or not templates are enabled. */
176 private static boolean templatesEnabled;
177
178 /**
179 * The rectangle surrounding the "matched bracket" if bracket matching
180 * is enabled.
181 */
182Rectangle match;
183
184 /**
185 * Colors used for the "matched bracket" if bracket matching is enabled.
186 */
187 private Color matchedBracketBGColor;
188 private Color matchedBracketBorderColor;
189
190 /** The location of the last matched bracket. */
191 private int lastBracketMatchPos;
192
193 /** Whether or not bracket matching is enabled. */
194 private boolean bracketMatchingEnabled;
195
196 /** Whether or not bracket matching is animated. */
197 private boolean animateBracketMatching;
198
199 private BracketMatchingTimer bracketRepaintTimer;
200
201 /**
202 * Whether or not auto-indent is on.
203 */
204 private boolean autoIndentEnabled;
205
206 /**
207 * Whether curly braces should be closed on Enter key presses, (if the
208 * current language supports it).
209 */
210 private boolean closeCurlyBraces;
211
212 /**
213 * Whether closing markup tags should be automatically completed when
214 * "<code>&lt;/</code>" is typed (if the current language is a markup
215 * language).
216 */
217 private boolean closeMarkupTags;
218
219 /**
220 * Whether or not lines with nothing but whitespace are "made empty."
221 */
222 private boolean clearWhitespaceLines;
223
224 /** Whether we are displaying visible whitespace (spaces and tabs). */
225 private boolean whitespaceVisible;
226
227 /** Whether EOL markers should be visible at the end of each line. */
228 private boolean eolMarkersVisible;
229
230 /** Whether tab lines are enabled. */
231 private boolean paintTabLines;
232
233 /** The color to use when painting tab lines. */
234 private Color tabLineColor;
235
236 /**
237 * Whether hyperlinks are enabled (must be supported by the syntax
238 * scheme being used).
239 */
240 private boolean hyperlinksEnabled;
241
242 /** The color to use when painting hyperlinks. */
243 private Color hyperlinkFG;
244
245 /**
246 * Mask used to determine if the correct key is being held down to scan
247 * for hyperlinks (ctrl, meta, etc.).
248 */
249 private int linkScanningMask;
250
251 /** Used during "Copy as RTF" operations. */
252 private RtfGenerator rtfGenerator;
253
254 /** Handles "mark occurrences" support. */
255 private MarkOccurrencesSupport markOccurrencesSupport;
256
257 /** The color used to render "marked occurrences." */
258 private Color markOccurrencesColor;
259
260 /** Whether a border should be painted around marked occurrences. */
261 private boolean paintMarkOccurrencesBorder;
262
263 /** Metrics of the text area's font. */
264 private FontMetrics defaultFontMetrics;
265
266 /** Manages running the parser. */
267 private ParserManager parserManager;
268
269 /**
270 * Whether the editor is currently scanning for hyperlinks on mouse
271 * movement.
272 */
273 private boolean isScanningForLinks;
274
275 private int hoveredOverLinkOffset;
276
277 private FoldManager foldManager;
278
279 /** Whether "focusable" tool tips are used instead of standard ones. */
280 private boolean useFocusableTips;
281
282 /** The last focusable tip displayed. */
283 private FocusableTip focusableTip;
284
285 /** Cached desktop anti-aliasing hints, if anti-aliasing is enabled. */
286 private Map aaHints;
287
288private int lineHeight; // Height of a line of text; same for default, bold & italic.
289private int maxAscent;
290private boolean fractionalFontMetricsEnabled;
291
292
293 /**
294 * Constructor.
295 */
296 public RSyntaxTextArea() {
297 init();
298 }
299
300
301 /**
302 * Constructor.
303 *
304 * @param doc The document for the editor.
305 */
306 public RSyntaxTextArea(RSyntaxDocument doc) {
307 super(doc);
308 init();
309 }
310
311 /**
312 * Constructor.
313 *
314 * @param text The initial text to display.
315 */
316 public RSyntaxTextArea(String text) {
317 super(text);
318 init();
319 }
320
321
322 /**
323 * Constructor.
324 *
325 * @param rows The number of rows to display.
326 * @param cols The number of columns to display.
327 * @throws IllegalArgumentException If either <code>rows</code> or
328 * <code>cols</code> is negative.
329 */
330 public RSyntaxTextArea(int rows, int cols) {
331 super(rows, cols);
332 init();
333 }
334
335
336 /**
337 * Constructor.
338 *
339 * @param text The initial text to display.
340 * @param rows The number of rows to display.
341 * @param cols The number of columns to display.
342 * @throws IllegalArgumentException If either <code>rows</code> or
343 * <code>cols</code> is negative.
344 */
345 public RSyntaxTextArea(String text, int rows, int cols) {
346 super(text, rows, cols);
347 init();
348 }
349
350
351 /**
352 * Constructor.
353 *
354 * @param doc The document for the editor.
355 * @param text The initial text to display.
356 * @param rows The number of rows to display.
357 * @param cols The number of columns to display.
358 * @throws IllegalArgumentException If either <code>rows</code> or
359 * <code>cols</code> is negative.
360 */
361 public RSyntaxTextArea(RSyntaxDocument doc, String text,int rows,int cols) {
362 super(doc, text, rows, cols);
363 init();
364 }
365
366
367 /**
368 * Creates a new <code>RSyntaxTextArea</code>.
369 *
370 * @param textMode Either <code>INSERT_MODE</code> or
371 * <code>OVERWRITE_MODE</code>.
372 */
373 public RSyntaxTextArea(int textMode) {
374 super(textMode);
375 init();
376 }
377
378
379 /**
380 * Adds an "active line range" listener to this text area.
381 *
382 * @param l The listener to add.
383 * @see #removeActiveLineRangeListener(ActiveLineRangeListener)
384 */
385 public void addActiveLineRangeListener(ActiveLineRangeListener l) {
386 listenerList.add(ActiveLineRangeListener.class, l);
387 }
388
389
390 /**
391 * Adds a hyperlink listener to this text area.
392 *
393 * @param l The listener to add.
394 * @see #removeHyperlinkListener(HyperlinkListener)
395 */
396 public void addHyperlinkListener(HyperlinkListener l) {
397 listenerList.add(HyperlinkListener.class, l);
398 }
399
400
401 /**
402 * Updates the font metrics the first time we're displayed.
403 */
404 public void addNotify() {
405
406 super.addNotify();
407
408 // We know we've just been connected to a screen resource (by
409 // definition), so initialize our font metrics objects.
410 refreshFontMetrics(getGraphics2D(getGraphics()));
411
412 // Re-start parsing if we were removed from one container and added
413 // to another
414 if (parserManager!=null) {
415 parserManager.restartParsing();
416 }
417
418 }
419
420
421 /**
422 * Adds the parser to "validate" the source code in this text area. This
423 * can be anything from a spell checker to a "compiler" that verifies
424 * source code.
425 *
426 * @param parser The new parser. A value of <code>null</code> will
427 * do nothing.
428 * @see #getParser(int)
429 * @see #getParserCount()
430 * @see #removeParser(Parser)
431 */
432 public void addParser(Parser parser) {
433 if (parserManager==null) {
434 parserManager = new ParserManager(this);
435 }
436 parserManager.addParser(parser);
437 }
438
439
440 /**
441 * Recalculates the height of a line in this text area and the
442 * maximum ascent of all fonts displayed.
443 */
444 private void calculateLineHeight() {
445
446 lineHeight = maxAscent = 0;
447
448 // Each token style.
449 for (int i=0; i<syntaxScheme.getStyleCount(); i++) {
450 Style ss = syntaxScheme.getStyle(i);
451 if (ss!=null && ss.font!=null) {
452 FontMetrics fm = getFontMetrics(ss.font);
453 int height = fm.getHeight();
454 if (height>lineHeight)
455 lineHeight = height;
456 int ascent = fm.getMaxAscent();
457 if (ascent>maxAscent)
458 maxAscent = ascent;
459 }
460 }
461
462 // The text area's (default) font).
463 Font temp = getFont();
464 FontMetrics fm = getFontMetrics(temp);
465 int height = fm.getHeight();
466 if (height>lineHeight) {
467 lineHeight = height;
468 }
469 int ascent = fm.getMaxAscent();
470 if (ascent>maxAscent) {
471 maxAscent = ascent;
472 }
473
474 }
475
476
477 /**
478 * Removes all parsers from this text area.
479 *
480 * @see #removeParser(Parser)
481 */
482 public void clearParsers() {
483 if (parserManager!=null) {
484 parserManager.clearParsers();
485 }
486 }
487
488
489 /**
490 * Clones a token list. This is necessary as tokens are reused in
491 * {@link RSyntaxDocument}, so we can't simply use the ones we
492 * are handed from it.
493 *
494 * @param t The token list to clone.
495 * @return The clone of the token list.
496 */
497 private Token cloneTokenList(Token t) {
498
499 if (t==null) {
500 return null;
501 }
502
503 Token clone = new DefaultToken();
504 clone.copyFrom(t);
505 Token cloneEnd = clone;
506
507 while ((t=t.getNextToken())!=null) {
508 Token temp = new DefaultToken();
509 temp.copyFrom(t);
510 cloneEnd.setNextToken(temp);
511 cloneEnd = temp;
512 }
513
514 return clone;
515
516 }
517
518
519 /**
520 * Overridden to toggle the enabled state of various
521 * RSyntaxTextArea-specific menu items.
522 *
523 * If you set the popup menu via {@link #setPopupMenu(JPopupMenu)}, you
524 * will want to override this method, especially if you removed any of the
525 * menu items in the default popup menu.
526 *
527 * @param popupMenu The popup menu. This will never be <code>null</code>.
528 * @see #createPopupMenu()
529 * @see #setPopupMenu(JPopupMenu)
530 */
531 protected void configurePopupMenu(JPopupMenu popupMenu) {
532
533 super.configurePopupMenu(popupMenu); // Currently does nothing
534
535 // Be nice and let them set the popup to null without overriding
536 // this method
537 if (popupMenu!=null && popupMenu.getComponentCount()>0) {
538 Component c = popupMenu.getComponent(popupMenu.getComponentCount()-1);
539 if (c instanceof JMenu) { // Assume it's the folding menu
540 JMenu foldingMenu = (JMenu)c;
541 foldingMenu.setEnabled(foldManager.
542 isCodeFoldingSupportedAndEnabled());
543 }
544 }
545 }
546
547
548 /**
549 * Copies the currently selected text to the system clipboard, with
550 * any necessary style information (font, foreground color and background
551 * color). Does nothing for <code>null</code> selections.
552 */
553 public void copyAsRtf() {
554
555 int selStart = getSelectionStart();
556 int selEnd = getSelectionEnd();
557 if (selStart==selEnd) {
558 return;
559 }
560
561 // Make sure there is a system clipboard, and that we can write
562 // to it.
563 SecurityManager sm = System.getSecurityManager();
564 if (sm!=null) {
565 try {
566 sm.checkSystemClipboardAccess();
567 } catch (SecurityException se) {
568 UIManager.getLookAndFeel().provideErrorFeedback(null);
569 return;
570 }
571 }
572 Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
573
574 // Create the RTF selection.
575 RtfGenerator gen = getRTFGenerator();
576 Token tokenList = getTokenListFor(selStart, selEnd);
577 for (Token t=tokenList; t!=null; t=t.getNextToken()) {
578 if (t.isPaintable()) {
579 if (t.textCount==1 && t.text[t.textOffset]=='\n') {
580 gen.appendNewline();
581 }
582 else {
583 Font font = getFontForTokenType(t.type);
584 Color bg = getBackgroundForTokenType(t.type);
585 boolean underline = getUnderlineForToken(t);
586 // Small optimization - don't print fg color if this
587 // is a whitespace color. Saves on RTF size.
588 if (t.isWhitespace()) {
589 gen.appendToDocNoFG(t.getLexeme(), font, bg, underline);
590 }
591 else {
592 Color fg = getForegroundForToken(t);
593 gen.appendToDoc(t.getLexeme(), font, fg, bg, underline);
594 }
595 }
596 }
597 }
598
599 // Set the system clipboard contents to the RTF selection.
600 RtfTransferable contents = new RtfTransferable(gen.getRtf().getBytes());
601 //System.out.println("*** " + new String(gen.getRtf().getBytes()));
602 try {
603 cb.setContents(contents, null);
604 } catch (IllegalStateException ise) {
605 UIManager.getLookAndFeel().provideErrorFeedback(null);
606 return;
607 }
608
609 }
610
611
612 /**
613 * Returns the document to use for an <code>RSyntaxTextArea</code>
614 *
615 * @return The document.
616 */
617 protected Document createDefaultModel() {
618 return new RSyntaxDocument(SYNTAX_STYLE_NONE);
619 }
620
621
622 /**
623 * Returns the caret event/mouse listener for <code>RTextArea</code>s.
624 *
625 * @return The caret event/mouse listener.
626 */
627 protected RTAMouseListener createMouseListener() {
628 return new RSyntaxTextAreaMutableCaretEvent(this);
629 }
630
631
632 /**
633 * Overridden to add menu items related to cold folding.
634 *
635 * @return The popup menu.
636 */
637 protected JPopupMenu createPopupMenu() {
638
639 JPopupMenu popup = super.createPopupMenu();
640 popup.addSeparator();
641
642 ResourceBundle bundle = ResourceBundle.getBundle(MSG);
643 foldingMenu = new JMenu(bundle.getString("ContextMenu.Folding"));
644 foldingMenu.add(createPopupMenuItem(toggleCurrentFoldAction));
645 foldingMenu.add(createPopupMenuItem(collapseAllCommentFoldsAction));
646 foldingMenu.add(createPopupMenuItem(collapseAllFoldsAction));
647 foldingMenu.add(createPopupMenuItem(expandAllFoldsAction));
648 popup.add(foldingMenu);
649
650 return popup;
651
652 }
653
654
655 /**
656 * See createPopupMenuActions() in RTextArea.
657 * TODO: Remove these horrible hacks and move localizing of actions into
658 * the editor kits, where it should be! The context menu should contain
659 * actions from the editor kits.
660 */
661 private static void createRstaPopupMenuActions() {
662
663 ResourceBundle msg = ResourceBundle.getBundle(MSG);
664
665 toggleCurrentFoldAction = new RSyntaxTextAreaEditorKit.
666 ToggleCurrentFoldAction();
667 toggleCurrentFoldAction.setProperties(msg, "Action.ToggleCurrentFold");
668
669 collapseAllCommentFoldsAction = new RSyntaxTextAreaEditorKit.
670 CollapseAllCommentFoldsAction();
671 collapseAllCommentFoldsAction.setProperties(msg, "Action.CollapseCommentFolds");
672
673 collapseAllFoldsAction = new RSyntaxTextAreaEditorKit.CollapseAllFoldsAction(true);
674 expandAllFoldsAction = new RSyntaxTextAreaEditorKit.ExpandAllFoldsAction(true);
675
676 }
677
678
679 /**
680 * Returns the a real UI to install on this text area.
681 *
682 * @return The UI.
683 */
684 protected RTextAreaUI createRTextAreaUI() {
685 return new RSyntaxTextAreaUI(this);
686 }
687
688
689 /**
690 * If the caret is on a bracket, this method finds the matching bracket,
691 * and if it exists, highlights it.
692 */
693 protected final void doBracketMatching() {
694
695 // We always need to repaint the "matched bracket" highlight if it
696 // exists.
697 if (match!=null) {
698 repaint(match);
699 }
700
701 // If a matching bracket is found, get its bounds and paint it!
702 int pos = RSyntaxUtilities.getMatchingBracketPosition(this);
703 if (pos>-1 && pos!=lastBracketMatchPos) {
704 try {
705 match = modelToView(pos);
706 if (match!=null) { // Happens if we're not yet visible
707 if (getAnimateBracketMatching()) {
708 bracketRepaintTimer.restart();
709 }
710 repaint(match);
711 }
712 } catch (BadLocationException ble) {
713 ble.printStackTrace(); // Shouldn't happen.
714 }
715 }
716 else if (pos==-1) {
717 // Set match to null so the old value isn't still repainted.
718 match = null;
719 bracketRepaintTimer.stop();
720 }
721 lastBracketMatchPos = pos;
722
723 }
724
725
726 /**
727 * Notifies all listeners that a caret change has occurred.
728 *
729 * @param e The caret event.
730 */
731 protected void fireCaretUpdate(CaretEvent e) {
732 super.fireCaretUpdate(e);
733 if (isBracketMatchingEnabled()) {
734 doBracketMatching();
735 }
736 }
737
738
739 /**
740 * Notifies all listeners that the active line range has changed.
741 *
742 * @param min The minimum "active" line, or <code>-1</code>.
743 * @param max The maximum "active" line, or <code>-1</code>.
744 */
745 private void fireActiveLineRangeEvent(int min, int max) {
746 ActiveLineRangeEvent e = null; // Lazily created
747 // Guaranteed to return a non-null array
748 Object[] listeners = listenerList.getListenerList();
749 // Process the listeners last to first, notifying
750 // those that are interested in this event
751 for (int i = listeners.length-2; i>=0; i-=2) {
752 if (listeners[i]==ActiveLineRangeListener.class) {
753 if (e==null) {
754 e = new ActiveLineRangeEvent(this, min, max);
755 }
756 ((ActiveLineRangeListener)listeners[i+1]).activeLineRangeChanged(e);
757 }
758 }
759 }
760
761
762 /**
763 * Notifies all listeners that have registered interest for notification
764 * on this event type. The listener list is processed last to first.
765 *
766 * @param e The event to fire.
767 * @see EventListenerList
768 */
769 private void fireHyperlinkUpdate(HyperlinkEvent e) {
770 // Guaranteed to return a non-null array
771 Object[] listeners = listenerList.getListenerList();
772 // Process the listeners last to first, notifying
773 // those that are interested in this event
774 for (int i = listeners.length-2; i>=0; i-=2) {
775 if (listeners[i]==HyperlinkListener.class) {
776 ((HyperlinkListener)listeners[i+1]).hyperlinkUpdate(e);
777 }
778 }
779 }
780
781
782 /**
783 * Notifies listeners that the marked occurrences for this text area
784 * have changed.
785 */
786 void fireMarkedOccurrencesChanged() {
787 firePropertyChange(RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY,
788 null, null);
789 }
790
791
792 /**
793 * Fires a notification that the parser notices for this text area have
794 * changed.
795 */
796 void fireParserNoticesChange() {
797 firePropertyChange(PARSER_NOTICES_PROPERTY, null, null);
798 }
799
800
801 /**
802 * Called whenever a fold is collapsed or expanded. This causes the
803 * text editor to revalidate. This method is here because of poor design
804 * and should be removed.
805 *
806 * @param fold The fold that was collapsed or expanded.
807 */
808 public void foldToggled(Fold fold) {
809 match = null; // TODO: Update the bracket rect rather than hide it
810 possiblyUpdateCurrentLineHighlightLocation();
811 revalidate();
812 repaint();
813 }
814
815
816 /**
817 * Forces the given {@link Parser} to re-parse the content of this text
818 * area.<p>
819 *
820 * This method can be useful when a <code>Parser</code> can be configured
821 * as to what notices it returns. For example, if a Java language parser
822 * can be configured to set whether no serialVersionUID is a warning,
823 * error, or ignored, this method can be called after changing the expected
824 * notice type to have the document re-parsed.
825 *
826 * @param parser The index of the <code>Parser</code> to re-run.
827 * @see #getParser(int)
828 */
829 public void forceReparsing(int parser) {
830 parserManager.forceReparsing(parser);
831 }
832
833
834 /**
835 * Forces re-parsing with a specific parser. Note that if this parser is
836 * not installed on this text area, nothing will happen.
837 *
838 * @param parser The parser that should re-parse this text area's contents.
839 * This should be installed on this text area.
840 * @return Whether the parser was installed on this text area.
841 * @see #forceReparsing(int)
842 */
843 public boolean forceReparsing(Parser parser) {
844 for (int i=0; i<getParserCount(); i++) {
845 if (getParser(i)==parser) {
846 forceReparsing(i);
847 return true;
848 }
849 }
850 return false;
851 }
852
853
854 /**
855 * Returns whether bracket matching should be animated.
856 *
857 * @return Whether bracket matching should be animated.
858 * @see #setAnimateBracketMatching(boolean)
859 */
860 public boolean getAnimateBracketMatching() {
861 return animateBracketMatching;
862 }
863
864
865 /**
866 * Returns whether anti-aliasing is enabled in this editor.
867 *
868 * @return Whether anti-aliasing is enabled in this editor.
869 * @see #setAntiAliasingEnabled(boolean)
870 * @see #getFractionalFontMetricsEnabled()
871 */
872 public boolean getAntiAliasingEnabled() {
873 return aaHints!=null;
874 }
875
876
877 /**
878 * Returns the background color for tokens of the specified type.
879 *
880 * @param type The type of token.
881 * @return The background color to use for that token type. If
882 * this value is <code>null</code> then this token type
883 * has no special background color.
884 * @see #getForegroundForToken(Token)
885 */
886 public Color getBackgroundForTokenType(int type) {
887 // Don't default to this.getBackground(), as Tokens simply don't
888 // paint a background if they get a null Color.
889 return syntaxScheme.getStyle(type).background;
890 }
891
892
893 /**
894 * Returns whether curly braces should be automatically closed when a
895 * newline is entered after an opening curly brace. Note that this
896 * property is only honored for languages that use curly braces to denote
897 * code blocks.
898 *
899 * @return Whether curly braces should be automatically closed.
900 * @see #setCloseCurlyBraces(boolean)
901 */
902 public boolean getCloseCurlyBraces() {
903 return closeCurlyBraces;
904 }
905
906
907 /**
908 * Returns whether closing markup tags should be automatically completed
909 * when "<code>&lt;/</code>" is typed. Note that this property is only
910 * honored for markup languages, such as HTML, XML and PHP.
911 *
912 * @return Whether closing markup tags should be automatically completed.
913 * @see #setCloseMarkupTags(boolean)
914 */
915 public boolean getCloseMarkupTags() {
916 return closeMarkupTags;
917 }
918
919
920 /**
921 * Returns the code template manager for all instances of
922 * <code>RSyntaxTextArea</code>. The manager is lazily created.
923 *
924 * @return The code template manager.
925 * @see #setTemplatesEnabled(boolean)
926 */
927 public static synchronized CodeTemplateManager getCodeTemplateManager() {
928 if (codeTemplateManager==null) {
929 codeTemplateManager = new CodeTemplateManager();
930 }
931 return codeTemplateManager;
932 }
933
934
935 /**
936 * Returns the default bracket-match background color.
937 *
938 * @return The color.
939 * @see #getDefaultBracketMatchBorderColor
940 */
941 public static final Color getDefaultBracketMatchBGColor() {
942 return DEFAULT_BRACKET_MATCH_BG_COLOR;
943 }
944
945
946 /**
947 * Returns the default bracket-match border color.
948 *
949 * @return The color.
950 * @see #getDefaultBracketMatchBGColor
951 */
952 public static final Color getDefaultBracketMatchBorderColor() {
953 return DEFAULT_BRACKET_MATCH_BORDER_COLOR;
954 }
955
956
957 /**
958 * Returns the default selection color for this text area. This
959 * color was chosen because it's light and <code>RSyntaxTextArea</code>
960 * does not change text color between selected/unselected text for
961 * contrast like regular <code>JTextArea</code>s do.
962 *
963 * @return The default selection color.
964 */
965 public static Color getDefaultSelectionColor() {
966 return DEFAULT_SELECTION_COLOR;
967 }
968
969
970 /**
971 * Returns the "default" syntax highlighting color scheme. The colors
972 * used are somewhat standard among syntax highlighting text editors.
973 *
974 * @return The default syntax highlighting color scheme.
975 * @see #restoreDefaultSyntaxScheme()
976 * @see #getSyntaxScheme()
977 * @see #setSyntaxScheme(SyntaxScheme)
978 */
979 public SyntaxScheme getDefaultSyntaxScheme() {
980 return new SyntaxScheme(getFont());
981 }
982
983
984 /**
985 * Returns whether an EOL marker should be drawn at the end of each line.
986 *
987 * @return Whether EOL markers should be visible.
988 * @see #setEOLMarkersVisible(boolean)
989 * @see #isWhitespaceVisible()
990 */
991 public boolean getEOLMarkersVisible() {
992 return eolMarkersVisible;
993 }
994
995
996 /**
997 * Returns the fold manager for this text area.
998 *
999 * @return The fold manager.
1000 */
1001 public FoldManager getFoldManager() {
1002 return foldManager;
1003 }
1004
1005
1006 /**
1007 * Returns the font for tokens of the specified type.
1008 *
1009 * @param type The type of token.
1010 * @return The font to use for that token type.
1011 * @see #getFontMetricsForTokenType(int)
1012 */
1013 public Font getFontForTokenType(int type) {
1014 Font f = syntaxScheme.getStyle(type).font;
1015 return f!=null ? f : getFont();
1016 }
1017
1018
1019 /**
1020 * Returns the font metrics for tokens of the specified type.
1021 *
1022 * @param type The type of token.
1023 * @return The font metrics to use for that token type.
1024 * @see #getFontForTokenType(int)
1025 */
1026 public FontMetrics getFontMetricsForTokenType(int type) {
1027 FontMetrics fm = syntaxScheme.getStyle(type).fontMetrics;
1028 return fm!=null ? fm : defaultFontMetrics;
1029 }
1030
1031
1032 /**
1033 * Returns the foreground color to use when painting a token.
1034 *
1035 * @param t The token.
1036 * @return The foreground color to use for that token. This
1037 * value is never <code>null</code>.
1038 * @see #getBackgroundForTokenType(int)
1039 */
1040 public Color getForegroundForToken(Token t) {
1041 if (getHyperlinksEnabled() && t.isHyperlink() &&
1042 hoveredOverLinkOffset==t.offset) {
1043 return hyperlinkFG;
1044 }
1045 return getForegroundForTokenType(t.type);
1046 }
1047
1048
1049 /**
1050 * Returns the foreground color to use when painting a token. This does
1051 * not take into account whether the token is a hyperlink.
1052 *
1053 * @param type The token type.
1054 * @return The foreground color to use for that token. This
1055 * value is never <code>null</code>.
1056 * @see #getForegroundForToken(Token)
1057 */
1058 public Color getForegroundForTokenType(int type) {
1059 Color fg = syntaxScheme.getStyle(type).foreground;
1060 return fg!=null ? fg : getForeground();
1061 }
1062
1063
1064 /**
1065 * Returns whether fractional font metrics are enabled for this text area.
1066 *
1067 * @return Whether fractional font metrics are enabled.
1068 * @see #setFractionalFontMetricsEnabled
1069 * @see #getAntiAliasingEnabled()
1070 */
1071 public boolean getFractionalFontMetricsEnabled() {
1072 return fractionalFontMetricsEnabled;
1073 }
1074
1075
1076 /**
1077 * Returns a <code>Graphics2D</code> version of the specified graphics
1078 * that has been initialized with the proper rendering hints.
1079 *
1080 * @param g The graphics context for which to get a
1081 * <code>Graphics2D</code>.
1082 * @return The <code>Graphics2D</code>.
1083 */
1084 private final Graphics2D getGraphics2D(Graphics g) {
1085 Graphics2D g2d = (Graphics2D)g;
1086 if (aaHints!=null) {
1087 g2d.addRenderingHints(aaHints);
1088 }
1089 if (fractionalFontMetricsEnabled) {
1090 g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
1091 RenderingHints.VALUE_FRACTIONALMETRICS_ON);
1092 }
1093 return g2d;
1094 }
1095
1096
1097 /**
1098 * Returns the color to use when painting hyperlinks.
1099 *
1100 * @return The color to use when painting hyperlinks.
1101 * @see #setHyperlinkForeground(Color)
1102 * @see #getHyperlinksEnabled()
1103 */
1104 public Color getHyperlinkForeground() {
1105 return hyperlinkFG;
1106 }
1107
1108
1109 /**
1110 * Returns whether hyperlinks are enabled for this text area.
1111 *
1112 * @return Whether hyperlinks are enabled for this text area.
1113 * @see #setHyperlinksEnabled(boolean)
1114 */
1115 public boolean getHyperlinksEnabled() {
1116 return hyperlinksEnabled;
1117 }
1118
1119
1120 /**
1121 * Returns the last visible offset in this text area. This may not be the
1122 * length of the document if code folding is enabled.
1123 *
1124 * @return The last visible offset in this text area.
1125 */
1126 public int getLastVisibleOffset() {
1127 if (isCodeFoldingEnabled()) {
1128 int lastVisibleLine = foldManager.getLastVisibleLine();
1129 if (lastVisibleLine<getLineCount()-1) { // Not the last line
1130 try {
1131 return getLineEndOffset(lastVisibleLine) - 1;
1132 } catch (BadLocationException ble) { // Never happens
1133 ble.printStackTrace();
1134 }
1135 }
1136 }
1137 return getDocument().getLength();
1138 }
1139
1140
1141 /**
1142 * Returns the height to use for a line of text in this text area.
1143 *
1144 * @return The height of a line of text in this text area.
1145 */
1146 public int getLineHeight() {
1147 //System.err.println("... getLineHeight() returning " + lineHeight);
1148 return lineHeight;
1149 }
1150
1151
1152 /**
1153 * Returns a list of "marked occurrences" in the text area. If there are
1154 * no marked occurrences, this will be an empty list.
1155 *
1156 * @return The list of marked occurrences.
1157 */
1158 public List getMarkedOccurrences() {
1159 return ((RSyntaxTextAreaHighlighter)getHighlighter()).
1160 getMarkedOccurrences();
1161 }
1162
1163
1164 /**
1165 * Returns whether "Mark Occurrences" is enabled.
1166 *
1167 * @return Whether "Mark Occurrences" is enabled.
1168 * @see #setMarkOccurrences(boolean)
1169 */
1170 public boolean getMarkOccurrences() {
1171 return markOccurrencesSupport!=null;
1172 }
1173
1174
1175 /**
1176 * Returns the color used to "mark occurrences."
1177 *
1178 * @return The mark occurrences color.
1179 * @see #setMarkOccurrencesColor(Color)
1180 */
1181 public Color getMarkOccurrencesColor() {
1182 return markOccurrencesColor;
1183 }
1184
1185
1186 /**
1187 * Returns whether tokens of the specified type should have "mark
1188 * occurrences" enabled for the current programming language.
1189 *
1190 * @param type The token type.
1191 * @return Whether tokens of this type should have "mark occurrences"
1192 * enabled.
1193 */
1194 boolean getMarkOccurrencesOfTokenType(int type) {
1195 RSyntaxDocument doc = (RSyntaxDocument)getDocument();
1196 return doc.getMarkOccurrencesOfTokenType(type);
1197 }
1198
1199
1200 /**
1201 * Gets the color used as the background for a matched bracket.
1202 *
1203 * @return The color used. If this is <code>null</code>, no special
1204 * background is painted behind a matched bracket.
1205 * @see #setMatchedBracketBGColor
1206 * @see #getMatchedBracketBorderColor
1207 */
1208 public Color getMatchedBracketBGColor() {
1209 return matchedBracketBGColor;
1210 }
1211
1212
1213 /**
1214 * Gets the color used as the border for a matched bracket.
1215 *
1216 * @return The color used.
1217 * @see #setMatchedBracketBorderColor
1218 * @see #getMatchedBracketBGColor
1219 */
1220 public Color getMatchedBracketBorderColor() {
1221 return matchedBracketBorderColor;
1222 }
1223
1224
1225 /**
1226 * Returns the matched bracket's rectangle, or <code>null</code> if there
1227 * is currently no matched bracket. Note that this shouldn't ever be
1228 * called by the user.
1229 *
1230 * @return The rectangle surrounding the matched bracket.
1231 */
1232 public final Rectangle getMatchRectangle() {
1233 return match;
1234 }
1235
1236
1237 /**
1238 * Overridden to return the max ascent for any font used in the editor.
1239 *
1240 * @return The max ascent value.
1241 */
1242 public int getMaxAscent() {
1243 return maxAscent;
1244 }
1245
1246
1247 /**
1248 * Returns whether tab lines are painted.
1249 *
1250 * @return Whether tab lines are painted.
1251 * @see #setPaintTabLines(boolean)
1252 * @see #getTabLineColor()
1253 */
1254 public boolean getPaintTabLines() {
1255 return paintTabLines;
1256 }
1257
1258
1259 /**
1260 * Returns the specified parser.
1261 *
1262 * @param index The {@link Parser} to retrieve.
1263 * @return The <code>Parser</code>.
1264 * @see #getParserCount()
1265 * @see #addParser(Parser)
1266 */
1267 public Parser getParser(int index) {
1268 return parserManager.getParser(index);
1269 }
1270
1271
1272 /**
1273 * Returns the number of parsers operating on this text area.
1274 *
1275 * @return The parser count.
1276 * @see #addParser(Parser)
1277 */
1278 public int getParserCount() {
1279 return parserManager==null ? 0 : parserManager.getParserCount();
1280 }
1281
1282
1283 /**
1284 * Returns a list of the current parser notices for this text area.
1285 * This method (like most Swing methods) should only be called on the
1286 * EDT.
1287 *
1288 * @return The list of notices. This will be an empty list if there are
1289 * none.
1290 */
1291 public List getParserNotices() {
1292 return parserManager==null ? Collections.EMPTY_LIST :
1293 parserManager.getParserNotices();
1294 }
1295
1296
1297 /**
1298 * Returns the RTF generator for this text area, lazily creating it
1299 * if necessary.
1300 *
1301 * @return The RTF generator.
1302 */
1303 private RtfGenerator getRTFGenerator() {
1304 if (rtfGenerator==null) {
1305 rtfGenerator = new RtfGenerator();
1306 }
1307 else {
1308 rtfGenerator.reset();
1309 }
1310 return rtfGenerator;
1311 }
1312
1313
1314 /**
1315 * If auto-indent is enabled, this method returns whether a new line after
1316 * this one should be indented (based on the standard indentation rules for
1317 * the current programming language). For example, in Java, for a line
1318 * containing:
1319 *
1320 * <pre>
1321 * for (int i=0; i<10; i++) {
1322 * </pre>
1323 *
1324 * the following line should be indented.
1325 *
1326 * @param line The line to check.
1327 * @return Whether a line inserted after this one should be auto-indented.
1328 * If auto-indentation is disabled, this will always return
1329 * <code>false</code>.
1330 * @see #isAutoIndentEnabled()
1331 */
1332 public boolean getShouldIndentNextLine(int line) {
1333 if (isAutoIndentEnabled()) {
1334 RSyntaxDocument doc = (RSyntaxDocument)getDocument();
1335 return doc.getShouldIndentNextLine(line);
1336 }
1337 return false;
1338 }
1339
1340
1341 /**
1342 * Returns what type of syntax highlighting this editor is doing.
1343 *
1344 * @return The style being used, such as
1345 * {@link SyntaxConstants#SYNTAX_STYLE_JAVA}.
1346 * @see #setSyntaxEditingStyle(String)
1347 * @see SyntaxConstants
1348 */
1349 public String getSyntaxEditingStyle() {
1350 return syntaxStyleKey;
1351 }
1352
1353
1354 /**
1355 * Returns all of the colors currently being used in syntax highlighting
1356 * by this text component.
1357 *
1358 * @return An instance of <code>SyntaxScheme</code> that represents
1359 * the colors currently being used for syntax highlighting.
1360 * @see #setSyntaxScheme(SyntaxScheme)
1361 */
1362 public SyntaxScheme getSyntaxScheme() {
1363 return syntaxScheme;
1364 }
1365
1366
1367 /**
1368 * Returns the color used to paint tab lines.
1369 *
1370 * @return The color used to paint tab lines.
1371 * @see #setTabLineColor(Color)
1372 * @see #getPaintTabLines()
1373 * @see #setPaintTabLines(boolean)
1374 */
1375 public Color getTabLineColor() {
1376 return tabLineColor;
1377 }
1378
1379
1380 /**
1381 * Returns whether a border is painted around marked occurrences.
1382 *
1383 * @return Whether a border is painted.
1384 * @see #setPaintMarkOccurrencesBorder(boolean)
1385 * @see #getMarkOccurrencesColor()
1386 * @see #getMarkOccurrences()
1387 */
1388 public boolean getPaintMarkOccurrencesBorder() {
1389 return paintMarkOccurrencesBorder;
1390 }
1391
1392
1393 /**
1394 * Returns whether or not templates are enabled for all instances
1395 * of <code>RSyntaxTextArea</code>.
1396 *
1397 * @return Whether templates are enabled.
1398 * @see #saveTemplates()
1399 * @see #setTemplateDirectory(String)
1400 * @see #setTemplatesEnabled(boolean)
1401 */
1402 public static synchronized boolean getTemplatesEnabled() {
1403 return templatesEnabled;
1404 }
1405
1406
1407 /**
1408 * Returns a token list for the given range in the document.
1409 *
1410 * @param startOffs The starting offset in the document.
1411 * @param endOffs The end offset in the document.
1412 * @return The first token in the token list.
1413 */
1414 private Token getTokenListFor(int startOffs, int endOffs) {
1415
1416 Token tokenList = null;
1417 Token lastToken = null;
1418
1419 Element map = getDocument().getDefaultRootElement();
1420 int startLine = map.getElementIndex(startOffs);
1421 int endLine = map.getElementIndex(endOffs);
1422
1423 for (int line=startLine; line<=endLine; line++) {
1424 Token t = getTokenListForLine(line);
1425 t = cloneTokenList(t);
1426 if (tokenList==null) {
1427 tokenList = t;
1428 lastToken = tokenList;
1429 }
1430 else {
1431 lastToken.setNextToken(t);
1432 }
1433 while (lastToken.getNextToken()!=null &&
1434 lastToken.getNextToken().isPaintable()) {
1435 lastToken = lastToken.getNextToken();
1436 }
1437 if (line<endLine) {
1438 // Document offset MUST be correct to prevent exceptions
1439 // in getTokenListFor()
1440 int docOffs = map.getElement(line).getEndOffset()-1;
1441 t = new DefaultToken(new char[] { '\n' }, 0,0, docOffs,
1442 Token.WHITESPACE);
1443 lastToken.setNextToken(t);
1444 lastToken = t;
1445 }
1446 }
1447
1448 // Trim the beginning and end of the token list so that it starts
1449 // at startOffs and ends at endOffs.
1450
1451 // Be careful and check that startOffs is actually in the list.
1452 // startOffs can be < the token list's start if the end "newline"
1453 // character of a line is the first character selected (the token
1454 // list returned for that line will be null, so the first token in
1455 // the final token list will be from the next line and have a
1456 // starting offset > startOffs?).
1457 if (startOffs>=tokenList.offset) {
1458 while (!tokenList.containsPosition(startOffs)) {
1459 tokenList = tokenList.getNextToken();
1460 }
1461 tokenList.makeStartAt(startOffs);
1462 }
1463
1464 Token temp = tokenList;
1465 // Be careful to check temp for null here. It is possible that no
1466 // token contains endOffs, if endOffs is at the end of a line.
1467 while (temp!=null && !temp.containsPosition(endOffs)) {
1468 temp = temp.getNextToken();
1469 }
1470 if (temp!=null) {
1471 temp.textCount = endOffs - temp.offset;
1472 temp.setNextToken(null);
1473 }
1474
1475 return tokenList;
1476
1477 }
1478
1479
1480 /**
1481 * Returns a list of tokens representing the given line.
1482 *
1483 * @param line The line number to get tokens for.
1484 * @return A linked list of tokens representing the line's text.
1485 */
1486 public Token getTokenListForLine(int line) {
1487 return ((RSyntaxDocument)getDocument()).getTokenListForLine(line);
1488 }
1489
1490
1491 /**
1492 * Returns the tool tip to display for a mouse event at the given
1493 * location. This method is overridden to give a registered parser a
1494 * chance to display a tool tip (such as an error description when the
1495 * mouse is over an error highlight).
1496 *
1497 * @param e The mouse event.
1498 */
1499 public String getToolTipText(MouseEvent e) {
1500
1501 // Check parsers for tool tips first.
1502 String text = null;
1503 URL imageBase = null;
1504 if (parserManager!=null) {
1505 ToolTipInfo info = parserManager.getToolTipText(e);
1506 if (info!=null) { // Should always be true
1507 text = info.getToolTipText(); // May be null
1508 imageBase = info.getImageBase(); // May be null
1509 }
1510 }
1511 if (text==null) {
1512 text = super.getToolTipText(e);
1513 }
1514
1515 // Do we want to use "focusable" tips?
1516 if (getUseFocusableTips()) {
1517 if (text!=null) {
1518 if (focusableTip==null) {
1519 focusableTip = new FocusableTip(this, parserManager);
1520 }
1521 focusableTip.setImageBase(imageBase);
1522 focusableTip.toolTipRequested(e, text);
1523 }
1524 // No tool tip text at new location - hide tip window if one is
1525 // currently visible
1526 else if (focusableTip!=null) {
1527 focusableTip.possiblyDisposeOfTipWindow();
1528 }
1529 return null;
1530 }
1531
1532 return text; // Standard tool tips
1533
1534 }
1535
1536
1537 /**
1538 * Returns whether the specified token should be underlined.
1539 * A token is underlined if its syntax style includes underlining,
1540 * or if it is a hyperlink and hyperlinks are enabled.
1541 *
1542 * @param t The token.
1543 * @return Whether the specified token should be underlined.
1544 */
1545 public boolean getUnderlineForToken(Token t) {
1546 return (t.isHyperlink() && getHyperlinksEnabled()) ||
1547 syntaxScheme.getStyle(t.type).underline;
1548 }
1549
1550
1551 /**
1552 * Returns whether "focusable" tool tips are used instead of standard
1553 * ones. Focusable tool tips are tool tips that the user can click on,
1554 * resize, copy from, and click links in.
1555 *
1556 * @return Whether to use focusable tool tips.
1557 * @see #setUseFocusableTips(boolean)
1558 * @see FocusableTip
1559 */
1560 public boolean getUseFocusableTips() {
1561 return useFocusableTips;
1562 }
1563
1564
1565 /**
1566 * Called by constructors to initialize common properties of the text
1567 * editor.
1568 */
1569 protected void init() {
1570
1571 // NOTE: Our actions are created here instead of in a static block
1572 // so they are only created when the first RTextArea is instantiated,
1573 // not before. There have been reports of users calling static getters
1574 // (e.g. RSyntaxTextArea.getDefaultBracketMatchBGColor()) which would
1575 // cause these actions to be created and (possibly) incorrectly
1576 // localized, if they were in a static block.
1577 if (toggleCurrentFoldAction==null) {
1578 createRstaPopupMenuActions();
1579 }
1580
1581 // Set some RSyntaxTextArea default values.
1582 syntaxStyleKey = SYNTAX_STYLE_NONE;
1583 setMatchedBracketBGColor(getDefaultBracketMatchBGColor());
1584 setMatchedBracketBorderColor(getDefaultBracketMatchBorderColor());
1585 setBracketMatchingEnabled(true);
1586 setAnimateBracketMatching(true);
1587 lastBracketMatchPos = -1;
1588 setSelectionColor(getDefaultSelectionColor());
1589 setTabLineColor(null);
1590 setMarkOccurrencesColor(MarkOccurrencesSupport.DEFAULT_COLOR);
1591
1592 foldManager = new FoldManager(this);
1593
1594 // Set auto-indent related stuff.
1595 setAutoIndentEnabled(true);
1596 setCloseCurlyBraces(true);
1597 setCloseMarkupTags(true);
1598 setClearWhitespaceLinesEnabled(true);
1599
1600 setHyperlinksEnabled(true);
1601 setLinkScanningMask(InputEvent.CTRL_DOWN_MASK);
1602 setHyperlinkForeground(Color.BLUE);
1603 isScanningForLinks = false;
1604 setUseFocusableTips(true);
1605
1606 setAntiAliasingEnabled(true);
1607 restoreDefaultSyntaxScheme();
1608
1609 }
1610
1611
1612 /**
1613 * Returns whether or not auto-indent is enabled.
1614 *
1615 * @return Whether or not auto-indent is enabled.
1616 * @see #setAutoIndentEnabled(boolean)
1617 */
1618 public boolean isAutoIndentEnabled() {
1619 return autoIndentEnabled;
1620 }
1621
1622
1623 /**
1624 * Returns whether or not bracket matching is enabled.
1625 *
1626 * @return <code>true</code> iff bracket matching is enabled.
1627 * @see #setBracketMatchingEnabled
1628 */
1629 public final boolean isBracketMatchingEnabled() {
1630 return bracketMatchingEnabled;
1631 }
1632
1633
1634 /**
1635 * Returns whether or not lines containing nothing but whitespace are made
1636 * into blank lines when Enter is pressed in them.
1637 *
1638 * @return Whether or not whitespace-only lines are cleared when
1639 * the user presses Enter on them.
1640 * @see #setClearWhitespaceLinesEnabled(boolean)
1641 */
1642 public boolean isClearWhitespaceLinesEnabled() {
1643 return clearWhitespaceLines;
1644 }
1645
1646
1647 /**
1648 * Returns whether code folding is enabled. Note that only certain
1649 * languages support code folding; those that do not will ignore this
1650 * property.
1651 *
1652 * @return Whether code folding is enabled.
1653 * @see #setCodeFoldingEnabled(boolean)
1654 */
1655 public boolean isCodeFoldingEnabled() {
1656 return foldManager.isCodeFoldingEnabled();
1657 }
1658
1659
1660 /**
1661 * Returns whether whitespace (spaces and tabs) is visible.
1662 *
1663 * @return Whether whitespace is visible.
1664 * @see #setWhitespaceVisible(boolean)
1665 * @see #getEOLMarkersVisible()
1666 */
1667 public boolean isWhitespaceVisible() {
1668 return whitespaceVisible;
1669 }
1670
1671
1672 /**
1673 * Returns the token at the specified position in the model.
1674 *
1675 * @param offs The position in the model.
1676 * @return The token, or <code>null</code> if no token is at that
1677 * position.
1678 * @see #viewToToken(Point)
1679 */
1680 private Token modelToToken(int offs) {
1681 if (offs>=0) {
1682 try {
1683 int line = getLineOfOffset(offs);
1684 Token t = getTokenListForLine(line);
1685 while (t!=null && t.isPaintable()) {
1686 if (t.containsPosition(offs)) {
1687 return t;
1688 }
1689 t = t.getNextToken();
1690 }
1691 } catch (BadLocationException ble) {
1692 ble.printStackTrace(); // Never happens
1693 }
1694 }
1695 return null;
1696 }
1697
1698
1699 /**
1700 * The <code>paintComponent</code> method is overridden so we
1701 * apply any necessary rendering hints to the Graphics object.
1702 */
1703 protected void paintComponent(Graphics g) {
1704 super.paintComponent(getGraphics2D(g));
1705 }
1706
1707
1708 private void refreshFontMetrics(Graphics2D g2d) {
1709 // It is assumed that any rendering hints are already applied to g2d.
1710 defaultFontMetrics = g2d.getFontMetrics(getFont());
1711 syntaxScheme.refreshFontMetrics(g2d);
1712 if (getLineWrap()==false) {
1713 // HORRIBLE HACK! The un-wrapped view needs to refresh its cached
1714 // longest line information.
1715 SyntaxView sv = (SyntaxView)getUI().getRootView(this).getView(0);
1716 sv.calculateLongestLine();
1717 }
1718 }
1719
1720
1721 /**
1722 * Removes an "active line range" listener from this text area.
1723 *
1724 * @param l The listener to remove.
1725 * @see #removeActiveLineRangeListener(ActiveLineRangeListener)
1726 */
1727 public void removeActiveLineRangeListener(ActiveLineRangeListener l) {
1728 listenerList.remove(ActiveLineRangeListener.class, l);
1729 }
1730
1731
1732 /**
1733 * Removes a hyperlink listener from this text area.
1734 *
1735 * @param l The listener to remove.
1736 * @see #addHyperlinkListener(HyperlinkListener)
1737 */
1738 public void removeHyperlinkListener(HyperlinkListener l) {
1739 listenerList.remove(HyperlinkListener.class, l);
1740 }
1741
1742
1743 /**
1744 * Overridden so we stop this text area's parsers, if any.
1745 */
1746 public void removeNotify() {
1747 if (parserManager!=null) {
1748 parserManager.stopParsing();
1749 }
1750 super.removeNotify();
1751 }
1752
1753
1754 /**
1755 * Removes a parser from this text area.
1756 *
1757 * @param parser The {@link Parser} to remove.
1758 * @return Whether the parser was found and removed.
1759 * @see #clearParsers()
1760 * @see #addParser(Parser)
1761 * @see #getParser(int)
1762 */
1763 public boolean removeParser(Parser parser) {
1764 boolean removed = false;
1765 if (parserManager!=null) {
1766 removed = parserManager.removeParser(parser);
1767 }
1768 return removed;
1769 }
1770
1771
1772 /**
1773 * Sets the colors used for syntax highlighting to their defaults.
1774 *
1775 * @see #setSyntaxScheme(SyntaxScheme)
1776 * @see #getSyntaxScheme()
1777 * @see #getDefaultSyntaxScheme()
1778 */
1779 public void restoreDefaultSyntaxScheme() {
1780 setSyntaxScheme(getDefaultSyntaxScheme());
1781 }
1782
1783
1784 /**
1785 * Attempts to save all currently-known templates to the current template
1786 * directory, as set by <code>setTemplateDirectory</code>. Templates
1787 * will be saved as XML files with names equal to their abbreviations; for
1788 * example, a template that expands on the word "forb" will be saved as
1789 * <code>forb.xml</code>.
1790 *
1791 * @return Whether or not the save was successful. The save will
1792 * be unsuccessful if the template directory does not exist or
1793 * if it has not been set (i.e., you have not yet called
1794 * <code>setTemplateDirectory</code>).
1795 * @see #getTemplatesEnabled
1796 * @see #setTemplateDirectory
1797 * @see #setTemplatesEnabled
1798 */
1799 public static synchronized boolean saveTemplates() {
1800 if (!getTemplatesEnabled()) {
1801 return false;
1802 }
1803 return getCodeTemplateManager().saveTemplates();
1804 }
1805
1806
1807 /**
1808 * Sets the "active line range." Note that this
1809 * <code>RSyntaxTextArea</code> itself does nothing with this information,
1810 * but if it is contained inside an {@link RTextScrollPane}, the active
1811 * line range may be displayed in the icon area of the {@link Gutter}.<p>
1812 *
1813 * Note that basic users of <code>RSyntaxTextArea</code> will not call this
1814 * method directly; rather, it is usually called by instances of
1815 * <code>LanguageSupport</code> in the <code>RSTALangaugeSupport</code>
1816 * library. See <a href="http://fifesoft.com">http://fifesoft.com</a>
1817 * for more information about this library.
1818 *
1819 * @param min The "minimum" line in the active line range, or
1820 * <code>-1</code> if the range is being cleared.
1821 * @param max The "maximum" line in the active line range, or
1822 * <code>-1</code> if the range is being cleared.
1823 * @see #addActiveLineRangeListener(ActiveLineRangeListener)
1824 */
1825 public void setActiveLineRange(int min, int max) {
1826 if (min==-1) {
1827 max = -1; // Force max to be -1 if min is.
1828 }
1829 fireActiveLineRangeEvent(min, max);
1830 }
1831
1832
1833 /**
1834 * Sets whether bracket matching should be animated. This fires a property
1835 * change event of type {@link #ANIMATE_BRACKET_MATCHING_PROPERTY}.
1836 *
1837 * @param animate Whether to animate bracket matching.
1838 * @see #getAnimateBracketMatching()
1839 */
1840 public void setAnimateBracketMatching(boolean animate) {
1841 if (animate!=animateBracketMatching) {
1842 animateBracketMatching = animate;
1843 if (animate && bracketRepaintTimer==null) {
1844 bracketRepaintTimer = new BracketMatchingTimer();
1845 }
1846 firePropertyChange(ANIMATE_BRACKET_MATCHING_PROPERTY,
1847 !animate, animate);
1848 }
1849 }
1850
1851
1852 /**
1853 * Sets whether anti-aliasing is enabled in this editor. This method
1854 * fires a property change event of type {@link #ANTIALIAS_PROPERTY}.
1855 *
1856 * @param enabled Whether anti-aliasing is enabled.
1857 * @see #getAntiAliasingEnabled()
1858 */
1859 public void setAntiAliasingEnabled(boolean enabled) {
1860
1861 boolean currentlyEnabled = aaHints!=null;
1862
1863 if (enabled!=currentlyEnabled) {
1864
1865 if (enabled) {
1866 aaHints = RSyntaxUtilities.getDesktopAntiAliasHints();
1867 // If the desktop query method comes up empty, use the standard
1868 // Java2D greyscale method. Note this will likely NOT be as
1869 // nice as what would be used if the getDesktopAntiAliasHints()
1870 // call worked.
1871 if (aaHints==null) {
1872 aaHints = new HashMap();
1873 aaHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
1874 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1875 }
1876 }
1877 else {
1878 aaHints = null;
1879 }
1880
1881 // We must be connected to a screen resource for our graphics
1882 // to be non-null.
1883 if (isDisplayable()) {
1884 refreshFontMetrics(getGraphics2D(getGraphics()));
1885 }
1886 firePropertyChange(ANTIALIAS_PROPERTY, !enabled, enabled);
1887 repaint();
1888
1889 }
1890
1891 }
1892
1893
1894 /**
1895 * Sets whether or not auto-indent is enabled. This fires a property
1896 * change event of type {@link #AUTO_INDENT_PROPERTY}.
1897 *
1898 * @param enabled Whether or not auto-indent is enabled.
1899 * @see #isAutoIndentEnabled()
1900 */
1901 public void setAutoIndentEnabled(boolean enabled) {
1902 if (autoIndentEnabled!=enabled) {
1903 autoIndentEnabled = enabled;
1904 firePropertyChange(AUTO_INDENT_PROPERTY, !enabled, enabled);
1905 }
1906 }
1907
1908
1909 /**
1910 * Sets whether bracket matching is enabled. This fires a property change
1911 * event of type {@link #BRACKET_MATCHING_PROPERTY}.
1912 *
1913 * @param enabled Whether or not bracket matching should be enabled.
1914 * @see #isBracketMatchingEnabled()
1915 */
1916 public void setBracketMatchingEnabled(boolean enabled) {
1917 if (enabled!=bracketMatchingEnabled) {
1918 bracketMatchingEnabled = enabled;
1919 repaint();
1920 firePropertyChange(BRACKET_MATCHING_PROPERTY, !enabled, enabled);
1921 }
1922 }
1923
1924
1925 /**
1926 * Sets whether or not lines containing nothing but whitespace are made
1927 * into blank lines when Enter is pressed in them. This method fires
1928 * a property change event of type {@link #CLEAR_WHITESPACE_LINES_PROPERTY}.
1929 *
1930 * @param enabled Whether or not whitespace-only lines are cleared when
1931 * the user presses Enter on them.
1932 * @see #isClearWhitespaceLinesEnabled()
1933 */
1934 public void setClearWhitespaceLinesEnabled(boolean enabled) {
1935 if (enabled!=clearWhitespaceLines) {
1936 clearWhitespaceLines = enabled;
1937 firePropertyChange(CLEAR_WHITESPACE_LINES_PROPERTY,
1938 !enabled, enabled);
1939 }
1940 }
1941
1942
1943 /**
1944 * Toggles whether curly braces should be automatically closed when a
1945 * newline is entered after an opening curly brace. Note that this
1946 * property is only honored for languages that use curly braces to denote
1947 * code blocks.<p>
1948 *
1949 * This method fires a property change event of type
1950 * {@link #CLOSE_CURLY_BRACES_PROPERTY}.
1951 *
1952 * @param close Whether curly braces should be automatically closed.
1953 * @see #getCloseCurlyBraces()
1954 */
1955 public void setCloseCurlyBraces(boolean close) {
1956 if (close!=closeCurlyBraces) {
1957 closeCurlyBraces = close;
1958 firePropertyChange(CLOSE_CURLY_BRACES_PROPERTY, !close, close);
1959 }
1960 }
1961
1962
1963 /**
1964 * Sets whether closing markup tags should be automatically completed
1965 * when "<code>&lt;/</code>" is typed. Note that this property is only
1966 * honored for markup languages, such as HTML, XML and PHP.<p>
1967 *
1968 * This method fires a property change event of type
1969 * {@link #CLOSE_MARKUP_TAGS_PROPERTY}.
1970 *
1971 * @param close Whether closing markup tags should be automatically
1972 * completed.
1973 * @see #getCloseMarkupTags()
1974 */
1975 public void setCloseMarkupTags(boolean close) {
1976 if (close!=closeMarkupTags) {
1977 closeMarkupTags = close;
1978 firePropertyChange(CLOSE_MARKUP_TAGS_PROPERTY, !close, close);
1979 }
1980 }
1981
1982
1983 /**
1984 * Sets whether code folding is enabled. Note that only certain
1985 * languages will support code folding out of the box. Those languages
1986 * which do not support folding will ignore this property.<p>
1987 * This method fires a property change event of type
1988 * {@link #CODE_FOLDING_PROPERTY}.
1989 *
1990 * @param enabled Whether code folding should be enabled.
1991 * @see #isCodeFoldingEnabled()
1992 */
1993 public void setCodeFoldingEnabled(boolean enabled) {
1994 if (enabled!=foldManager.isCodeFoldingEnabled()) {
1995 foldManager.setCodeFoldingEnabled(enabled);
1996 firePropertyChange(CODE_FOLDING_PROPERTY, !enabled, enabled);
1997 }
1998 }
1999
2000
2001 /**
2002 * Sets the document used by this text area. This is overridden so that
2003 * only instances of {@link RSyntaxDocument} are accepted; for all
2004 * others, an exception will be thrown.
2005 *
2006 * @param document The new document for this text area.
2007 * @throws IllegalArgumentException If the document is not an
2008 * <code>RSyntaxDocument</code>.
2009 */
2010 public void setDocument(Document document) {
2011 if (!(document instanceof RSyntaxDocument))
2012 throw new IllegalArgumentException("Documents for " +
2013 "RSyntaxTextArea must be instances of " +
2014 "RSyntaxDocument!");
2015 super.setDocument(document);
2016 }
2017
2018
2019 /**
2020 * Sets whether EOL markers are visible at the end of each line. This
2021 * method fires a property change of type {@link #EOL_VISIBLE_PROPERTY}.
2022 *
2023 * @param visible Whether EOL markers are visible.
2024 * @see #getEOLMarkersVisible()
2025 * @see #setWhitespaceVisible(boolean)
2026 */
2027 public void setEOLMarkersVisible(boolean visible) {
2028 if (visible!=eolMarkersVisible) {
2029 eolMarkersVisible = visible;
2030 repaint();
2031 firePropertyChange(EOL_VISIBLE_PROPERTY, !visible, visible);
2032 }
2033 }
2034
2035
2036 /**
2037 * Sets the font used by this text area. Note that this method does not
2038 * alter the appearance of an <code>RSyntaxTextArea</code> since it uses
2039 * different fonts for each token type.
2040 *
2041 * @param font The font.
2042 */
2043 public void setFont(Font font) {
2044
2045 Font old = super.getFont();
2046 super.setFont(font); // Do this first.
2047
2048 // Usually programmers keep a single font for all token types, but
2049 // may use bold or italic for styling some.
2050 SyntaxScheme scheme = getSyntaxScheme();
2051 if (scheme!=null && old!=null) {
2052 scheme.changeBaseFont(old, font);
2053 calculateLineHeight();
2054 }
2055
2056 // We must be connected to a screen resource for our
2057 // graphics to be non-null.
2058 if (isDisplayable()) {
2059 refreshFontMetrics(getGraphics2D(getGraphics()));
2060 // Updates the margin line.
2061 updateMarginLineX();
2062 // Force the current line highlight to be repainted, even
2063 // though the caret's location hasn't changed.
2064 forceCurrentLineHighlightRepaint();
2065 // Get line number border in text area to repaint again
2066 // since line heights have updated.
2067 firePropertyChange("font", old, font);
2068 // So parent JScrollPane will have its scrollbars updated.
2069 revalidate();
2070 }
2071
2072 }
2073
2074
2075 /**
2076 * Sets whether fractional font metrics are enabled. This method fires
2077 * a property change event of type {@link #FRACTIONAL_FONTMETRICS_PROPERTY}.
2078 *
2079 * @param enabled Whether fractional font metrics are enabled.
2080 * @see #getFractionalFontMetricsEnabled()
2081 */
2082 public void setFractionalFontMetricsEnabled(boolean enabled) {
2083 if (fractionalFontMetricsEnabled!=enabled) {
2084 fractionalFontMetricsEnabled = enabled;
2085 // We must be connected to a screen resource for our graphics to be
2086 // non-null.
2087 if (isDisplayable()) {
2088 refreshFontMetrics(getGraphics2D(getGraphics()));
2089 }
2090 firePropertyChange(FRACTIONAL_FONTMETRICS_PROPERTY,
2091 !enabled, enabled);
2092 }
2093 }
2094
2095
2096 /**
2097 * Sets the highlighter used by this text area.
2098 *
2099 * @param h The highlighter.
2100 * @throws IllegalArgumentException If <code>h</code> is not an instance
2101 * of {@link RSyntaxTextAreaHighlighter}.
2102 */
2103 public void setHighlighter(Highlighter h) {
2104 if (!(h instanceof RSyntaxTextAreaHighlighter)) {
2105 throw new IllegalArgumentException("RSyntaxTextArea requires " +
2106 "an RSyntaxTextAreaHighlighter for its Highlighter");
2107 }
2108 super.setHighlighter(h);
2109 }
2110
2111
2112 /**
2113 * Sets the color to use when painting hyperlinks.
2114 *
2115 * @param fg The color to use when painting hyperlinks.
2116 * @throws NullPointerException If <code>fg</code> is <code>null</code>.
2117 * @see #getHyperlinkForeground()
2118 * @see #setHyperlinksEnabled(boolean)
2119 */
2120 public void setHyperlinkForeground(Color fg) {
2121 if (fg==null) {
2122 throw new NullPointerException("fg cannot be null");
2123 }
2124 hyperlinkFG = fg;
2125 }
2126
2127
2128 /**
2129 * Sets whether hyperlinks are enabled for this text area. This method
2130 * fires a property change event of type
2131 * {@link #HYPERLINKS_ENABLED_PROPERTY}.
2132 *
2133 * @param enabled Whether hyperlinks are enabled.
2134 * @see #getHyperlinksEnabled()
2135 */
2136 public void setHyperlinksEnabled(boolean enabled) {
2137 if (this.hyperlinksEnabled!=enabled) {
2138 this.hyperlinksEnabled = enabled;
2139 repaint();
2140 firePropertyChange(HYPERLINKS_ENABLED_PROPERTY, !enabled, enabled);
2141 }
2142 }
2143
2144
2145 /**
2146 * Sets the mask for the key used to toggle whether we are scanning for
2147 * hyperlinks with mouse hovering.
2148 *
2149 * @param mask The mask to use. This should be a value such as
2150 * {@link InputEvent#CTRL_DOWN_MASK} or
2151 * {@link InputEvent#META_DOWN_MASK}.
2152 * For invalid values, behavior is undefined.
2153 * @see InputEvent
2154 */
2155 public void setLinkScanningMask(int mask) {
2156 if (mask==InputEvent.CTRL_DOWN_MASK ||
2157 mask==InputEvent.META_DOWN_MASK ||
2158 mask==InputEvent.ALT_DOWN_MASK ||
2159 mask==InputEvent.SHIFT_DOWN_MASK) {
2160 linkScanningMask = mask;
2161 }
2162 }
2163
2164
2165 /**
2166 * Toggles whether "mark occurrences" is enabled. This method fires a
2167 * property change event of type {@link #MARK_OCCURRENCES_PROPERTY}.
2168 *
2169 * @param markOccurrences Whether "Mark Occurrences" should be enabled.
2170 * @see #getMarkOccurrences()
2171 * @see #setMarkOccurrencesColor(Color)
2172 */
2173 public void setMarkOccurrences(boolean markOccurrences) {
2174 if (markOccurrences) {
2175 if (markOccurrencesSupport==null) {
2176 markOccurrencesSupport = new MarkOccurrencesSupport();
2177 markOccurrencesSupport.install(this);
2178 firePropertyChange(MARK_OCCURRENCES_PROPERTY, false, true);
2179 }
2180 }
2181 else {
2182 if (markOccurrencesSupport!=null) {
2183 markOccurrencesSupport.uninstall();
2184 markOccurrencesSupport = null;
2185 firePropertyChange(MARK_OCCURRENCES_PROPERTY, true, false);
2186 }
2187 }
2188 }
2189
2190
2191 /**
2192 * Sets the "mark occurrences" color.
2193 *
2194 * @param color The new color. This cannot be <code>null</code>.
2195 * @see #getMarkOccurrencesColor()
2196 * @see #setMarkOccurrences(boolean)
2197 */
2198 public void setMarkOccurrencesColor(Color color) {
2199 markOccurrencesColor = color;
2200 if (markOccurrencesSupport!=null) {
2201 markOccurrencesSupport.setColor(color);
2202 }
2203 }
2204
2205
2206 /**
2207 * Sets the color used as the background for a matched bracket.
2208 *
2209 * @param color The color to use. If this is <code>null</code>, then no
2210 * special background is painted behind a matched bracket.
2211 * @see #getMatchedBracketBGColor
2212 * @see #setMatchedBracketBorderColor
2213 * @see #setPaintMarkOccurrencesBorder(boolean)
2214 */
2215 public void setMatchedBracketBGColor(Color color) {
2216 matchedBracketBGColor = color;
2217 if (match!=null)
2218 repaint();
2219 }
2220
2221
2222 /**
2223 * Sets the color used as the border for a matched bracket.
2224 *
2225 * @param color The color to use.
2226 * @see #getMatchedBracketBorderColor
2227 * @see #setMatchedBracketBGColor
2228 */
2229 public void setMatchedBracketBorderColor(Color color) {
2230 matchedBracketBorderColor = color;
2231 if (match!=null)
2232 repaint();
2233 }
2234
2235
2236 /**
2237 * Toggles whether a border should be painted around marked occurrences.
2238 *
2239 * @param paintBorder Whether to paint a border.
2240 * @see #getPaintMarkOccurrencesBorder()
2241 * @see #setMarkOccurrencesColor(Color)
2242 * @see #setMarkOccurrences(boolean)
2243 */
2244 public void setPaintMarkOccurrencesBorder(boolean paintBorder) {
2245 paintMarkOccurrencesBorder = paintBorder;
2246 if (markOccurrencesSupport!=null) {
2247 markOccurrencesSupport.setPaintBorder(paintBorder);
2248 }
2249 }
2250
2251
2252 /**
2253 * Toggles whether tab lines are painted. This method fires a property
2254 * change event of type {@link #TAB_LINES_PROPERTY}.
2255 *
2256 * @param paint Whether tab lines are painted.
2257 * @see #getPaintTabLines()
2258 * @see #setTabLineColor(Color)
2259 */
2260 public void setPaintTabLines(boolean paint) {
2261 if (paint!=paintTabLines) {
2262 paintTabLines = paint;
2263 repaint();
2264 firePropertyChange(TAB_LINES_PROPERTY, !paint, paint);
2265 }
2266 }
2267
2268
2269 /**
2270 * Sets what type of syntax highlighting this editor is doing. This method
2271 * fires a property change of type {@link #SYNTAX_STYLE_PROPERTY}.
2272 *
2273 * @param styleKey The syntax editing style to use, for example,
2274 * {@link SyntaxConstants#SYNTAX_STYLE_NONE} or
2275 * {@link SyntaxConstants#SYNTAX_STYLE_JAVA}.
2276 * @see #getSyntaxEditingStyle()
2277 * @see SyntaxConstants
2278 */
2279 public void setSyntaxEditingStyle(String styleKey) {
2280 if (styleKey==null) {
2281 styleKey = SYNTAX_STYLE_NONE;
2282 }
2283 if (!styleKey.equals(syntaxStyleKey)) {
2284 String oldStyle = syntaxStyleKey;
2285 syntaxStyleKey = styleKey;
2286 ((RSyntaxDocument)getDocument()).setSyntaxStyle(styleKey);
2287 firePropertyChange(SYNTAX_STYLE_PROPERTY, oldStyle, styleKey);
2288 setActiveLineRange(-1, -1);
2289 }
2290
2291 }
2292
2293
2294 /**
2295 * Sets all of the colors used in syntax highlighting to the colors
2296 * specified. This uses a shallow copy of the color scheme so that
2297 * multiple text areas can share the same color scheme and have their
2298 * properties changed simultaneously.<p>
2299 *
2300 * This method fires a property change event of type
2301 * {@link #SYNTAX_SCHEME_PROPERTY}.
2302 *
2303 * @param scheme The instance of <code>SyntaxScheme</code> to use.
2304 * @see #getSyntaxScheme()
2305 */
2306 public void setSyntaxScheme(SyntaxScheme scheme) {
2307
2308 // NOTE: We don't check whether colorScheme is the same as the
2309 // current scheme because DecreaseFontSizeAction and
2310 // IncreaseFontSizeAction need it this way.
2311 // FIXME: Find a way around this.
2312
2313 SyntaxScheme old = this.syntaxScheme;
2314 this.syntaxScheme = scheme;
2315
2316 // Recalculate the line height. We do this here instead of in
2317 // refreshFontMetrics() as this method is called less often and we
2318 // don't need the rendering hints to get the font's height.
2319 calculateLineHeight();
2320
2321 if (isDisplayable()) {
2322 refreshFontMetrics(getGraphics2D(getGraphics()));
2323 }
2324
2325 // Updates the margin line.
2326 updateMarginLineX();
2327
2328 // Force the current line highlight to be repainted, even though
2329 // the caret's location hasn't changed.
2330 forceCurrentLineHighlightRepaint();
2331
2332 // So encompassing JScrollPane will have its scrollbars updated.
2333 revalidate();
2334
2335 firePropertyChange(SYNTAX_SCHEME_PROPERTY, old, this.syntaxScheme);
2336
2337 }
2338
2339
2340 /**
2341 * If templates are enabled, all currently-known templates are forgotten
2342 * and all templates are loaded from all files in the specified directory
2343 * ending in "*.xml". If templates aren't enabled, nothing happens.
2344 *
2345 * @param dir The directory containing files ending in extension
2346 * <code>.xml</code> that contain templates to load.
2347 * @return <code>true</code> if the load was successful;
2348 * <code>false</code> if either templates aren't currently
2349 * enabled or the load failed somehow (most likely, the
2350 * directory doesn't exist).
2351 * @see #getTemplatesEnabled
2352 * @see #setTemplatesEnabled
2353 * @see #saveTemplates
2354 */
2355 public static synchronized boolean setTemplateDirectory(String dir) {
2356 if (getTemplatesEnabled() && dir!=null) {
2357 File directory = new File(dir);
2358 if (directory.isDirectory()) {
2359 return getCodeTemplateManager().
2360 setTemplateDirectory(directory)>-1;
2361 }
2362 boolean created = directory.mkdir();
2363 if (created) {
2364 return getCodeTemplateManager().
2365 setTemplateDirectory(directory)>-1;
2366 }
2367 }
2368 return false;
2369 }
2370
2371
2372 /**
2373 * Enables or disables templates.<p>
2374 *
2375 * Templates are a set of "shorthand identifiers" that you can configure
2376 * so that you only have to type a short identifier (such as "forb") to
2377 * insert a larger amount of code into the document (such as:<p>
2378 *
2379 * <pre>
2380 * for (&lt;caret&gt;) {
2381 *
2382 * }
2383 * </pre>
2384 *
2385 * Templates are a shared resource among all instances of
2386 * <code>RSyntaxTextArea</code>; that is, templates can only be
2387 * enabled/disabled for all text areas globally, not individually, and
2388 * all text areas have access of the same templates. This should not
2389 * be an issue; rather, it should be beneficial as it promotes
2390 * uniformity among all text areas in an application.
2391 *
2392 * @param enabled Whether or not templates should be enabled.
2393 * @see #getTemplatesEnabled()
2394 */
2395 public static synchronized void setTemplatesEnabled(boolean enabled) {
2396 templatesEnabled = enabled;
2397 }
2398
2399
2400 /**
2401 * Sets the color use to paint tab lines. This method fires a property
2402 * change event of type {@link #TAB_LINE_COLOR_PROPERTY}.
2403 *
2404 * @param c The color. If this value is <code>null</code>, the default
2405 * (gray) is used.
2406 * @see #getTabLineColor()
2407 * @see #setPaintTabLines(boolean)
2408 * @see #getPaintTabLines()
2409 */
2410 public void setTabLineColor(Color c) {
2411
2412 if (c==null) {
2413 c = Color.gray;
2414 }
2415
2416 if (!c.equals(tabLineColor)) {
2417 Color old = tabLineColor;
2418 tabLineColor = c;
2419 if (getPaintTabLines()) {
2420 repaint();
2421 }
2422 firePropertyChange(TAB_LINE_COLOR_PROPERTY, old, tabLineColor);
2423 }
2424
2425 }
2426
2427
2428 /**
2429 * Sets whether "focusable" tool tips are used instead of standard ones.
2430 * Focusable tool tips are tool tips that the user can click on,
2431 * resize, copy from, and clink links in. This method fires a property
2432 * change event of type {@link #FOCUSABLE_TIPS_PROPERTY}.
2433 *
2434 * @param use Whether to use focusable tool tips.
2435 * @see #getUseFocusableTips()
2436 * @see FocusableTip
2437 */
2438 public void setUseFocusableTips(boolean use) {
2439 if (use!=useFocusableTips) {
2440 useFocusableTips = use;
2441 firePropertyChange(FOCUSABLE_TIPS_PROPERTY, !use, use);
2442 }
2443 }
2444
2445
2446 /**
2447 * Sets whether whitespace is visible. This method fires a property change
2448 * of type {@link #VISIBLE_WHITESPACE_PROPERTY}.
2449 *
2450 * @param visible Whether whitespace should be visible.
2451 * @see #isWhitespaceVisible
2452 */
2453 public void setWhitespaceVisible(boolean visible) {
2454 if (whitespaceVisible!=visible) {
2455 whitespaceVisible = visible;
2456 ((RSyntaxDocument)getDocument()).setWhitespaceVisible(visible);
2457 repaint();
2458 firePropertyChange(VISIBLE_WHITESPACE_PROPERTY, !visible, visible);
2459 }
2460 }
2461
2462
2463 /**
2464 * Resets the editor state after the user clicks on a hyperlink or releases
2465 * the hyperlink modifier.
2466 */
2467 private void stopScanningForLinks() {
2468 if (isScanningForLinks) {
2469 Cursor c = getCursor();
2470 isScanningForLinks = false;
2471 hoveredOverLinkOffset = -1;
2472 if (c!=null && c.getType()==Cursor.HAND_CURSOR) {
2473 setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
2474 repaint(); // TODO: Repaint just the affected line.
2475 }
2476 }
2477 }
2478
2479
2480 /**
2481 * Returns the token at the specified position in the view.
2482 *
2483 * @param p The position in the view.
2484 * @return The token, or <code>null</code> if no token is at that
2485 * position.
2486 * @see #modelToToken(int)
2487 */
2488 /*
2489 * TODO: This is a little inefficient. This should convert view
2490 * coordinates to the underlying token (if any). The way things currently
2491 * are, we're calling getTokenListForLine() twice (once in viewToModel()
2492 * and once here).
2493 */
2494 private Token viewToToken(Point p) {
2495 return modelToToken(viewToModel(p));
2496 }
2497
2498
2499 /**
2500 * A timer that animates the "bracket matching" animation.
2501 */
2502 private class BracketMatchingTimer extends Timer implements ActionListener {
2503
2504 private int pulseCount;
2505
2506 public BracketMatchingTimer() {
2507 super(20, null);
2508 addActionListener(this);
2509 setCoalesce(false);
2510 }
2511
2512 public void actionPerformed(ActionEvent e) {
2513 if (isBracketMatchingEnabled()) {
2514 if (match!=null) {
2515 if (pulseCount<5) {
2516 pulseCount++;
2517 match.x--;
2518 match.y--;
2519 match.width += 2;
2520 match.height += 2;
2521 repaint(match.x,match.y, match.width,match.height);
2522 }
2523 else if (pulseCount<7) {
2524 pulseCount++;
2525 match.x++;
2526 match.y++;
2527 match.width -= 2;
2528 match.height -= 2;
2529 repaint(match.x-2,match.y-2, match.width+5,match.height+5);
2530 }
2531 else {
2532 stop();
2533 pulseCount = 0;
2534 }
2535 }
2536 }
2537 }
2538
2539 public void start() {
2540 match.x += 3;
2541 match.y += 3;
2542 match.width -= 6;
2543 match.height -= 6; // So animation can "grow" match
2544 pulseCount = 0;
2545 super.start();
2546 }
2547
2548 }
2549
2550
2551 /**
2552 * Handles hyperlinks.
2553 */
2554 private class RSyntaxTextAreaMutableCaretEvent
2555 extends RTextAreaMutableCaretEvent {
2556
2557 protected RSyntaxTextAreaMutableCaretEvent(RTextArea textArea) {
2558 super(textArea);
2559 }
2560
2561 public void mouseClicked(MouseEvent e) {
2562 if (getHyperlinksEnabled() && isScanningForLinks &&
2563 hoveredOverLinkOffset>-1) {
2564 Token t = modelToToken(hoveredOverLinkOffset);
2565 URL url = null;
2566 String desc = null;
2567 try {
2568 String temp = t.getLexeme();
2569 // URI's need "http://" prefix for web URL's to work.
2570 if (temp.startsWith("www.")) {
2571 temp = "http://" + temp;
2572 }
2573 url = new URL(temp);
2574 } catch (MalformedURLException mue) {
2575 desc = mue.getMessage();
2576 }
2577 HyperlinkEvent he = new HyperlinkEvent(this,
2578 HyperlinkEvent.EventType.ACTIVATED,
2579 url, desc);
2580 fireHyperlinkUpdate(he);
2581 stopScanningForLinks();
2582 }
2583 }
2584
2585 public void mouseMoved(MouseEvent e) {
2586 super.mouseMoved(e);
2587 if (getHyperlinksEnabled()) {
2588 if ((e.getModifiersEx()&linkScanningMask)!=0) {
2589 isScanningForLinks = true;
2590 Token t = viewToToken(e.getPoint());
2591 Cursor c2 = null;
2592 if (t!=null && t.isHyperlink()) {
2593 hoveredOverLinkOffset = t.offset;
2594 c2 = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
2595 }
2596 else {
2597 c2 = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
2598 hoveredOverLinkOffset = -1;
2599 }
2600 if (getCursor()!=c2) {
2601 setCursor(c2);
2602 // TODO: Repaint just the affected line(s).
2603 repaint(); // Link either left or went into.
2604 }
2605 }
2606 else {
2607 if (isScanningForLinks) {
2608 stopScanningForLinks();
2609 }
2610 }
2611 }
2612 }
2613
2614 }
2615
2616
2617}
Note: See TracBrowser for help on using the repository browser.