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