source: other-projects/rsyntax-textarea/src/java/org/fife/ui/rtextarea/RTextAreaBase.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: 33.9 KB
Line 
1/*
2 * 04/07/2005
3 *
4 * RTextAreaBase.java - The base class for an RTextArea.
5 *
6 * This library is distributed under a modified BSD license. See the included
7 * RSyntaxTextArea.License.txt file for details.
8 */
9package org.fife.ui.rtextarea;
10
11import java.awt.AWTEvent;
12import java.awt.Color;
13import java.awt.Font;
14import java.awt.Graphics;
15import java.awt.Image;
16import java.awt.Rectangle;
17import java.awt.event.ComponentEvent;
18import java.awt.event.FocusEvent;
19import java.awt.event.FocusListener;
20import java.awt.event.MouseEvent;
21import java.awt.event.MouseListener;
22import java.awt.event.MouseMotionListener;
23import javax.swing.JTextArea;
24import javax.swing.event.CaretEvent;
25import javax.swing.plaf.ColorUIResource;
26import javax.swing.plaf.TextUI;
27import javax.swing.text.AbstractDocument;
28import javax.swing.text.BadLocationException;
29
30
31/**
32 * This is the base class for <code>RTextArea</code>; basically it's just an
33 * extension of <code>javax.swing.JTextArea</code> adding a bunch of properties.
34 * <p>
35 *
36 * This class is only supposed to be overridden by <code>RTextArea</code>.
37 *
38 * @author Robert Futrell
39 * @version 0.8
40 */
41abstract class RTextAreaBase extends JTextArea {
42
43 public static final String BACKGROUND_IMAGE_PROPERTY = "background.image";
44 public static final String CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY = "RTA.currentLineHighlightColor";
45 public static final String CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY = "RTA.currentLineHighlightFade";
46 public static final String HIGHLIGHT_CURRENT_LINE_PROPERTY = "RTA.currentLineHighlight";
47 public static final String ROUNDED_SELECTION_PROPERTY = "RTA.roundedSelection";
48
49 private boolean tabsEmulatedWithSpaces; // If true, tabs will be expanded to spaces.
50
51 private boolean highlightCurrentLine; // If true, the current line is highlighted.
52 private Color currentLineColor; // The color used to highlight the current line.
53 private boolean marginLineEnabled; // If true, paint a "margin line."
54 private Color marginLineColor; // The color used to paint the margin line.
55 private int marginLineX; // The x-location of the margin line.
56 private int marginSizeInChars; // How many 'm' widths the margin line is over.
57 private boolean fadeCurrentLineHighlight; // "Fade effect" for current line highlight.
58 private boolean roundedSelectionEdges;
59 private int previousCaretY;
60int currentCaretY; // Used to know when to rehighlight current line.
61
62 private BackgroundPainterStrategy backgroundPainter; // Paints the background.
63
64 private RTAMouseListener mouseListener;
65
66 private static final Color DEFAULT_CARET_COLOR = new ColorUIResource(255,51,51);
67 private static final Color DEFAULT_CURRENT_LINE_HIGHLIGHT_COLOR = new Color(255,255,170);
68 private static final Color DEFAULT_MARGIN_LINE_COLOR = new Color(255,224,224);
69 private static final int DEFAULT_TAB_SIZE = 4;
70 private static final int DEFAULT_MARGIN_LINE_POSITION = 80;
71
72
73 /**
74 * Constructor.
75 */
76 public RTextAreaBase() {
77 init();
78 }
79
80
81 /**
82 * Constructor.
83 *
84 * @param doc The document for the editor.
85 */
86 public RTextAreaBase(AbstractDocument doc) {
87 super(doc);
88 init();
89 }
90
91
92 /**
93 * Constructor.
94 *
95 * @param text The initial text to display.
96 */
97 public RTextAreaBase(String text) {
98 super(text);
99 init();
100 }
101
102
103 /**
104 * Constructor.
105 *
106 * @param rows The number of rows to display.
107 * @param cols The number of columns to display.
108 * @throws IllegalArgumentException If either <code>rows</code> or
109 * <code>cols</code> is negative.
110 */
111 public RTextAreaBase(int rows, int cols) {
112 super(rows, cols);
113 init();
114 }
115
116
117 /**
118 * Constructor.
119 *
120 * @param text The initial text to display.
121 * @param rows The number of rows to display.
122 * @param cols The number of columns to display.
123 * @throws IllegalArgumentException If either <code>rows</code> or
124 * <code>cols</code> is negative.
125 */
126 public RTextAreaBase(String text, int rows, int cols) {
127 super(text, rows, cols);
128 init();
129 }
130
131
132 /**
133 * Constructor.
134 *
135 * @param doc The document for the editor.
136 * @param text The initial text to display.
137 * @param rows The number of rows to display.
138 * @param cols The number of columns to display.
139 * @throws IllegalArgumentException If either <code>rows</code> or
140 * <code>cols</code> is negative.
141 */
142 public RTextAreaBase(AbstractDocument doc, String text, int rows,
143 int cols) {
144 super(doc, text, rows, cols);
145 init();
146 }
147
148
149 /**
150 * Adds listeners that listen for changes to the current line, so we can
151 * update our "current line highlight." This is needed only because of an
152 * apparent difference between the JRE 1.4.2 and 1.5.0 (needed on 1.4.2,
153 * not needed on 1.5.0).
154 */
155 protected void addCurrentLineHighlightListeners() {
156 boolean add = true;
157 MouseMotionListener[] mouseMotionListeners = getMouseMotionListeners();
158 for (int i=0; i<mouseMotionListeners.length; i++) {
159 if (mouseMotionListeners[i]==mouseListener) {
160 add = false;
161 break;
162 }
163 }
164 if (add==true) {
165 //System.err.println("Adding mouse motion listener!");
166 addMouseMotionListener(mouseListener);
167 }
168 MouseListener[] mouseListeners = getMouseListeners();
169 for (int i=0; i<mouseListeners.length; i++) {
170 if (mouseListeners[i]==mouseListener) {
171 add = false;
172 break;
173 }
174 }
175 if (add==true) {
176 //System.err.println("Adding mouse listener!");
177 addMouseListener(mouseListener);
178 }
179 }
180
181
182 /*
183 * TODO: Figure out why RTextArea doesn't work with RTL orientation!
184 */
185// public void applyComponentOrientation(ComponentOrientation orientation) {
186// super.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
187// }
188
189
190 /**
191 * Converts all instances of a number of spaces equal to a tab size
192 * into a tab in this text area.
193 *
194 * @see #convertTabsToSpaces
195 * @see #getTabsEmulated
196 * @see #setTabsEmulated
197 */
198 public void convertSpacesToTabs() {
199
200 // FIXME: This is inefficient and will yield an OutOfMemoryError if
201 // done on a large document. We should scan 1 line at a time and
202 // replace; it'll be slower but safer.
203
204 int caretPosition = getCaretPosition();
205 int tabSize = getTabSize();
206 String tabInSpaces = "";
207 for (int i=0; i<tabSize; i++)
208 tabInSpaces += " ";
209 String text = getText();
210 setText(text.replaceAll(tabInSpaces, "\t"));
211 int newDocumentLength = getDocument().getLength();
212
213 // Place the caret back in its proper position.
214 if (caretPosition<newDocumentLength)
215 setCaretPosition(caretPosition);
216 else
217 setCaretPosition(newDocumentLength-1);
218
219 }
220
221
222 /**
223 * Converts all instances of a tab into a number of spaces equivalent
224 * to a tab in this text area.
225 *
226 * @see #convertSpacesToTabs
227 * @see #getTabsEmulated
228 * @see #setTabsEmulated
229 */
230 public void convertTabsToSpaces() {
231
232 // FIXME: This is inefficient and will yield an OutOfMemoryError if
233 // done on a large document. We should scan 1 line at a time and
234 // replace; it'll be slower but safer.
235
236 int caretPosition = getCaretPosition();
237 int tabSize = getTabSize();
238 StringBuffer tabInSpaces = new StringBuffer();
239 for (int i=0; i<tabSize; i++)
240 tabInSpaces.append(' ');
241 String text = getText();
242 setText(text.replaceAll("\t", tabInSpaces.toString()));
243
244 // Put caret back at same place in document.
245 setCaretPosition(caretPosition);
246
247 }
248
249
250 /**
251 * Returns the caret event/mouse listener for <code>RTextArea</code>s.
252 *
253 * @return The caret event/mouse listener.
254 */
255 protected abstract RTAMouseListener createMouseListener();
256
257
258 /**
259 * Returns the a real UI to install on this text component. Subclasses
260 * can override this method to return an extended version of
261 * <code>RTextAreaUI</code>.
262 *
263 * @return The UI.
264 */
265 protected abstract RTextAreaUI createRTextAreaUI();
266
267
268 /**
269 * Forces the current line highlight to be repainted. This hack is
270 * necessary for those situations when the view (appearance) changes
271 * but the caret's location hasn't (and thus the current line highlight
272 * coordinates won't get changed). Examples of this are when
273 * word wrap is toggled and when syntax styles are changed in an
274 * <code>RSyntaxTextArea</code>.
275 */
276 protected void forceCurrentLineHighlightRepaint() {
277 // Check isShowing() to prevent BadLocationException
278 // in constructor if linewrap is set to true.
279 if (isShowing()) {
280 // Changing previousCaretY makes us sure to get a repaint.
281 previousCaretY = -1;
282 // Trick it into checking for the need to repaint by firing
283 // a false caret event.
284 fireCaretUpdate(mouseListener);
285 }
286 }
287
288
289 /**
290 * Returns the <code>java.awt.Color</code> used as the background color for
291 * this text area. If a <code>java.awt.Image</code> image is currently
292 * being used instead, <code>null</code> is returned.
293 *
294 * @return The current background color, or <code>null</code> if an image
295 * is currently the background.
296 */
297 public final Color getBackground() {
298 Object bg = getBackgroundObject();
299 return (bg instanceof Color) ? (Color)bg : null;
300 }
301
302 /**
303 * Returns the image currently used for the background.
304 * If the current background is currently a <code>java.awt.Color</code> and
305 * not a <code>java.awt.Image</code>, then <code>null</code> is returned.
306 *
307 * @return A <code>java.awt.Image</code> used for the background, or
308 * <code>null</code> if the background is not an image.
309 * @see #setBackgroundImage
310 */
311 public final Image getBackgroundImage() {
312 Object bg = getBackgroundObject();
313 return (bg instanceof Image) ? (Image)bg : null;
314 }
315
316
317 /**
318 * Returns the <code>Object</code> representing the background for all
319 * documents in this tabbed pane; either a <code>java.awt.Color</code> or a
320 * <code>java.lang.Image</code> containing the image used as the background
321 * for this text area.
322 *
323 * @return The <code>Object</code> used for the background.
324 * @see #setBackgroundObject(Object newBackground)
325 */
326 public final Object getBackgroundObject() {
327 if (backgroundPainter==null)
328 return null;
329 return (backgroundPainter instanceof ImageBackgroundPainterStrategy) ?
330 (Object)((ImageBackgroundPainterStrategy)backgroundPainter).
331 getMasterImage() :
332 (Object)((ColorBackgroundPainterStrategy)backgroundPainter).
333 getColor();
334 }
335
336
337 /**
338 * Gets the line number that the caret is on.
339 *
340 * @return The zero-based line number that the caret is on.
341 */
342 public final int getCaretLineNumber() {
343 try {
344 return getLineOfOffset(getCaretPosition());
345 } catch (BadLocationException ble) {
346 return 0; // Never happens
347 }
348 }
349
350
351 /**
352 * Gets the position from the beginning of the current line that the caret
353 * is on.
354 *
355 * @return The zero-based position from the beginning of the current line
356 * that the caret is on.
357 */
358 public final int getCaretOffsetFromLineStart() {
359 try {
360 int pos = getCaretPosition();
361 return pos - getLineStartOffset(getLineOfOffset(pos));
362 } catch (BadLocationException ble) {
363 return 0; // Never happens
364 }
365 }
366
367
368 /**
369 * Returns the color being used to highlight the current line. Note that
370 * if highlighting the current line is turned off, you will not be seeing
371 * this highlight.
372 *
373 * @return The color being used to highlight the current line.
374 * @see #getHighlightCurrentLine()
375 * @see #setHighlightCurrentLine(boolean)
376 * @see #setCurrentLineHighlightColor
377 */
378 public Color getCurrentLineHighlightColor() {
379 return currentLineColor;
380 }
381
382
383 /**
384 * Returns the default caret color.
385 *
386 * @return The default caret color.
387 */
388 public static final Color getDefaultCaretColor() {
389 return DEFAULT_CARET_COLOR;
390 }
391
392
393 /**
394 * Returns the "default" color for highlighting the current line. Note
395 * that this color was chosen only because it looks nice (to me) against a
396 * white background.
397 *
398 * @return The default color for highlighting the current line.
399 */
400 public static final Color getDefaultCurrentLineHighlightColor() {
401 return DEFAULT_CURRENT_LINE_HIGHLIGHT_COLOR;
402 }
403
404
405 /**
406 * Returns the default font for text areas.
407 *
408 * @return The default font.
409 */
410 public static final Font getDefaultFont() {
411
412 Font font = null;
413
414 if (isOSX()) {
415 // Snow Leopard (1.6) uses Menlo as default monospaced font,
416 // pre-Snow Leopard used Monaco.
417 font = new Font("Menlo", Font.PLAIN, 12);
418 if (!"Menlo".equals(font.getFamily())) {
419 font = new Font("Monaco", Font.PLAIN, 12);
420 if (!"Monaco".equals(font.getFamily())) { // Shouldn't happen
421 font = new Font("Monospaced", Font.PLAIN, 13);
422 }
423 }
424 }
425 else {
426 font = new Font("Monospaced", Font.PLAIN, 13);
427 }
428
429 //System.out.println(font.getFamily() + ", " + font.getName());
430 return font;
431
432 }
433
434
435 /**
436 * Returns the default foreground color for text in this text area.
437 *
438 * @return The default foreground color.
439 */
440 public static final Color getDefaultForeground() {
441 return Color.BLACK;
442 }
443
444
445 /**
446 * Returns the default color for the margin line.
447 *
448 * @return The default margin line color.
449 * @see #getMarginLineColor()
450 * @see #setMarginLineColor(Color)
451 */
452 public static final Color getDefaultMarginLineColor() {
453 return DEFAULT_MARGIN_LINE_COLOR;
454 }
455
456
457 /**
458 * Returns the default margin line position.
459 *
460 * @return The default margin line position.
461 * @see #getMarginLinePosition
462 * @see #setMarginLinePosition
463 */
464 public static final int getDefaultMarginLinePosition() {
465 return DEFAULT_MARGIN_LINE_POSITION;
466 }
467
468
469 /**
470 * Returns the default tab size, in spaces.
471 *
472 * @return The default tab size.
473 */
474 public static final int getDefaultTabSize() {
475 return DEFAULT_TAB_SIZE;
476 }
477
478
479 /**
480 * Returns whether the current line highlight is faded.
481 *
482 * @return Whether the current line highlight is faded.
483 * @see #setFadeCurrentLineHighlight
484 */
485 public boolean getFadeCurrentLineHighlight() {
486 return fadeCurrentLineHighlight;
487 }
488
489
490 /**
491 * Returns whether or not the current line is highlighted.
492 *
493 * @return Whether or the current line is highlighted.
494 * @see #setHighlightCurrentLine(boolean)
495 * @see #getCurrentLineHighlightColor
496 * @see #setCurrentLineHighlightColor
497 */
498 public boolean getHighlightCurrentLine() {
499 return highlightCurrentLine;
500 }
501
502
503 /**
504 * Returns the offset of the last character of the line that the caret is
505 * on.
506 *
507 * @return The last offset of the line that the caret is currently on.
508 */
509 public final int getLineEndOffsetOfCurrentLine() {
510 try {
511 return getLineEndOffset(getCaretLineNumber());
512 } catch (BadLocationException ble) {
513 return 0; // Never happens
514 }
515 }
516
517
518 /**
519 * Returns the height of a line of text in this text area.
520 *
521 * @return The height of a line of text.
522 */
523 public int getLineHeight() {
524 return getRowHeight();
525 }
526
527
528 /**
529 * Returns the offset of the first character of the line that the caret is
530 * on.
531 *
532 * @return The first offset of the line that the caret is currently on.
533 */
534 public final int getLineStartOffsetOfCurrentLine() {
535 try {
536 return getLineStartOffset(getCaretLineNumber());
537 } catch (BadLocationException ble) {
538 return 0; // Never happens
539 }
540 }
541
542
543 /**
544 * Returns the color used to paint the margin line.
545 *
546 * @return The margin line color.
547 * @see #setMarginLineColor(Color)
548 */
549 public Color getMarginLineColor() {
550 return marginLineColor;
551 }
552
553
554 /**
555 * Returns the margin line position (in pixels) from the left-hand side of
556 * the text area.
557 *
558 * @return The margin line position.
559 * @see #getDefaultMarginLinePosition
560 * @see #setMarginLinePosition
561 */
562 public int getMarginLinePixelLocation() {
563 return marginLineX;
564 }
565
566
567 /**
568 * Returns the margin line position (which is the number of 'm' widths in
569 * the current font the margin line is over).
570 *
571 * @return The margin line position.
572 * @see #getDefaultMarginLinePosition
573 * @see #setMarginLinePosition
574 */
575 public int getMarginLinePosition() {
576 return marginSizeInChars;
577 }
578
579
580 /**
581 * Returns whether selection edges are rounded in this text area.
582 *
583 * @return Whether selection edges are rounded.
584 * @see #setRoundedSelectionEdges(boolean)
585 */
586 public boolean getRoundedSelectionEdges() {
587 return roundedSelectionEdges;
588 }
589
590
591 /**
592 * Returns whether or not tabs are emulated with spaces (i.e., "soft"
593 * tabs).
594 *
595 * @return <code>true</code> if tabs are emulated with spaces;
596 * <code>false</code> if they aren't.
597 * @see #setTabsEmulated
598 */
599 public boolean getTabsEmulated() {
600 return tabsEmulatedWithSpaces;
601 }
602
603
604 /**
605 * Initializes this text area.
606 */
607 private void init() {
608
609 // Sets the UI. Note that setUI() is overridden in RTextArea to only
610 // update the popup menu; this method must be called to set the real
611 // UI. This is done because the look and feel of an RTextArea is
612 // independent of the installed Java look and feels.
613 setRTextAreaUI(createRTextAreaUI());
614
615 // So we get notified when the component is resized.
616 enableEvents(AWTEvent.COMPONENT_EVENT_MASK|AWTEvent.KEY_EVENT_MASK);
617
618 // Defaults for various properties.
619 setHighlightCurrentLine(true);
620 setCurrentLineHighlightColor(getDefaultCurrentLineHighlightColor());
621 setMarginLineEnabled(false);
622 setMarginLineColor(getDefaultMarginLineColor());
623 setMarginLinePosition(getDefaultMarginLinePosition());
624 setBackgroundObject(Color.WHITE);
625 setWrapStyleWord(true);// We only support wrapping at word boundaries.
626 setTabSize(5);
627 setForeground(Color.BLACK);
628 setTabsEmulated(false);
629
630 // Stuff needed by the caret listener below.
631 previousCaretY = currentCaretY = getInsets().top;
632
633 // Stuff to highlight the current line.
634 mouseListener = createMouseListener();
635 // Also acts as a focus listener so we can update our shared actions
636 // (cut, copy, etc. on the popup menu).
637 addFocusListener(mouseListener);
638 addCurrentLineHighlightListeners();
639
640 }
641
642
643 /**
644 * Returns whether or not the margin line is being painted.
645 *
646 * @return Whether or not the margin line is being painted.
647 * @see #setMarginLineEnabled
648 */
649 public boolean isMarginLineEnabled() {
650 return marginLineEnabled;
651 }
652
653
654 /**
655 * Returns whether the OS we're running on is OS X.
656 *
657 * @return Whether the OS we're running on is OS X.
658 */
659 public static boolean isOSX() {
660 // Recommended at:
661 // http://developer.apple.com/mac/library/technotes/tn2002/tn2110.html
662 String osName = System.getProperty("os.name").toLowerCase();
663 return osName.startsWith("mac os x");
664 }
665
666
667 /**
668 * Paints the text area.
669 *
670 * @param g The graphics context with which to paint.
671 */
672 protected void paintComponent(Graphics g) {
673
674 //long startTime = System.currentTimeMillis();
675
676 backgroundPainter.paint(g, getVisibleRect());
677
678 // Paint the main part of the text area.
679 TextUI ui = getUI();
680 if (ui != null) {
681 // Not allowed to modify g, so make a copy.
682 Graphics scratchGraphics = g.create();
683 try {
684 ui.update(scratchGraphics, this);
685 } finally {
686 scratchGraphics.dispose();
687 }
688 }
689
690 //long endTime = System.currentTimeMillis();
691 //System.err.println(endTime-startTime);
692
693 }
694
695
696 /**
697 * Updates the current line highlight location.
698 */
699 protected void possiblyUpdateCurrentLineHighlightLocation() {
700
701 int width = getWidth();
702 int lineHeight = getLineHeight();
703 int dot = getCaretPosition();
704
705 // If we're wrapping lines we need to check the actual y-coordinate
706 // of the caret, not just the line number, since a single logical
707 // line can span multiple physical lines.
708 if (getLineWrap()) {
709 try {
710 Rectangle temp = modelToView(dot);
711 if (temp!=null) {
712 currentCaretY = temp.y;
713 }
714 } catch (BadLocationException ble) {
715 ble.printStackTrace(); // Should never happen.
716 }
717 }
718
719 // No line wrap - we can simply check the line number (quicker).
720 else {
721// Document doc = getDocument();
722// if (doc!=null) {
723// Element map = doc.getDefaultRootElement();
724// int caretLine = map.getElementIndex(dot);
725// Rectangle alloc = ((RTextAreaUI)getUI()).
726// getVisibleEditorRect();
727// if (alloc!=null)
728// currentCaretY = alloc.y + caretLine*lineHeight;
729// }
730// Modified for code folding requirements
731try {
732 Rectangle temp = modelToView(dot);
733 if (temp!=null) {
734 currentCaretY = temp.y;
735 }
736} catch (BadLocationException ble) {
737 ble.printStackTrace(); // Should never happen.
738}
739 }
740
741 // Repaint current line (to fill in entire highlight), and old line
742 // (to erase entire old highlight) if necessary. Always repaint
743 // current line in case selection is added or removed.
744 repaint(0,currentCaretY, width,lineHeight);
745 if (previousCaretY!=currentCaretY) {
746 repaint(0,previousCaretY, width,lineHeight);
747 }
748
749 previousCaretY = currentCaretY;
750
751 }
752
753
754 /**
755 * Overridden so we can tell when the text area is resized and update the
756 * current-line highlight, if necessary (i.e., if it is enabled and if
757 * lineWrap is enabled.
758 *
759 * @param e The component event about to be sent to all registered
760 * <code>ComponentListener</code>s.
761 */
762 protected void processComponentEvent(ComponentEvent e) {
763
764 // In line wrap mode, resizing the text area means that the caret's
765 // "line" could change - not to a different logical line, but a
766 // different physical one. So, here we force a repaint of the current
767 // line's highlight if necessary.
768 if (e.getID()==ComponentEvent.COMPONENT_RESIZED &&
769 getLineWrap()==true && getHighlightCurrentLine()) {
770 previousCaretY = -1; // So we are sure to repaint.
771 fireCaretUpdate(mouseListener);
772 }
773
774 super.processComponentEvent(e);
775
776 }
777
778
779 /**
780 * Sets the background color of this text editor. Note that this is
781 * equivalent to calling <code>setBackgroundObject(bg)</code>.
782 *
783 * NOTE: the opaque property is set to <code>true</code> when the
784 * background is set to a color (by this method). When an image is used
785 * for the background, opaque is set to false. This is because
786 * we perform better when setOpaque is true, but if we use an
787 * image for the background when opaque is true, we get on-screen
788 * garbage when the user scrolls via the arrow keys. Thus we
789 * need setOpaque to be false in that case.<p>
790 * You never have to change the opaque property yourself; it is always done
791 * for you.
792 *
793 * @param bg The color to use as the background color.
794 */
795 public void setBackground(Color bg) {
796 Object oldBG = getBackgroundObject();
797 if (oldBG instanceof Color) { // Just change color of strategy.
798 ((ColorBackgroundPainterStrategy)backgroundPainter).
799 setColor(bg);
800 }
801 else { // Was an image painter...
802 backgroundPainter = new ColorBackgroundPainterStrategy(bg);
803 }
804 setOpaque(true);
805 firePropertyChange("background", oldBG, bg);
806 repaint();
807 }
808
809
810 /**
811 * Sets this image as the background image. This method fires a
812 * property change event of type {@link #BACKGROUND_IMAGE_PROPERTY}.<p>
813 *
814 * NOTE: the opaque property is set to <code>true</code> when the
815 * background is set to a color. When an image is used for the
816 * background (by this method), opaque is set to false. This is because
817 * we perform better when setOpaque is true, but if we use an
818 * image for the background when opaque is true, we get on-screen
819 * garbage when the user scrolls via the arrow keys. Thus we
820 * need setOpaque to be false in that case.<p>
821 * You never have to change the opaque property yourself; it is always done
822 * for you.
823 *
824 * @param image The image to use as this text area's background.
825 * @see #getBackgroundImage
826 */
827 public void setBackgroundImage(Image image) {
828 Object oldBG = getBackgroundObject();
829 if (oldBG instanceof Image) { // Just change image being displayed.
830 ((BufferedImageBackgroundPainterStrategy)backgroundPainter).
831 setImage(image);
832 }
833 else { // Was a color strategy...
834 BufferedImageBackgroundPainterStrategy strategy =
835 new BufferedImageBackgroundPainterStrategy(this);
836 strategy.setImage(image);
837 backgroundPainter = strategy;
838 }
839 setOpaque(false);
840 firePropertyChange(BACKGROUND_IMAGE_PROPERTY, oldBG, image);
841 repaint();
842 }
843
844
845 /**
846 * Makes the background into this <code>Object</code>.
847 *
848 * @param newBackground The <code>java.awt.Color</code> or
849 * <code>java.awt.Image</code> object. If <code>newBackground</code>
850 * is not either of these, the background is set to plain white.
851 */
852 public void setBackgroundObject(Object newBackground) {
853 if (newBackground instanceof Color) {
854 setBackground((Color)newBackground);
855 }
856 else if (newBackground instanceof Image) {
857 setBackgroundImage((Image)newBackground);
858 }
859 else {
860 setBackground(Color.WHITE);
861 }
862
863 }
864
865
866 /*
867 * TODO: Figure out why RTextArea doesn't work with RTL (e.g. Arabic)
868 * and fix it!
869 */
870// public void setComponentOrientation(ComponentOrientation o) {
871// if (!o.isLeftToRight()) {
872// o = ComponentOrientation.LEFT_TO_RIGHT;
873// }
874// super.setComponentOrientation(o);
875// }
876
877
878 /**
879 * Sets the color to use to highlight the current line. Note that if
880 * highlighting the current line is turned off, you will not be able to
881 * see this highlight. This method fires a property change of type
882 * {@link #CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY}.
883 *
884 * @param color The color to use to highlight the current line.
885 * @throws NullPointerException if <code>color</code> is <code>null</code>.
886 * @see #getHighlightCurrentLine()
887 * @see #setHighlightCurrentLine(boolean)
888 * @see #getCurrentLineHighlightColor
889 */
890 public void setCurrentLineHighlightColor(Color color) {
891 if (color==null)
892 throw new NullPointerException();
893 if (!color.equals(currentLineColor)) {
894 Color old = currentLineColor;
895 currentLineColor = color;
896 firePropertyChange(CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY,
897 old, color);
898 }
899 }
900
901
902 /**
903 * Sets whether the current line highlight should have a "fade" effect.
904 * This method fires a property change event of type
905 * <code>CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY</code>.
906 *
907 * @param fade Whether the fade effect should be enabled.
908 * @see #getFadeCurrentLineHighlight
909 */
910 public void setFadeCurrentLineHighlight(boolean fade) {
911 if (fade!=fadeCurrentLineHighlight) {
912 fadeCurrentLineHighlight = fade;
913 if (getHighlightCurrentLine())
914 forceCurrentLineHighlightRepaint();
915 firePropertyChange(CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY,
916 !fade, fade);
917 }
918 }
919
920
921 /**
922 * Sets the font for this text area. This is overridden only so that we
923 * can update the size of the "current line highlight" and the location of
924 * the "margin line," if necessary.
925 *
926 * @param font The font to use for this text component.
927 */
928 public void setFont(Font font) {
929 super.setFont(font);
930 updateMarginLineX();
931 if (highlightCurrentLine)
932 possiblyUpdateCurrentLineHighlightLocation();
933 }
934
935
936 /**
937 * Sets whether or not the current line is highlighted. This method
938 * fires a property change of type {@link #HIGHLIGHT_CURRENT_LINE_PROPERTY}.
939 *
940 * @param highlight Whether or not to highlight the current line.
941 * @see #getHighlightCurrentLine()
942 * @see #getCurrentLineHighlightColor
943 * @see #setCurrentLineHighlightColor
944 */
945 public void setHighlightCurrentLine(boolean highlight) {
946 if (highlight!=highlightCurrentLine) {
947 highlightCurrentLine = highlight;
948 firePropertyChange(HIGHLIGHT_CURRENT_LINE_PROPERTY,
949 !highlight, highlight);
950 repaint(); // Repaint entire width of line.
951 }
952 }
953
954
955 /**
956 * Sets whether or not word wrap is enabled. This is overridden so that
957 * the "current line highlight" gets updated if it needs to be.
958 *
959 * @param wrap Whether or not word wrap should be enabled.
960 */
961 public void setLineWrap(boolean wrap) {
962 super.setLineWrap(wrap);
963 forceCurrentLineHighlightRepaint();
964 }
965
966
967 /**
968 * Sets the color used to paint the margin line.
969 *
970 * @param color The new margin line color.
971 * @see #getDefaultMarginLineColor()
972 * @see #getMarginLineColor()
973 */
974 public void setMarginLineColor(Color color) {
975 marginLineColor = color;
976 if (marginLineEnabled) {
977 Rectangle visibleRect = getVisibleRect();
978 repaint(marginLineX,visibleRect.y, 1,visibleRect.height);
979 }
980 }
981
982
983 /**
984 * Enables or disables the margin line.
985 *
986 * @param enabled Whether or not the margin line should be enabled.
987 * @see #isMarginLineEnabled
988 */
989 public void setMarginLineEnabled(boolean enabled) {
990 if (enabled!=marginLineEnabled) {
991 marginLineEnabled = enabled;
992 if (marginLineEnabled) {
993 Rectangle visibleRect = getVisibleRect();
994 repaint(marginLineX,visibleRect.y, 1,visibleRect.height);
995 }
996 }
997 }
998
999
1000 /**
1001 * Sets the number of 'm' widths the margin line is over.
1002 *
1003 * @param size The margin size.
1004 * #see #getDefaultMarginLinePosition
1005 * @see #getMarginLinePosition
1006 */
1007 public void setMarginLinePosition(int size) {
1008 marginSizeInChars = size;
1009 if (marginLineEnabled) {
1010 Rectangle visibleRect = getVisibleRect();
1011 repaint(marginLineX,visibleRect.y, 1,visibleRect.height);
1012 updateMarginLineX();
1013 repaint(marginLineX,visibleRect.y, 1,visibleRect.height);
1014 }
1015 }
1016
1017
1018 /**
1019 * Sets whether the edges of selections are rounded in this text area.
1020 * This method fires a property change of type
1021 * {@link #ROUNDED_SELECTION_PROPERTY}.
1022 *
1023 * @param rounded Whether selection edges should be rounded.
1024 * @see #getRoundedSelectionEdges()
1025 */
1026 public void setRoundedSelectionEdges(boolean rounded) {
1027 if (roundedSelectionEdges!=rounded) {
1028 roundedSelectionEdges = rounded;
1029 ConfigurableCaret cc = (ConfigurableCaret)getCaret();
1030 cc.setRoundedSelectionEdges(rounded);
1031 if (cc.getDot()!=cc.getMark()) { // ie, if there is a selection
1032 repaint();
1033 }
1034 firePropertyChange(ROUNDED_SELECTION_PROPERTY, !rounded,
1035 rounded);
1036 }
1037 }
1038
1039
1040 /**
1041 * Sets the UI for this <code>RTextArea</code>. Note that, for instances
1042 * of <code>RTextArea</code>, <code>setUI</code> only updates the popup
1043 * menu; this is because <code>RTextArea</code>s' look and feels are
1044 * independent of the Java Look and Feel. This method is here so
1045 * subclasses can set a UI (subclass of <code>RTextAreaUI</code>) if they
1046 * have to.
1047 *
1048 * @param ui The new UI.
1049 * @see #setUI
1050 */
1051 protected void setRTextAreaUI(RTextAreaUI ui) {
1052
1053 super.setUI(ui);
1054
1055 // Workaround as setUI makes the text area opaque, even if we don't
1056 // want it to be.
1057 setOpaque(getBackgroundObject() instanceof Color);
1058
1059 }
1060
1061
1062 /**
1063 * Changes whether or not tabs should be emulated with spaces (i.e., soft
1064 * tabs). Note that this affects all tabs inserted AFTER this call, not
1065 * tabs already in the document. For that, see
1066 * {@link #convertTabsToSpaces} and {@link #convertSpacesToTabs}.
1067 *
1068 * @param areEmulated Whether or not tabs should be emulated with spaces.
1069 * @see #convertSpacesToTabs
1070 * @see #convertTabsToSpaces
1071 * @see #getTabsEmulated
1072 */
1073 public void setTabsEmulated(boolean areEmulated) {
1074 tabsEmulatedWithSpaces = areEmulated;
1075 }
1076
1077
1078 /**
1079 * Workaround, since in JDK1.4 it appears that <code>setTabSize()</code>
1080 * doesn't work for a <code>JTextArea</code> unless you use the constructor
1081 * specifying the number of rows and columns...<p>
1082 * Sets the number of characters to expand tabs to. This will be multiplied
1083 * by the maximum advance for variable width fonts. A PropertyChange event
1084 * ("tabSize") is fired when the tab size changes.
1085 *
1086 * @param size Number of characters to expand to.
1087 */
1088 public void setTabSize(int size) {
1089 super.setTabSize(size);
1090 boolean b = getLineWrap();
1091 setLineWrap(!b);
1092 setLineWrap(b);
1093 }
1094
1095
1096 /**
1097 * This is here so subclasses such as <code>RSyntaxTextArea</code> that
1098 * have multiple fonts can define exactly what it means, for example, for
1099 * the margin line to be "80 characters" over.
1100 */
1101 protected void updateMarginLineX() {
1102 marginLineX = getFontMetrics(getFont()).charWidth('m') *
1103 marginSizeInChars;
1104 }
1105
1106
1107 /**
1108 * Returns the y-coordinate of the specified line.
1109 *
1110 * @param line The line number.
1111 * @return The y-coordinate of the top of the line, or <code>-1</code> if
1112 * this text area doesn't yet have a positive size or the line is
1113 * hidden (i.e. from folding).
1114 * @throws BadLocationException If <code>line</code> isn't a valid line
1115 * number for this document.
1116 */
1117 public int yForLine(int line) throws BadLocationException {
1118 return ((RTextAreaUI)getUI()).yForLine(line);
1119 }
1120
1121
1122 /**
1123 * Returns the y-coordinate of the line containing an offset.
1124 *
1125 * @param offs The offset info the document.
1126 * @return The y-coordinate of the top of the offset, or <code>-1</code> if
1127 * this text area doesn't yet have a positive size or the line is
1128 * hidden (i.e. from folding).
1129 * @throws BadLocationException If <code>offs</code> isn't a valid offset
1130 * into the document.
1131 */
1132 public int yForLineContaining(int offs) throws BadLocationException {
1133 return ((RTextAreaUI)getUI()).yForLineContaining(offs);
1134 }
1135
1136
1137 protected class RTAMouseListener extends CaretEvent implements
1138 MouseListener, MouseMotionListener, FocusListener {
1139
1140 RTAMouseListener(RTextAreaBase textArea) {
1141 super(textArea);
1142 }
1143
1144 public void focusGained(FocusEvent e) {}
1145 public void focusLost(FocusEvent e) {}
1146 public void mouseDragged(MouseEvent e) {}
1147 public void mouseMoved(MouseEvent e) {}
1148 public void mouseClicked(MouseEvent e) {}
1149 public void mousePressed(MouseEvent e) {}
1150 public void mouseReleased(MouseEvent e) {}
1151 public void mouseEntered(MouseEvent e) {}
1152 public void mouseExited(MouseEvent e) {}
1153
1154 public int getDot() {
1155 return dot;
1156 }
1157
1158 public int getMark() {
1159 return mark;
1160 }
1161
1162 protected int dot;
1163 protected int mark;
1164
1165 }
1166
1167
1168}
Note: See TracBrowser for help on using the repository browser.