-
Notifications
You must be signed in to change notification settings - Fork 268
Expand file tree
/
Copy pathRTextAreaUI.java
More file actions
618 lines (513 loc) · 18.2 KB
/
Copy pathRTextAreaUI.java
File metadata and controls
618 lines (513 loc) · 18.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
/*
* 04/25/2007
*
* RTextAreaUI.java - UI used by instances of RTextArea.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rtextarea;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.border.Border;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
/**
* The UI used by instances of <code>RTextArea</code>. This UI takes into
* account all of the "extras" involved in an <code>RTextArea</code>, including
* having a special caret (for insert and overwrite), background images,
* highlighting the current line, etc.
*
* @author Robert Futrell
* @version 0.5
*/
public class RTextAreaUI extends BasicTextAreaUI {
private static final String SHARED_ACTION_MAP_NAME = "RTextAreaUI.actionMap";
private static final String SHARED_INPUT_MAP_NAME = "RTextAreaUI.inputMap";
protected RTextArea textArea; // The text area for which we are the UI.
private static final EditorKit defaultKit = new RTextAreaEditorKit();
private static final TransferHandler defaultTransferHandler =
new RTATextTransferHandler();
private static final String RTEXTAREA_KEYMAP_NAME = "RTextAreaKeymap";
/**
* Creates a UI for an RTextArea.
*
* @param textArea A text area.
* @return The UI.
*/
public static ComponentUI createUI(JComponent textArea) {
return new RTextAreaUI(textArea);
}
/**
* Constructor.
*
* @param textArea An instance of <code>RTextArea</code>.
* @throws IllegalArgumentException If <code>textArea</code> is not an
* instance of <code>RTextArea</code>.
*/
public RTextAreaUI(JComponent textArea) {
if (!(textArea instanceof RTextArea)) {
throw new IllegalArgumentException("RTextAreaUI is for " +
"instances of RTextArea only!");
}
this.textArea = (RTextArea)textArea;
}
/**
* The Nimbus LAF (and any Synth laf might have similar issues) doesn't set
* many UIManager properties that BasicLAF UI's look for. This causes
* problems for custom Basic-based UI's such as RTextAreaUI. This method
* attempts to detect if Nimbus has been installed, and if so, sets proper
* values for some editor properties.
*
* @param editor The text area.
*/
private void correctNimbusDefaultProblems(JTextComponent editor) {
// Don't check UIManager.getLookAndFeel().getName() for "Nimbus",
// as other Synth-based LaFs might have not set these properties,
// in which case we'll need to use our fallback values.
// Check for null, but not for UIResource, for these properties,
// because if Nimbus was installed these values would all be given
// null values. Another laf might have successfully installed
// UIResource values, which we don't want to override.
Color c = editor.getCaretColor();
if (c==null) {
editor.setCaretColor(RTextArea.getDefaultCaretColor());
}
c = editor.getSelectionColor();
if (c==null) {
c = UIManager.getColor("nimbusSelectionBackground");
if (c==null) { // Not Nimbus, but still need a value - fallback
c = UIManager.getColor("textHighlight");
if (c==null) {
c = new ColorUIResource(Color.BLUE);
}
}
editor.setSelectionColor(c);
}
c = editor.getSelectedTextColor();
if (c==null) {
c = UIManager.getColor("nimbusSelectedText");
if (c==null) { // Not Nimbus, but still need a value - fallback
c = UIManager.getColor("textHighlightText");
if (c==null) {
c = new ColorUIResource(Color.WHITE);
}
}
editor.setSelectedTextColor(c);
}
c = editor.getDisabledTextColor();
if (c==null) {
c = UIManager.getColor("nimbusDisabledText");
if (c==null) { // Not Nimbus, but still need a value - fallback
c = UIManager.getColor("textInactiveText");
if (c==null) {
c = new ColorUIResource(Color.DARK_GRAY);
}
}
editor.setDisabledTextColor(c);
}
Border border = editor.getBorder();
if (border==null) {
editor.setBorder(new BasicBorders.MarginBorder());
}
Insets margin = editor.getMargin();
if (margin==null) {
editor.setMargin(new InsetsUIResource(2, 2, 2, 2));
}
}
/**
* Creates the view for an element. Returns a WrappedPlainView or
* PlainView.
*
* @param elem The element.
* @return The view.
*/
@Override
public View create(Element elem) {
if (textArea.getLineWrap())
return new WrappedPlainView(elem, textArea.getWrapStyleWord());
else
return new PlainView(elem);
}
/**
* Returns the default caret for an <code>RTextArea</code>. This caret is
* capable of displaying itself differently for insert/overwrite modes.
*
* @return The caret.
*/
@Override
protected Caret createCaret() {
Caret caret = new ConfigurableCaret();
caret.setBlinkRate(500);
return caret;
}
/**
* Creates the keymap for this text area. This takes the super class's
* keymap, but sets the default keystroke to be RTextAreaEditorKit's
* DefaultKeyTypedAction. This must be done to override the default
* keymap's default key-typed action.
*
* @return The keymap.
*/
@Override
protected Keymap createKeymap() {
// Load the keymap we'll be using (it's saved by
// JTextComponent.addKeymap).
Keymap map = JTextComponent.getKeymap(RTEXTAREA_KEYMAP_NAME);
if (map==null) {
Keymap parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
map = JTextComponent.addKeymap(RTEXTAREA_KEYMAP_NAME, parent);
map.setDefaultAction(new RTextAreaEditorKit.DefaultKeyTypedAction());
}
return map;
}
/**
* Creates a default action map. This action map contains actions for all
* basic text area work - cut, copy, paste, select, caret motion, etc.<p>
*
* This isn't named <code>createActionMap()</code> because there is a
* package-private member by that name in <code>BasicTextAreaUI</code>,
* and some compilers will give warnings that we are not overriding that
* method since it is package-private.
*
* @return The action map.
*/
protected ActionMap createRTextAreaActionMap() {
// Get the actions of the text area (which in turn gets them from its
// DefaultEditorKit).
ActionMap map = new ActionMapUIResource();
Action[] actions = textArea.getActions();
int n = actions.length;
for (int i = 0; i < n; i++) {
Action a = actions[i];
map.put(a.getValue(Action.NAME), a);
}
// Not sure if we need these; not sure they are ever called
// (check their NAMEs).
map.put(TransferHandler.getCutAction().getValue(Action.NAME),
TransferHandler.getCutAction());
map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
TransferHandler.getCopyAction());
map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
TransferHandler.getPasteAction());
return map;
}
/**
* Returns the name to use to cache/fetch the shared action map. This
* should be overridden by subclasses if the subclass has its own custom
* editor kit to install, so its actions get picked up.
*
* @return The name of the cached action map.
*/
protected String getActionMapName() {
return SHARED_ACTION_MAP_NAME;
}
/**
* Fetches the EditorKit for the UI.
*
* @param tc the text component for which this UI is installed
* @return the editor capabilities
* @see TextUI#getEditorKit
*/
@Override
public EditorKit getEditorKit(JTextComponent tc) {
return defaultKit;
}
/**
* Returns the text area for which we are the UI.
*
* @return The text area.
*/
public RTextArea getRTextArea() {
return textArea;
}
/**
* Returns an action map to use by a text area.<p>
*
* This method is not named <code>getActionMap()</code> because there is
* a package-private method in <code>BasicTextAreaUI</code> with that name.
* Thus, creating a new method with that name causes certain compilers to
* issue warnings that you are not actually overriding the original method
* (since it is package-private).
*
* @return The action map.
* @see #createRTextAreaActionMap()
*/
private ActionMap getRTextAreaActionMap() {
// Get the UIManager-cached action map; if this is the first
// RTextArea created, create the action map and cache it.
ActionMap map = (ActionMap)UIManager.get(getActionMapName());
if (map==null) {
map = createRTextAreaActionMap();
UIManager.put(getActionMapName(), map);
}
ActionMap componentMap = new ActionMapUIResource();
componentMap.put("requestFocus", new FocusAction());
if (map != null)
componentMap.setParent(map);
return componentMap;
}
/**
* Get the InputMap to use for the UI.<p>
*
* This method is not named <code>getInputMap()</code> because there is
* a package-private method in <code>BasicTextAreaUI</code> with that name.
* Thus, creating a new method with that name causes certain compilers to
* issue warnings that you are not actually overriding the original method
* (since it is package-private).
*/
protected InputMap getRTextAreaInputMap() {
InputMap map = new InputMapUIResource();
InputMap shared = (InputMap)UIManager.get(SHARED_INPUT_MAP_NAME);
if (shared==null) {
shared = new RTADefaultInputMap();
UIManager.put(SHARED_INPUT_MAP_NAME, shared);
}
//KeyStroke[] keys = shared.allKeys();
//for (int i=0; i<keys.length; i++)
// System.err.println(keys[i] + " -> " + shared.get(keys[i]));
map.setParent(shared);
return map;
}
/**
* Gets the allocation to give the root View. Due
* to an unfortunate set of historical events this
* method is inappropriately named. The Rectangle
* returned has nothing to do with visibility.
* The component must have a non-zero positive size for
* this translation to be computed.
*
* @return the bounding box for the root view
*/
@Override
protected Rectangle getVisibleEditorRect() {
Rectangle alloc = textArea.getBounds();
if ((alloc.width > 0) && (alloc.height > 0)) {
alloc.x = alloc.y = 0;
Insets insets = textArea.getInsets();
alloc.x += insets.left;
alloc.y += insets.top;
alloc.width -= insets.left + insets.right;
alloc.height -= insets.top + insets.bottom;
return alloc;
}
return null;
}
@Override
protected void installDefaults() {
super.installDefaults();
JTextComponent editor = getComponent();
editor.setFont(RTextAreaBase.getDefaultFont());
// Nimbus (and possibly other Synth lafs) doesn't play by BasicLaf
// rules and doesn't set properties needed by custom BasicTextAreaUI's.
correctNimbusDefaultProblems(editor);
editor.setTransferHandler(defaultTransferHandler);
}
/**
* {@inheritDoc}
*/
@Override
protected void installKeyboardActions() {
// NOTE: Don't call super.installKeyboardActions(), as that causes
// JTextAreas to stop responding to certain keystrokes if an RTextArea
// is the first-instantiated text area. This is because of the code
// path installKeyboardActions() -> getActionMap() -> createActionMap().
// In BasicTextUI#createActionMap(), "editor.getActions()" is called,
// and the current editor's returned Actions are used to create the
// ActionMap, which is then cached and used in all future J/RTextAreas.
// Unfortunately, RTextArea actions don't worn in JTextAreas.
//super.installKeyboardActions();
RTextArea textArea = getRTextArea();
// backward compatibility support... keymaps for the UI
// are now installed in the more friendly input map.
textArea.setKeymap(createKeymap());
// Since BasicTextUI.getInputMap() is package-private, instead use
// our own version here.
InputMap map = getRTextAreaInputMap();
SwingUtilities.replaceUIInputMap(textArea,JComponent.WHEN_FOCUSED,map);
// Same thing here with action map.
ActionMap am = getRTextAreaActionMap();
if (am!=null) {
SwingUtilities.replaceUIActionMap(textArea, am);
}
}
/**
* Installs this UI to the given text component.
*/
@Override
public void installUI(JComponent c) {
if (!(c instanceof RTextArea)) {
throw new Error("RTextAreaUI needs an instance of RTextArea!");
}
super.installUI(c);
}
@Override
protected void paintBackground(Graphics g) {
// Only fill in the background if an image isn't being used.
Color bg = textArea.getBackground();
if (bg!=null) {
g.setColor(bg);
//g.fillRect(0, 0, textArea.getWidth(), textArea.getHeight());
Rectangle r = g.getClipBounds();
g.fillRect(r.x,r.y, r.width,r.height);
}
paintEditorAugmentations(g);
}
/**
* Paints the highlighted current line, if it is enabled.
*
* @param g The graphics context with which to paint.
* @param visibleRect The visible rectangle of the text area.
*/
protected void paintCurrentLineHighlight(Graphics g, Rectangle visibleRect) {
if (textArea.getHighlightCurrentLine()) {
Caret caret = textArea.getCaret();
if (caret.getDot()==caret.getMark()) {
Color highlight = textArea.getCurrentLineHighlightColor();
// NOTE: We use the getLineHeight() method below instead
// of currentCaretRect.height because of a bug where
// currentCaretRect.height is incorrect when an RSyntaxTextArea
// is first displayed (it is initialized with the text area's
// font.getHeight() (via RTextArea), but isn't changed to
// account for the syntax styles before it is displayed).
//int height = textArea.currentCaretRect.height);
int height = textArea.getLineHeight();
if (textArea.getFadeCurrentLineHighlight()) {
Graphics2D g2d = (Graphics2D)g;
Color bg = textArea.getBackground();
GradientPaint paint = new GradientPaint(
visibleRect.x,0, highlight,
visibleRect.x+visibleRect.width,0,
bg==null ? Color.WHITE : bg);
g2d.setPaint(paint);
g2d.fillRect(visibleRect.x,textArea.currentCaretY,
visibleRect.width, height);
}
else {
g.setColor(highlight);
g.fillRect(visibleRect.x,textArea.currentCaretY,
visibleRect.width, height);
}
}
}
}
/**
* Paints editor augmentations added by RTextArea: highlighted lines,
* current line highlight, and margin line.
*
* @param g The graphics context with which to paint.
*/
protected void paintEditorAugmentations(Graphics g) {
Rectangle visibleRect = textArea.getVisibleRect();
boolean first = textArea.getPaintCurrentLineHighlightFirst();
if (first) {
paintCurrentLineHighlight(g, visibleRect);
}
paintLineHighlights(g);
if (!first) {
paintCurrentLineHighlight(g, visibleRect);
}
paintMarginLine(g, visibleRect);
}
/**
* Paints any line highlights.
*
* @param g The graphics context.
*/
protected void paintLineHighlights(Graphics g) {
LineHighlightManager lhm = textArea.getLineHighlightManager();
if (lhm!=null) {
lhm.paintLineHighlights(g);
}
}
/**
* Draws the "margin line" if enabled.
*
* @param g The graphics context to paint with.
* @param visibleRect The visible rectangle of this text area.
*/
protected void paintMarginLine(Graphics g, Rectangle visibleRect) {
if (textArea.isMarginLineEnabled()) {
g.setColor(textArea.getMarginLineColor());
Insets insets = textArea.getInsets();
int marginLineX = textArea.getMarginLinePixelLocation() +
(insets==null ? 0 : insets.left);
g.drawLine(marginLineX,visibleRect.y,
marginLineX,visibleRect.y+visibleRect.height);
}
}
@Override
protected void paintSafely(Graphics g) {
// Paint editor augmentations if editor is not opaque because
// paintBackground() is not called in this case
if (!textArea.isOpaque()) {
paintEditorAugmentations(g);
}
super.paintSafely(g);
}
/**
* Returns the y-coordinate of the specified line.<p>
*
* The default implementation is equivalent to:
* <pre>
* int startOffs = textArea.getLineStartOffset(line);
* return yForLineContaining(startOffs);
* </pre>
*
* Subclasses that can calculate this value more quickly than traditional
* {@link #modelToView(JTextComponent, int)} calls should override this
* method to do so. This method may be used when the entire bounding box
* isn't needed, to speed up rendering.
*
* @param line The line number.
* @return The y-coordinate of the top of the line, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException If <code>line</code> isn't a valid line
* number for this document.
*/
public int yForLine(int line) throws BadLocationException {
int startOffs = textArea.getLineStartOffset(line);
return yForLineContaining(startOffs);
}
/**
* Returns the y-coordinate of the line containing an offset.<p>
*
* The default implementation is equivalent to:
* <pre>
* int line = textArea.getLineOfOffset(offs);
* int startOffs = textArea.getLineStartOffset(line);
* return modelToView(startOffs).y;
* </pre>
*
* Subclasses that can calculate this value more quickly than traditional
* {@link #modelToView(JTextComponent, int)} calls should override this
* method to do so. This method may be used when the entire bounding box
* isn't needed, to speed up rendering.
*
* @param offs The offset info the document.
* @return The y-coordinate of the top of the offset, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException If <code>offs</code> isn't a valid offset
* into the document.
*/
public int yForLineContaining(int offs) throws BadLocationException {
Rectangle r = modelToView(textArea, offs);
return r!=null ? r.y : -1;
}
/**
* Registered in the ActionMap.
*/
class FocusAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
textArea.requestFocus();
}
@Override
public boolean isEnabled() {
return textArea.isEditable();
}
}
}