日本語テキストの入力がインラインで行われるように調整
@@ -0,0 +1,340 @@ | ||
1 | +/* | |
2 | + * AWTResources.java | |
3 | + * | |
4 | + * Copyright (c) 1997, 1998, 1999 Kazuki YASUMATSU. All Rights Reserved. | |
5 | + * | |
6 | + * Permission to use, copy, modify, and distribute this software and its | |
7 | + * documentation for any purpose and without fee or royalty is hereby | |
8 | + * granted, provided that both the above copyright notice and this | |
9 | + * permission notice appear in all copies of the software and | |
10 | + * documentation or portions thereof, including modifications, that you | |
11 | + * make. | |
12 | + * | |
13 | + * THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO | |
14 | + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, | |
15 | + * BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR | |
16 | + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR | |
17 | + * THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY | |
18 | + * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. | |
19 | + * COPYRIGHT HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE | |
20 | + * OR DOCUMENTATION. | |
21 | + */ | |
22 | + | |
23 | +package jp.kyasu.awt; | |
24 | + | |
25 | +import jp.kyasu.graphics.VImage; | |
26 | + | |
27 | +import java.awt.Color; | |
28 | +import java.awt.Component; | |
29 | +import java.awt.Container; | |
30 | +import java.awt.Font; | |
31 | + | |
32 | +/** | |
33 | + * The <code>AWTResources</code> class provides the resources shared in | |
34 | + * this package. | |
35 | + * | |
36 | + * @version 20 Nov 1999 | |
37 | + * @author Kazuki YASUMATSU | |
38 | + */ | |
39 | +public class AWTResources { | |
40 | + | |
41 | + // ======== Resource Handling ======== | |
42 | + | |
43 | + static final protected jp.kyasu.util.Resources Resources = | |
44 | + new jp.kyasu.util.Resources("jp.kyasu.awt.resources.awt"); | |
45 | + | |
46 | + /** | |
47 | + * Returns the resource string indicated by the specified key. | |
48 | + * | |
49 | + * @param key the name of the resource. | |
50 | + * @return the string value of the resource, or <code>null</code> | |
51 | + * if there is no resource with that key. | |
52 | + */ | |
53 | + static public String getResourceString(String key) { | |
54 | + return Resources.getResourceString(key); | |
55 | + } | |
56 | + | |
57 | + /** | |
58 | + * Returns the resource string indicated by the specified key. | |
59 | + * | |
60 | + * @param key the name of the resource. | |
61 | + * @param def a default value. | |
62 | + * @return the string value of the resource, or the default value | |
63 | + * if there is no resource with that key. | |
64 | + */ | |
65 | + static public String getResourceString(String key, String def) { | |
66 | + return Resources.getResourceString(key, def); | |
67 | + } | |
68 | + | |
69 | + /** | |
70 | + * Returns the resource integer indicated by the specified key. | |
71 | + * | |
72 | + * @param key the name of the resource. | |
73 | + * @param def a default integer. | |
74 | + * @return the integer value of the resource, or the default integer | |
75 | + * if there is no resource with that key. | |
76 | + */ | |
77 | + static public int getResourceInteger(String key, int def) { | |
78 | + return Resources.getResourceInteger(key, def); | |
79 | + } | |
80 | + | |
81 | + /** | |
82 | + * Returns the resource boolean indicated by the specified key. | |
83 | + * | |
84 | + * @param key the name of the resource. | |
85 | + * @param def a default boolean. | |
86 | + * @return the boolean value of the resource, or the default boolean | |
87 | + * if there is no resource with that key. | |
88 | + */ | |
89 | + static public boolean getResourceBoolean(String key, boolean def) { | |
90 | + return Resources.getResourceBoolean(key, def); | |
91 | + } | |
92 | + | |
93 | + /** | |
94 | + * Returns the resource color indicated by the specified key. | |
95 | + * | |
96 | + * @param key the name of the resource. | |
97 | + * @param def a default color. | |
98 | + * @return the color value of the resource, or the default color | |
99 | + * if there is no resource with that key. | |
100 | + */ | |
101 | + static public Color getResourceColor(String key, Color def) { | |
102 | + return Resources.getResourceColor(key, def); | |
103 | + } | |
104 | + | |
105 | + /** The default null icon. */ | |
106 | + static protected VImage NULL_ICON = new VImage(new byte[]{ | |
107 | + (byte)0x47, (byte)0x49, (byte)0x46, (byte)0x38, (byte)0x39, | |
108 | + (byte)0x61, (byte)0x10, (byte)0x00, (byte)0x10, (byte)0x00, | |
109 | + (byte)0x80, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, | |
110 | + (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x21, | |
111 | + (byte)0xF9, (byte)0x04, (byte)0x01, (byte)0x00, (byte)0x00, | |
112 | + (byte)0x00, (byte)0x00, (byte)0x2C, (byte)0x00, (byte)0x00, | |
113 | + (byte)0x00, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x10, | |
114 | + (byte)0x00, (byte)0x00, (byte)0x02, (byte)0x0E, (byte)0x84, | |
115 | + (byte)0x8F, (byte)0xA9, (byte)0xCB, (byte)0xED, (byte)0x0F, | |
116 | + (byte)0xA3, (byte)0x9C, (byte)0xB4, (byte)0xDA, (byte)0x8B, | |
117 | + (byte)0xB3, (byte)0x3E, (byte)0x05, (byte)0x00, (byte)0x3B | |
118 | + }); | |
119 | + | |
120 | + /** | |
121 | + * Returns the resource icon indicated by the specified class and file. | |
122 | + * | |
123 | + * @param baseClass the base class. | |
124 | + * @param file a file name. | |
125 | + * @return the icon of the resource, or the default null icon | |
126 | + * if there is no resource with that key. | |
127 | + */ | |
128 | + static public VImage getIcon(Class baseClass, String file) { | |
129 | + if (baseClass == null || file == null) | |
130 | + return NULL_ICON; | |
131 | + | |
132 | + /* | |
133 | + java.net.URL url = baseClass.getResource(file); | |
134 | + if (url == null) { | |
135 | + return NULL_ICON; | |
136 | + } | |
137 | + return new VImage(url); | |
138 | + */ | |
139 | + | |
140 | + /* Copy resource into a byte array. This is necessary because | |
141 | + * several browsers consider Class.getResource a security risk | |
142 | + * because it can be used to load additional classes. | |
143 | + * Class.getResourceAsStream just returns raw bytes, which we | |
144 | + * can convert to an image. | |
145 | + */ | |
146 | + try { | |
147 | + java.io.InputStream resource = | |
148 | + baseClass.getResourceAsStream(file); | |
149 | + if (resource == null) { | |
150 | + return NULL_ICON; | |
151 | + } | |
152 | + java.io.BufferedInputStream in = | |
153 | + new java.io.BufferedInputStream(resource); | |
154 | + java.io.ByteArrayOutputStream out = | |
155 | + new java.io.ByteArrayOutputStream(1024); | |
156 | + byte[] buffer = new byte[1024]; | |
157 | + int n; | |
158 | + while ((n = in.read(buffer)) > 0) { | |
159 | + out.write(buffer, 0, n); | |
160 | + } | |
161 | + in.close(); | |
162 | + out.flush(); | |
163 | + buffer = out.toByteArray(); | |
164 | + if (buffer.length == 0) { | |
165 | + return NULL_ICON; | |
166 | + } | |
167 | + return new VImage(buffer); | |
168 | + } | |
169 | + catch (java.io.IOException e) { | |
170 | + return NULL_ICON; | |
171 | + } | |
172 | + } | |
173 | + | |
174 | + | |
175 | + // ======== Default Color ======== | |
176 | + | |
177 | + /** | |
178 | + * The default foreground color. | |
179 | + */ | |
180 | + static public final Color FOREGROUND_COLOR = | |
181 | + getResourceColor("kfc.foreground", Color.black); | |
182 | + | |
183 | + /** | |
184 | + * The default background color. | |
185 | + */ | |
186 | + static public final Color BACKGROUND_COLOR = | |
187 | + getResourceColor("kfc.background", Color.lightGray); | |
188 | + | |
189 | + | |
190 | + // ======== Double Buffer ======== | |
191 | + | |
192 | + /** | |
193 | + * The default double buffer policy. | |
194 | + */ | |
195 | + static public boolean USE_DOUBLE_BUFFER = | |
196 | + (System.getProperty("java.version").compareTo("1.2") >= 0); | |
197 | + | |
198 | + | |
199 | + /*if[JDK1.2]*/ | |
200 | + | |
201 | + // ======== Default Rendering Hint Map ======== | |
202 | + | |
203 | + /** | |
204 | + * The default rendering hint map. | |
205 | + */ | |
206 | + /*if[JDK1.2]*/ | |
207 | + static public final java.util.Map RENDERING_HINTS = new java.util.HashMap(); | |
208 | + | |
209 | + /*end[JDK1.2]*/ | |
210 | + | |
211 | + | |
212 | + // ======== Event Dispatching for Notifying Listeners ======== | |
213 | + | |
214 | + /** | |
215 | + * If true, notifies listeners directly without using the event queue. | |
216 | + */ | |
217 | + static public boolean IS_DIRECT_NOTIFICATION = | |
218 | + (System.getProperty("java.version").compareTo("1.1.5") >= 0) || | |
219 | + getResourceBoolean("kfc.directNotification", false); | |
220 | + | |
221 | + | |
222 | + // ======== JDK Bug Workaround ======== | |
223 | + | |
224 | + /** True if the JVM runs on Windows 95/NT. */ | |
225 | + static protected final boolean ON_WINDOWS = | |
226 | + System.getProperty("os.name").startsWith("Windows"); | |
227 | + | |
228 | + /** | |
229 | + * True if the JDK has the | |
230 | + * <code>sun.awt.windows.WWindowPeer#getFocusPeer()</code> bug. | |
231 | + * <p> | |
232 | + * Because of this bug, if a lightwight component is focus traversable, | |
233 | + * an application will be hung up. | |
234 | + * <p> | |
235 | + * This bug has been fixed from JDK1.1.5. | |
236 | + */ | |
237 | + static public boolean HAS_FOCUS_BUG = | |
238 | + ON_WINDOWS && | |
239 | + (System.getProperty("java.version").compareTo("1.1.4") <= 0); | |
240 | + | |
241 | + /** | |
242 | + * True if a pop-up window can receive the events even when the pop-up | |
243 | + * window is opend within a modal dialog. | |
244 | + */ | |
245 | + static public boolean CAN_OPEN_POPUP_IN_MODAL_DIALOG = ON_WINDOWS; | |
246 | + | |
247 | + /** | |
248 | + * True if the JDK has the <code>java.awt.Graphics#copyArea()</code> bug. | |
249 | + * <p> | |
250 | + * This bug has arised from JDK1.2. | |
251 | + */ | |
252 | + static public boolean HAS_COPY_AREA_BUG = | |
253 | + (System.getProperty("java.version").compareTo("1.2") >= 0); | |
254 | + | |
255 | + /** | |
256 | + * Checks the component state for the specified component. | |
257 | + * <p> | |
258 | + * If <code>HAS_FOCUS_BUG</code> is true, an application should use | |
259 | + * jp.kyasu.awt.{Dialog,Frame,Window} instead of | |
260 | + * java.awt.{Dialog,Frame,Window}. | |
261 | + * <p> | |
262 | + * If a top frame has a menu bar on windows, an application should | |
263 | + * wrap a lightweight component in a native component, so that pop-up | |
264 | + * menu and <code>getLocationOnScreen()</code> work correctly. | |
265 | + * | |
266 | + * @exception java.awt.IllegalComponentStateException if the component | |
267 | + * state is illegal. | |
268 | + */ | |
269 | + static public void checkComponentState(Component comp) { | |
270 | + if (!ON_WINDOWS) | |
271 | + return; | |
272 | + boolean inNativeContainer = false; | |
273 | + for (Container c = comp.getParent(); c != null; c = c.getParent()) { | |
274 | + if (c instanceof java.awt.Window) { | |
275 | + if (HAS_FOCUS_BUG) { | |
276 | + if (!(c instanceof jp.kyasu.awt.Dialog || | |
277 | + c instanceof jp.kyasu.awt.Frame || | |
278 | + c instanceof jp.kyasu.awt.Window)) | |
279 | + { | |
280 | + /* Make applets happy. | |
281 | + throw new java.awt.IllegalComponentStateException( | |
282 | + "jp.kyasu.awt.{Dialog,Frame,Window} should be used instead of java.awt.{Dialog,Frame,Window} for the AWT focusing bug."); | |
283 | + */ | |
284 | + } | |
285 | + } | |
286 | + if (c instanceof java.awt.Frame) { | |
287 | + if (!inNativeContainer && | |
288 | + ((java.awt.Frame)c).getMenuBar() != null) | |
289 | + { | |
290 | + throw new java.awt.IllegalComponentStateException( | |
291 | + "A lightweight component should be wrappend in a native component for the AWT menu bar bug."); | |
292 | + } | |
293 | + } | |
294 | + return; | |
295 | + } | |
296 | + if (!(c.getPeer() instanceof java.awt.peer.LightweightPeer)) { | |
297 | + inNativeContainer = true; | |
298 | + } | |
299 | + } | |
300 | + } | |
301 | + | |
302 | + /** The lock for the foucusing bug workaround. */ | |
303 | + static protected final Object FOCUS_LOCK = new Object(); | |
304 | + | |
305 | + /** The state for the foucusing bug workaround. */ | |
306 | + static protected boolean IN_REQUEST_FOCUS = false; | |
307 | + | |
308 | + /** | |
309 | + * The <code>Window#getFocusOwner()</code> workaround. | |
310 | + * | |
311 | + * @see jp.kyasu.awt.KComponent#requestFocus() | |
312 | + * @see jp.kyasu.awt.KContainer#requestFocus() | |
313 | + */ | |
314 | + static protected Component getFocusOwnerWorkaround(Component focus) { | |
315 | + synchronized (FOCUS_LOCK) { | |
316 | + if (focus == null) { | |
317 | + return null; | |
318 | + } | |
319 | + else if (IN_REQUEST_FOCUS) { | |
320 | + return focus; | |
321 | + } | |
322 | + else { | |
323 | + return getNativeComponent(focus); | |
324 | + } | |
325 | + } | |
326 | + } | |
327 | + | |
328 | + /** | |
329 | + * Returns the native component for the specified component. | |
330 | + */ | |
331 | + static protected Component getNativeComponent(Component c) { | |
332 | + if (!(c.getPeer() instanceof java.awt.peer.LightweightPeer)) | |
333 | + return c; | |
334 | + for (Container p = c.getParent(); p != null; p = p.getParent()) { | |
335 | + if (!(p.getPeer() instanceof java.awt.peer.LightweightPeer)) | |
336 | + return p; | |
337 | + } | |
338 | + return null; | |
339 | + } | |
340 | +} |
@@ -0,0 +1,662 @@ | ||
1 | +/* | |
2 | + * KComponent.java | |
3 | + * | |
4 | + * Copyright (c) 1997, 1998, 1999 Kazuki YASUMATSU. All Rights Reserved. | |
5 | + * | |
6 | + * Permission to use, copy, modify, and distribute this software and its | |
7 | + * documentation for any purpose and without fee or royalty is hereby | |
8 | + * granted, provided that both the above copyright notice and this | |
9 | + * permission notice appear in all copies of the software and | |
10 | + * documentation or portions thereof, including modifications, that you | |
11 | + * make. | |
12 | + * | |
13 | + * THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO | |
14 | + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, | |
15 | + * BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR | |
16 | + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR | |
17 | + * THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY | |
18 | + * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. | |
19 | + * COPYRIGHT HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE | |
20 | + * OR DOCUMENTATION. | |
21 | + */ | |
22 | + | |
23 | +package jp.kyasu.awt; | |
24 | + | |
25 | +import jp.kyasu.graphics.Text; | |
26 | +import jp.kyasu.graphics.Visualizable; | |
27 | + | |
28 | +import java.awt.AWTEvent; | |
29 | +import java.awt.Color; | |
30 | +import java.awt.Component; | |
31 | +import java.awt.Container; | |
32 | +import java.awt.Dimension; | |
33 | +import java.awt.Event; | |
34 | +import java.awt.Graphics; | |
35 | +import java.awt.Image; | |
36 | +import java.awt.Point; | |
37 | +import java.awt.Rectangle; | |
38 | +import java.awt.Toolkit; | |
39 | +import java.awt.event.ActionEvent; | |
40 | +import java.awt.event.AdjustmentEvent; | |
41 | +import java.awt.event.ItemEvent; | |
42 | +import java.awt.event.MouseEvent; | |
43 | + | |
44 | +/** | |
45 | + * A KComponent is an object having a graphical representation | |
46 | + * that can be displayed on the screen and that can interact with the | |
47 | + * user. | |
48 | + * <p> | |
49 | + * The KComponent class is the abstract base class for all components | |
50 | + * in this package. | |
51 | + * | |
52 | + * @version 20 Nov 1999 | |
53 | + * @author Kazuki YASUMATSU | |
54 | + */ | |
55 | +public abstract class KComponent extends java.awt.Component { | |
56 | + /** The double buffering. */ | |
57 | + protected boolean doubleBuffered; | |
58 | + transient protected Image offscreenBuffer; | |
59 | + | |
60 | + /** True if the keyboard focus traversal is enabled. */ | |
61 | + protected boolean focusTraversable; | |
62 | + | |
63 | + static protected Toolkit DefaultToolkit = Toolkit.getDefaultToolkit(); | |
64 | + | |
65 | + | |
66 | + protected KComponent() { | |
67 | + setForeground(AWTResources.FOREGROUND_COLOR); | |
68 | + setBackground(AWTResources.BACKGROUND_COLOR); | |
69 | + doubleBuffered = AWTResources.USE_DOUBLE_BUFFER; | |
70 | + offscreenBuffer = null; | |
71 | + focusTraversable = true; | |
72 | + } | |
73 | + | |
74 | + | |
75 | + /** | |
76 | + * Checks if this component should use a buffer to paint. | |
77 | + */ | |
78 | + public boolean isDoubleBuffered() { | |
79 | + return doubleBuffered; | |
80 | + } | |
81 | + | |
82 | + /** | |
83 | + * Set whether this component should use a buffer to paint. | |
84 | + * If set to true, all the drawing from this component will be done in | |
85 | + * an offscreen painting buffer. The offscreen painting buffer will the | |
86 | + * be copied onto the screen. | |
87 | + */ | |
88 | + public void setDoubleBuffered(boolean b) { | |
89 | + if (doubleBuffered == b) | |
90 | + return; | |
91 | + doubleBuffered = b; | |
92 | + offscreenBuffer = null; | |
93 | + } | |
94 | + | |
95 | + /** | |
96 | + * Checks if the keyboard focus traversal is enabled. | |
97 | + */ | |
98 | + public boolean isFocusTraversable() { | |
99 | + if (focusTraversable) { | |
100 | + return super.isFocusTraversable(); | |
101 | + } | |
102 | + return false; | |
103 | + } | |
104 | + | |
105 | + /** | |
106 | + * Enables or disables the keyboard focus traversal. | |
107 | + */ | |
108 | + public void setFocusTraversable(boolean b) { | |
109 | + focusTraversable = b; | |
110 | + } | |
111 | + | |
112 | + /** | |
113 | + * If true, notifies listeners directly without using the event queue. | |
114 | + */ | |
115 | + public boolean isDirectNotification() { | |
116 | + return AWTResources.IS_DIRECT_NOTIFICATION; | |
117 | + } | |
118 | + | |
119 | + | |
120 | + /** | |
121 | + * Enables or disables this component. | |
122 | + */ | |
123 | + public void setEnabled(boolean b) { | |
124 | + if (b) { | |
125 | + super.enable(); | |
126 | + } | |
127 | + else { | |
128 | + super.disable(); | |
129 | + } | |
130 | + } | |
131 | + | |
132 | + /** Enables this component. */ | |
133 | + public void enable() { setEnabled(true); } | |
134 | + | |
135 | + /* Enables or disables this component. */ | |
136 | + public void enable(boolean b) { setEnabled(b); } | |
137 | + | |
138 | + /** Disables this component. */ | |
139 | + public void disable() { setEnabled(false); } | |
140 | + | |
141 | + | |
142 | + /** | |
143 | + * Shows or hides this component. | |
144 | + */ | |
145 | + public void setVisible(boolean b) { | |
146 | + if (b) { | |
147 | + super.show(); | |
148 | + } | |
149 | + else { | |
150 | + super.hide(); | |
151 | + } | |
152 | + } | |
153 | + | |
154 | + /* Shows this component. */ | |
155 | + public void show() { setVisible(true); } | |
156 | + | |
157 | + /* Shows or hides this component. */ | |
158 | + public void show(boolean b) { setVisible(b); } | |
159 | + | |
160 | + /* Hides this component. */ | |
161 | + public void hide() { setVisible(false); } | |
162 | + | |
163 | + | |
164 | + /** | |
165 | + * Returns the location of this component. | |
166 | + */ | |
167 | + public Point getLocation() { | |
168 | + return super.location(); | |
169 | + } | |
170 | + | |
171 | + /** Returns the location of this component. */ | |
172 | + public Point location() { return getLocation(); } | |
173 | + | |
174 | + | |
175 | + /** | |
176 | + * Returns the size of this component. | |
177 | + */ | |
178 | + public Dimension getSize() { | |
179 | + return super.size(); | |
180 | + } | |
181 | + | |
182 | + /** Returns the size of this component. */ | |
183 | + public Dimension size() { return getSize(); } | |
184 | + | |
185 | + | |
186 | + /** | |
187 | + * Returns the bounds of this component. | |
188 | + */ | |
189 | + public Rectangle getBounds() { | |
190 | + return super.bounds(); | |
191 | + } | |
192 | + | |
193 | + /* Returns the bounds of this component. */ | |
194 | + public Rectangle bounds() { return getBounds(); } | |
195 | + | |
196 | + | |
197 | + /** | |
198 | + * Moves and resizes this component. | |
199 | + */ | |
200 | + public void setBounds(int x, int y, int width, int height) { | |
201 | + super.reshape(x, y, width, height); | |
202 | + } | |
203 | + | |
204 | + /** Moves and resizes this component. */ | |
205 | + public void reshape(int x, int y, int width, int height) { | |
206 | + setBounds(x, y, width, height); | |
207 | + } | |
208 | + | |
209 | + | |
210 | + /** | |
211 | + * Returns the preferred size of this component. | |
212 | + */ | |
213 | + public Dimension getPreferredSize() { | |
214 | + return super.preferredSize(); | |
215 | + } | |
216 | + | |
217 | + /** Returns the preferred size of this component. */ | |
218 | + public Dimension preferredSize() { return getPreferredSize(); } | |
219 | + | |
220 | + | |
221 | + /** | |
222 | + * Returns the minimum size of this component. | |
223 | + */ | |
224 | + public Dimension getMinimumSize() { | |
225 | + return super.minimumSize(); | |
226 | + } | |
227 | + | |
228 | + /** Returns the minimum size of this component. */ | |
229 | + public Dimension minimumSize() { return getMinimumSize(); } | |
230 | + | |
231 | + | |
232 | + /** | |
233 | + * Lays out this component. | |
234 | + */ | |
235 | + public void doLayout() { | |
236 | + super.layout(); | |
237 | + } | |
238 | + | |
239 | + /** Lays out this component. */ | |
240 | + public void layout() { doLayout(); } | |
241 | + | |
242 | + | |
243 | + /** | |
244 | + * Sets the foreground color of this component. | |
245 | + */ | |
246 | + public void setForeground(Color c) { | |
247 | + super.setForeground(c); | |
248 | + offscreenBuffer = null; | |
249 | + } | |
250 | + | |
251 | + /** | |
252 | + * Sets the background color of this component. | |
253 | + */ | |
254 | + public void setBackground(Color c) { | |
255 | + super.setBackground(c); | |
256 | + offscreenBuffer = null; | |
257 | + } | |
258 | + | |
259 | + | |
260 | + /** | |
261 | + * Paints this component. | |
262 | + */ | |
263 | + public void paint(Graphics g) { | |
264 | + if (!isShowing()) | |
265 | + return; | |
266 | + | |
267 | + Graphics pg = getPreferredGraphics(g); | |
268 | + if (pg == null) | |
269 | + return; | |
270 | + paintOn(pg); | |
271 | + if (pg != g) { | |
272 | + pg.dispose(); | |
273 | + } | |
274 | + | |
275 | + syncGraphics(g); | |
276 | + } | |
277 | + | |
278 | + /** | |
279 | + * Paints this component on the specified graphics object. | |
280 | + * All subclasses must override this method instead of paint(). | |
281 | + */ | |
282 | + protected void paintOn(Graphics g) { | |
283 | + } | |
284 | + | |
285 | + /** | |
286 | + * Updates this component. | |
287 | + */ | |
288 | + public void update(Graphics g) { | |
289 | + paint(g); | |
290 | + } | |
291 | + | |
292 | + /** | |
293 | + * Paints this component and all of its subcomponents immediately. | |
294 | + */ | |
295 | + public void repaintNow() { | |
296 | + if (isShowing()) { | |
297 | + Graphics g = getGraphics(); | |
298 | + if (g == null) | |
299 | + return; | |
300 | + try { | |
301 | + paint(g); | |
302 | + } | |
303 | + finally { | |
304 | + g.dispose(); | |
305 | + } | |
306 | + } | |
307 | + } | |
308 | + | |
309 | + /** | |
310 | + * Synchronizes this graphics state. | |
311 | + */ | |
312 | + public void syncGraphics() { | |
313 | + if (offscreenBuffer != null && isShowing()) { | |
314 | + Graphics g = getGraphics(); | |
315 | + //g.drawImage(offscreenBuffer, 0, 0, this); | |
316 | + g.drawImage(offscreenBuffer, 0, 0, null); | |
317 | + DefaultToolkit.sync(); | |
318 | + g.dispose(); | |
319 | + } | |
320 | + } | |
321 | + | |
322 | + /** | |
323 | + * Synchronizes this graphics state with the specified clip rectangle. | |
324 | + * | |
325 | + * @param x the x coordinate of the rectangle to intersect the clip with | |
326 | + * @param y the y coordinate of the rectangle to intersect the clip with | |
327 | + * @param width the width of the rectangle to intersect the clip with | |
328 | + * @param height the height of the rectangle to intersect the clip with | |
329 | + */ | |
330 | + public void syncGraphics(int x, int y, int width, int height) { | |
331 | + if (offscreenBuffer != null && isShowing()) { | |
332 | + Graphics g = getGraphics(); | |
333 | + g.clipRect(x, y, width, height); | |
334 | + //g.drawImage(offscreenBuffer, 0, 0, this); | |
335 | + g.drawImage(offscreenBuffer, 0, 0, null); | |
336 | + DefaultToolkit.sync(); | |
337 | + g.dispose(); | |
338 | + } | |
339 | + } | |
340 | + | |
341 | + /** | |
342 | + * Synchronizes this graphics state on the specified graphics context. | |
343 | + */ | |
344 | + public void syncGraphics(Graphics g) { | |
345 | + if (offscreenBuffer != null) { | |
346 | + //g.drawImage(offscreenBuffer, 0, 0, this); | |
347 | + g.drawImage(offscreenBuffer, 0, 0, null); | |
348 | + DefaultToolkit.sync(); | |
349 | + } | |
350 | + } | |
351 | + | |
352 | + /** | |
353 | + * Creates a preferred graphics context for this component. | |
354 | + */ | |
355 | + protected Graphics getPreferredGraphics() { | |
356 | + return getPreferredGraphics(null); | |
357 | + } | |
358 | + | |
359 | + /** | |
360 | + * Creates a preferred graphics context for this component with the | |
361 | + * specified graphics context. | |
362 | + */ | |
363 | + protected Graphics getPreferredGraphics(Graphics g) { | |
364 | + Dimension d = getSize(); | |
365 | + if (d.width <= 0 || d.height <= 0) | |
366 | + return null; | |
367 | + | |
368 | + Graphics pg; | |
369 | + if (!doubleBuffered) { | |
370 | + if (g != null) | |
371 | + pg = g; | |
372 | + else | |
373 | + pg = getGraphics(); | |
374 | + } | |
375 | + else { | |
376 | + if (offscreenBuffer == null || | |
377 | + offscreenBuffer.getWidth(null) != d.width || | |
378 | + offscreenBuffer.getHeight(null) != d.height) | |
379 | + { | |
380 | + offscreenBuffer = createImage(d.width, d.height); | |
381 | + pg = offscreenBuffer.getGraphics(); | |
382 | + pg.setColor(getBackground()); | |
383 | + pg.fillRect(0, 0, d.width, d.height); | |
384 | + } | |
385 | + else { | |
386 | + pg = offscreenBuffer.getGraphics(); | |
387 | + } | |
388 | + } | |
389 | + | |
390 | + Rectangle r = null; | |
391 | + if (g != null) | |
392 | + r = g.getClipBounds(); | |
393 | + if (r == null) { | |
394 | + pg.setClip(0, 0, d.width, d.height); | |
395 | + } | |
396 | + else { | |
397 | + if (r.x < 0) r.x = 0; | |
398 | + if (r.y < 0) r.y = 0; | |
399 | + if (r.width > d.width) r.width = d.width; | |
400 | + if (r.height > d.height) r.height = d.height; | |
401 | + pg.setClip(r.x, r.y, r.width, r.height); | |
402 | + } | |
403 | + | |
404 | + pg.setFont(getFont()); | |
405 | + pg.setColor(getForeground()); | |
406 | + | |
407 | + /*if[JDK1.2] | |
408 | + if (!AWTResources.RENDERING_HINTS.isEmpty()) { | |
409 | + java.awt.Graphics2D g2 = (java.awt.Graphics2D)pg; | |
410 | + g2.setRenderingHints(AWTResources.RENDERING_HINTS); | |
411 | + } | |
412 | + /*end[JDK1.2]*/ | |
413 | + | |
414 | + return pg; | |
415 | + } | |
416 | + | |
417 | + /** | |
418 | + * Requests that this component get the input focus. | |
419 | + * <p> | |
420 | + * For the <code>sun.awt.windows.WWindowPeer#getFocusPeer()</code> bug | |
421 | + * (it exists in the JDK for Windows 95/NT version 1.1.4 or before), | |
422 | + * if a lightwight component is focus traversable, an application will | |
423 | + * be hung up. | |
424 | + * | |
425 | + * @see jp.kyasu.awt.AWTResources#HAS_FOCUS_BUG | |
426 | + */ | |
427 | + public void requestFocus() { | |
428 | + if (!AWTResources.HAS_FOCUS_BUG) { | |
429 | + super.requestFocus(); | |
430 | + } | |
431 | + else { | |
432 | + synchronized (AWTResources.FOCUS_LOCK) { | |
433 | + boolean save = AWTResources.IN_REQUEST_FOCUS; | |
434 | + AWTResources.IN_REQUEST_FOCUS = true; | |
435 | + super.requestFocus(); | |
436 | + AWTResources.IN_REQUEST_FOCUS = save; | |
437 | + } | |
438 | + } | |
439 | + } | |
440 | + | |
441 | + /** | |
442 | + * Notifies this component that it has been added to a container | |
443 | + * and if a peer is required, it should be created. | |
444 | + * <p> | |
445 | + * The component state is checked for avoiding the JDK bug. | |
446 | + * | |
447 | + * @see jp.kyasu.awt.AWTResources#checkComponentState(java.awt.Component) | |
448 | + */ | |
449 | + public void addNotify() { | |
450 | + super.addNotify(); | |
451 | + AWTResources.checkComponentState(this); | |
452 | + } | |
453 | + | |
454 | + /** | |
455 | + * Returns the top fram of this component. | |
456 | + */ | |
457 | + public java.awt.Frame getFrame() { | |
458 | + for (Container c = getParent(); c != null; c = c.getParent()) { | |
459 | + if (c instanceof java.awt.Frame) | |
460 | + return (java.awt.Frame)c; | |
461 | + } | |
462 | + return null; | |
463 | + } | |
464 | + | |
465 | + /** | |
466 | + * Posts a 1.0 style event. | |
467 | + */ | |
468 | + protected void postOldEvent(AWTEvent e) { | |
469 | + Object src = e.getSource(); | |
470 | + int newid = e.getID(); | |
471 | + Event oe = null; | |
472 | + | |
473 | + switch(e.getID()) { | |
474 | + case ActionEvent.ACTION_PERFORMED: | |
475 | + ActionEvent ae = (ActionEvent)e; | |
476 | + String cmd; | |
477 | + if (src instanceof jp.kyasu.awt.Button) { | |
478 | + cmd = ((jp.kyasu.awt.Button)src).getLabel(); | |
479 | + } | |
480 | + else if (src instanceof java.awt.MenuItem) { | |
481 | + cmd = ((java.awt.MenuItem)src).getLabel(); | |
482 | + } | |
483 | + else { | |
484 | + cmd = ae.getActionCommand(); | |
485 | + } | |
486 | + oe = new Event(src, 0, newid, 0, 0, 0, ae.getModifiers(), cmd); | |
487 | + break; | |
488 | + | |
489 | + case ItemEvent.ITEM_STATE_CHANGED: | |
490 | + ItemEvent ie = (ItemEvent)e; | |
491 | + Object arg; | |
492 | + if (src instanceof jp.kyasu.awt.List) { | |
493 | + newid = (ie.getStateChange() == ItemEvent.SELECTED ? | |
494 | + Event.LIST_SELECT : Event.LIST_DESELECT); | |
495 | + arg = ie.getItem(); | |
496 | + } | |
497 | + else { | |
498 | + newid = Event.ACTION_EVENT; | |
499 | + if (src instanceof jp.kyasu.awt.Choice) { | |
500 | + arg = ie.getItem(); | |
501 | + } | |
502 | + else { // Checkbox | |
503 | + arg = new Boolean(ie.getStateChange()==ItemEvent.SELECTED); | |
504 | + } | |
505 | + } | |
506 | + oe = new Event(src, newid, arg); | |
507 | + break; | |
508 | + | |
509 | + case AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED: | |
510 | + AdjustmentEvent aje = (AdjustmentEvent)e; | |
511 | + switch(aje.getAdjustmentType()) { | |
512 | + case AdjustmentEvent.UNIT_INCREMENT: | |
513 | + newid = Event.SCROLL_LINE_DOWN; | |
514 | + break; | |
515 | + case AdjustmentEvent.UNIT_DECREMENT: | |
516 | + newid = Event.SCROLL_LINE_UP; | |
517 | + break; | |
518 | + case AdjustmentEvent.BLOCK_INCREMENT: | |
519 | + newid = Event.SCROLL_PAGE_DOWN; | |
520 | + break; | |
521 | + case AdjustmentEvent.BLOCK_DECREMENT: | |
522 | + newid = Event.SCROLL_PAGE_UP; | |
523 | + break; | |
524 | + case AdjustmentEvent.TRACK: | |
525 | + newid = Event.SCROLL_ABSOLUTE; | |
526 | + break; | |
527 | + default: | |
528 | + return; | |
529 | + } | |
530 | + oe = new Event(src, newid, new Integer(aje.getValue())); | |
531 | + break; | |
532 | + } | |
533 | + | |
534 | + if (oe != null) { | |
535 | + postEvent(oe); | |
536 | + } | |
537 | + } | |
538 | + | |
539 | + | |
540 | + // ================ ToolTip ================ | |
541 | + | |
542 | + /** | |
543 | + * The tooltip for this component. | |
544 | + */ | |
545 | + protected ToolTip toolTip = null; | |
546 | + | |
547 | + | |
548 | + /** | |
549 | + * Return the tooltip string that has been set with | |
550 | + * <code>setToolTipText()</code>. | |
551 | + * | |
552 | + * @return the string of the tool tip. | |
553 | + */ | |
554 | + public String getToolTipText() { | |
555 | + return (toolTip == null ? null : toolTip.getToolTipText()); | |
556 | + } | |
557 | + | |
558 | + /** | |
559 | + * Return the tooltip visual object that has been set with | |
560 | + * <code>setToolTipVisual()</code>. | |
561 | + * | |
562 | + * @return the visual object of the tool tip. | |
563 | + */ | |
564 | + public Visualizable getToolTipVisual() { | |
565 | + return (toolTip == null ? null : toolTip.getToolTipVisual()); | |
566 | + } | |
567 | + | |
568 | + /** | |
569 | + * Registers the string to display in a ToolTip. | |
570 | + * | |
571 | + * @param string The string to display when the cursor lingers over the | |
572 | + * component. If string is null, then it turns off tool tip | |
573 | + * for this component. | |
574 | + */ | |
575 | + public synchronized void setToolTipText(String string) { | |
576 | + if (string == null) { | |
577 | + toolTip.hidePopup(); | |
578 | + toolTip = null; | |
579 | + } | |
580 | + else { | |
581 | + if (toolTip == null) | |
582 | + toolTip = new ToolTip(string); | |
583 | + else | |
584 | + toolTip.setToolTipText(string); | |
585 | + enableEvents(AWTEvent.MOUSE_EVENT_MASK); | |
586 | + enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); | |
587 | + } | |
588 | + } | |
589 | + | |
590 | + /** | |
591 | + * Registers the text object to display in a ToolTip. | |
592 | + * | |
593 | + * @param text The text object to display when the cursor lingers over | |
594 | + * the component. If text is null, then it turns off tool | |
595 | + * tip for this component. | |
596 | + */ | |
597 | + public synchronized void setToolTipText(Text text) { | |
598 | + if (text == null) { | |
599 | + toolTip.hidePopup(); | |
600 | + toolTip = null; | |
601 | + } | |
602 | + else { | |
603 | + if (toolTip == null) | |
604 | + toolTip = new ToolTip(text); | |
605 | + else | |
606 | + toolTip.setToolTipText(text); | |
607 | + enableEvents(AWTEvent.MOUSE_EVENT_MASK); | |
608 | + enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); | |
609 | + } | |
610 | + } | |
611 | + | |
612 | + /** | |
613 | + * Registers the visual object to display in a ToolTip. | |
614 | + * | |
615 | + * @param visual The visual object to display when the cursor lingers | |
616 | + * over the component. If visual is null, then it turns | |
617 | + * off tool tip for this component. | |
618 | + */ | |
619 | + public synchronized void setToolTipVisual(Visualizable visual) { | |
620 | + if (visual == null) { | |
621 | + toolTip.hidePopup(); | |
622 | + toolTip = null; | |
623 | + } | |
624 | + else { | |
625 | + if (toolTip == null) | |
626 | + toolTip = new ToolTip(visual); | |
627 | + else | |
628 | + toolTip.setToolTipVisual(visual); | |
629 | + enableEvents(AWTEvent.MOUSE_EVENT_MASK); | |
630 | + enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); | |
631 | + } | |
632 | + } | |
633 | + | |
634 | + | |
635 | + protected void processMouseEvent(MouseEvent e) { | |
636 | + if (toolTip != null) { | |
637 | + switch (e.getID()) { | |
638 | + case MouseEvent.MOUSE_ENTERED: | |
639 | + toolTip.hidePopup(); | |
640 | + toolTip.showPopup(this, e.getX(), getSize().height + 2, | |
641 | + ToolTip.TOOLTIP_DELAY); | |
642 | + break; | |
643 | + case MouseEvent.MOUSE_PRESSED: | |
644 | + case MouseEvent.MOUSE_EXITED: | |
645 | + toolTip.hidePopup(); | |
646 | + break; | |
647 | + } | |
648 | + } | |
649 | + super.processMouseEvent(e); | |
650 | + } | |
651 | + | |
652 | + protected void processMouseMotionEvent(MouseEvent e) { | |
653 | + if (toolTip != null) { | |
654 | + switch (e.getID()) { | |
655 | + case MouseEvent.MOUSE_MOVED: | |
656 | + toolTip.setPopupLocationHint(e.getX(), getSize().height + 2); | |
657 | + break; | |
658 | + } | |
659 | + } | |
660 | + super.processMouseMotionEvent(e); | |
661 | + } | |
662 | +} |
@@ -0,0 +1,2745 @@ | ||
1 | +/* | |
2 | + * BasicTextEditController.java | |
3 | + * | |
4 | + * Copyright (c) 1997, 1998 Kazuki YASUMATSU. All Rights Reserved. | |
5 | + * | |
6 | + * Permission to use, copy, modify, and distribute this software and its | |
7 | + * documentation for any purpose and without fee or royalty is hereby | |
8 | + * granted, provided that both the above copyright notice and this | |
9 | + * permission notice appear in all copies of the software and | |
10 | + * documentation or portions thereof, including modifications, that you | |
11 | + * make. | |
12 | + * | |
13 | + * THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO | |
14 | + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, | |
15 | + * BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR | |
16 | + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR | |
17 | + * THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY | |
18 | + * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. | |
19 | + * COPYRIGHT HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE | |
20 | + * OR DOCUMENTATION. | |
21 | + */ | |
22 | + | |
23 | +package jp.kyasu.awt.text; | |
24 | + | |
25 | +import jp.kyasu.awt.Button; | |
26 | +import jp.kyasu.awt.Dialog; | |
27 | +import jp.kyasu.awt.Label; | |
28 | +import jp.kyasu.awt.Panel; | |
29 | +import jp.kyasu.awt.TextField; | |
30 | +import jp.kyasu.awt.TextEditModel; | |
31 | +import jp.kyasu.awt.Dialog; | |
32 | +import jp.kyasu.awt.Undo; | |
33 | +import jp.kyasu.graphics.ClickableTextAction; | |
34 | +import jp.kyasu.graphics.RichText; | |
35 | +import jp.kyasu.graphics.RichTextStyle; | |
36 | +import jp.kyasu.graphics.ParagraphStyle; | |
37 | +import jp.kyasu.graphics.ParagraphStyleModifier; | |
38 | +import jp.kyasu.graphics.Text; | |
39 | +import jp.kyasu.graphics.TextStyle; | |
40 | +import jp.kyasu.graphics.TextStyleModifier; | |
41 | +import jp.kyasu.graphics.text.TextPositionInfo; | |
42 | +import jp.kyasu.graphics.VActiveButton; | |
43 | + | |
44 | +import java.awt.BorderLayout; | |
45 | +import java.awt.Cursor; | |
46 | +import java.awt.Event; | |
47 | +import java.awt.Graphics; | |
48 | +import java.awt.GridBagLayout; | |
49 | +import java.awt.GridBagConstraints; | |
50 | +import java.awt.Insets; | |
51 | +import java.awt.Menu; | |
52 | +import java.awt.MenuItem; | |
53 | +import java.awt.MenuShortcut; | |
54 | +import java.awt.Point; | |
55 | +import java.awt.PopupMenu; | |
56 | +import java.awt.PrintJob; | |
57 | +import java.awt.event.*; | |
58 | +import java.text.BreakIterator; | |
59 | +import java.text.CharacterIterator; | |
60 | +import java.util.Enumeration; | |
61 | + | |
62 | +/*if[JDK1.2]*/ | |
63 | +import jp.kyasu.graphics.BasicTSModifier; | |
64 | +import jp.kyasu.graphics.TextBuffer; | |
65 | +import java.awt.BasicStroke; | |
66 | +import java.awt.Color; | |
67 | +import java.awt.Graphics2D; | |
68 | +import java.awt.Stroke; | |
69 | +import java.awt.event.InputMethodEvent; | |
70 | +import java.awt.event.InputMethodListener; | |
71 | +import java.awt.font.TextAttribute; | |
72 | +import java.awt.font.TextHitInfo; | |
73 | +import java.awt.im.InputMethodHighlight; | |
74 | +import java.text.AttributedCharacterIterator; | |
75 | +import java.util.Map; | |
76 | +/*end[JDK1.2]*/ | |
77 | + | |
78 | +/** | |
79 | + * The <code>BasicTextEditController</code> class implements a controller | |
80 | + * of a MVC model for the text editing. The model of the MVC model is a | |
81 | + * <code>TextEditModel</code> object and the view of the MVC model is a | |
82 | + * <code>TextEditView</code> object. | |
83 | + * <p> | |
84 | + * The <code>BasicTextEditController</code> class implements basic operations | |
85 | + * for the text editing. The <code>TextEditController</code> class (a | |
86 | + * subclass of this class) implements full operations for the text editing. | |
87 | + * <p> | |
88 | + * The principal editing operations on a <code>BasicTextEditController</code> | |
89 | + * are the <code>replaceRange (replaceSelection)</code>, | |
90 | + * <code>setRangeTextStyle (setSelectionTextStyle)</code>, | |
91 | + * <code>modifyRangeTextStyle (modifySelectionTextStyle)</code>, | |
92 | + * <code>setRangeParagraphStyle (setSelectionParagraphStyle)</code> and | |
93 | + * <code>modifyRangeParagraphStyle (modifySelectionParagraphStyle)</code> | |
94 | + * methods: | |
95 | + * <ul> | |
96 | + * <li>The <code>replaceRange (replaceSelection)</code> method replaces the | |
97 | + * specified range (selection) of the text (model) with the specified | |
98 | + * replacement text. | |
99 | + * <li>The <code>setRangeTextStyle (setSelectionTextStyle)</code> method | |
100 | + * sets the text style in the specified range (selection) of the text | |
101 | + * (model) to be the specified text style. | |
102 | + * <li>The <code>>modifyRangeTextStyle (modifySelectionTextStyle)</code> | |
103 | + * method modifies the text style in the specified range (selection) of | |
104 | + * the text (model) by using the specified <code>TextStyleModifier</code> | |
105 | + * object. | |
106 | + * <li>The <code>setRangeParagraphStyle (setSelectionParagraphStyle)</code> | |
107 | + * method sets the paragraph style at the specified range (selection) | |
108 | + * of the text (model) to be the specified paragraph style. | |
109 | + * <li>The <code>modifyRangeParagraphStyle | |
110 | + * (modifySelectionParagraphStyle)</code> method modifies the paragraph | |
111 | + * style at the specified range (selection) of the text (model) by using | |
112 | + * the specified <code>ParagraphStyleModifier</code> object. | |
113 | + * </ul> | |
114 | + * | |
115 | + * @see jp.kyasu.awt.TextEditModel | |
116 | + * @see jp.kyasu.awt.text.TextEditView | |
117 | + * @see jp.kyasu.awt.text.TextEditController | |
118 | + * | |
119 | + * @version 18 Dec 1998 | |
120 | + * @author Kazuki YASUMATSU | |
121 | + */ | |
122 | +public class BasicTextEditController extends TextController | |
123 | + implements ActionListener | |
124 | + /*if[JDK1.2]*/ | |
125 | + , InputMethodListener | |
126 | + /*end[JDK1.2]*/ | |
127 | +{ | |
128 | + protected TextEditModel model; | |
129 | + protected TextEditView view; | |
130 | + protected KeyBinding keyBinding; | |
131 | + protected boolean clickable; | |
132 | + protected Text typeInText; | |
133 | + protected TextStyle typeInStyle; | |
134 | + protected Menu editMenu; | |
135 | + protected PopupMenu popupMenu; | |
136 | + transient protected TextPositionInfo dragOrigin; | |
137 | + transient protected TextPositionInfo dragCurrent; | |
138 | + transient protected Undo lastUndo; | |
139 | + | |
140 | + | |
141 | + /** | |
142 | + * Constructs a text edit controller with the specified text edit view. | |
143 | + * | |
144 | + * @param view the text edit view. | |
145 | + */ | |
146 | + public BasicTextEditController(TextEditView view) { | |
147 | + super(); | |
148 | + if (view == null) | |
149 | + throw new NullPointerException(); | |
150 | + model = view.model; | |
151 | + this.view = view; | |
152 | + | |
153 | + keyBinding = new KeyBinding(); | |
154 | + | |
155 | + selectionVisibleAtFocus = false; | |
156 | + /* | |
157 | + if (selectionVisibleAtFocus) { | |
158 | + view.setSelectionVisible(false); | |
159 | + } | |
160 | + */ | |
161 | + | |
162 | + clickable = false; | |
163 | + typeInStyle = model.getRichText().getRichTextStyle().getTextStyle(); | |
164 | + typeInText = new Text(" ", typeInStyle); | |
165 | + | |
166 | + editMenu = createEditMenu(); | |
167 | + setPopupMenu(createPopupMenu()); | |
168 | + | |
169 | + dragOrigin = dragCurrent = null; | |
170 | + lastUndo = null; | |
171 | + } | |
172 | + | |
173 | + | |
174 | + /** | |
175 | + * Returns the model of this controller. | |
176 | + */ | |
177 | + public TextEditModel getModel() { | |
178 | + return model; | |
179 | + } | |
180 | + | |
181 | + /** | |
182 | + * Returns the view of this controller. | |
183 | + */ | |
184 | + public TextView getView() { | |
185 | + return view; | |
186 | + } | |
187 | + | |
188 | + /** | |
189 | + * Tests if the selection becomes visible when the view is focused. | |
190 | + */ | |
191 | + public boolean isSelectionVisibleAtFocus() { | |
192 | + return selectionVisibleAtFocus && view.selectionIsCaret(); | |
193 | + } | |
194 | + | |
195 | + /** | |
196 | + * Returns the text in the clipboard. | |
197 | + */ | |
198 | + public Text getClipboardText() { | |
199 | + return getClipboardText(typeInStyle); | |
200 | + } | |
201 | + | |
202 | + /** | |
203 | + * Returns the keymap of this controller. | |
204 | + * @see #setKeymap(jp.kyasu.awt.text.Keymap) | |
205 | + */ | |
206 | + public Keymap getKeymap() { | |
207 | + return keyBinding.getKeymap(); | |
208 | + } | |
209 | + | |
210 | + /** | |
211 | + * Sets the keymap of this controller. | |
212 | + * @see #getKeymap() | |
213 | + */ | |
214 | + public synchronized void setKeymap(Keymap keymap) { | |
215 | + keyBinding.setKeymap(keymap); | |
216 | + } | |
217 | + | |
218 | + /** | |
219 | + * Returns the key binding of this controller. | |
220 | + * @see #getKeyAction(java.lang.String) | |
221 | + * @see #addKeyAction(jp.kyasu.awt.text.KeyAction) | |
222 | + * @see #removeKeyAction(jp.kyasu.awt.text.KeyAction) | |
223 | + * @see #removeKeyActionNamed(java.lang.String) | |
224 | + */ | |
225 | + public KeyBinding getKeyBinding() { | |
226 | + return keyBinding; | |
227 | + } | |
228 | + | |
229 | + /** | |
230 | + * Returns the key action object associated with the specified name. | |
231 | + * @param actionName the name of the key action. | |
232 | + * @return the key action object, or <code>null</code> if no associated | |
233 | + * action exists. | |
234 | + * @see #addKeyAction(jp.kyasu.awt.text.KeyAction) | |
235 | + * @see #removeKeyAction(jp.kyasu.awt.text.KeyAction) | |
236 | + * @see #removeKeyActionNamed(java.lang.String) | |
237 | + */ | |
238 | + public KeyAction getKeyAction(String actionName) { | |
239 | + return keyBinding.getKeyAction(actionName); | |
240 | + } | |
241 | + | |
242 | + /** | |
243 | + * Adds the key action to the key binding of this controller. | |
244 | + * @param keyAction the key action object. | |
245 | + * @see #getKeyAction(java.lang.String) | |
246 | + * @see #removeKeyAction(jp.kyasu.awt.text.KeyAction) | |
247 | + * @see #removeKeyActionNamed(java.lang.String) | |
248 | + */ | |
249 | + public synchronized void addKeyAction(KeyAction keyAction) { | |
250 | + keyBinding.addKeyAction(keyAction); | |
251 | + } | |
252 | + | |
253 | + /** | |
254 | + * Removes the key action from the key binding of this controller. | |
255 | + * @param keyAction the key action object. | |
256 | + * @see #addKeyAction(jp.kyasu.awt.text.KeyAction) | |
257 | + * @see #getKeyAction(java.lang.String) | |
258 | + * @see #removeKeyActionNamed(java.lang.String) | |
259 | + */ | |
260 | + public synchronized void removeKeyAction(KeyAction keyAction) { | |
261 | + keyBinding.removeKeyAction(keyAction); | |
262 | + } | |
263 | + | |
264 | + /** | |
265 | + * Removes the key action named the specified name from the key binding | |
266 | + * of this controller. | |
267 | + * @param actionName the name of the key action. | |
268 | + * @see #addKeyAction(jp.kyasu.awt.text.KeyAction) | |
269 | + * @see #getKeyAction(java.lang.String) | |
270 | + * @see #removeKeyAction(jp.kyasu.awt.text.KeyAction) | |
271 | + */ | |
272 | + public synchronized void removeKeyActionNamed(String actionName) { | |
273 | + keyBinding.removeKeyActionNamed(actionName); | |
274 | + } | |
275 | + | |
276 | + /** | |
277 | + * Performs the key action named the specified name. | |
278 | + * @param actionName the name of the key action. | |
279 | + * @see #performKeyAction(java.lang.String, char) | |
280 | + */ | |
281 | + public void performKeyAction(String actionName) { | |
282 | + performKeyAction(actionName, ' '); | |
283 | + } | |
284 | + | |
285 | + /** | |
286 | + * Performs the key action named the specified name. | |
287 | + * @param actionName the name of the key action. | |
288 | + * @param keyChar the key character for the key action. | |
289 | + * @see #performKeyAction(java.lang.String) | |
290 | + */ | |
291 | + public synchronized void performKeyAction(String actionName, char keyChar) | |
292 | + { | |
293 | + KeyAction action = keyBinding.getKeyAction(actionName); | |
294 | + if (action != null) { | |
295 | + action.perform(keyChar); | |
296 | + } | |
297 | + } | |
298 | + | |
299 | + /** | |
300 | + * Tests if this controller handles <code>ClickableTextAction</code>. | |
301 | + * @see #setClickable(boolean) | |
302 | + * @see jp.kyasu.graphics.ClickableTextAction | |
303 | + */ | |
304 | + public boolean isClickable() { | |
305 | + return clickable; | |
306 | + } | |
307 | + | |
308 | + /** | |
309 | + * Makes this controller handle <code>ClickableTextAction</code>. | |
310 | + * @see #isClickable() | |
311 | + * @see jp.kyasu.graphics.ClickableTextAction | |
312 | + */ | |
313 | + public void setClickable(boolean b) { | |
314 | + clickable = b; | |
315 | + } | |
316 | + | |
317 | + /** | |
318 | + * Returns the edit menu of this controller. | |
319 | + */ | |
320 | + public Menu getEditMenu() { | |
321 | + return editMenu; | |
322 | + } | |
323 | + | |
324 | + /** | |
325 | + * Returns the popup menu of this controller. | |
326 | + * @see #setPopupMenu(java.awt.PopupMenu) | |
327 | + */ | |
328 | + public synchronized PopupMenu getPopupMenu() { | |
329 | + return popupMenu; | |
330 | + } | |
331 | + | |
332 | + /** | |
333 | + * Sets the popup menu of this controller. | |
334 | + * @see #getPopupMenu() | |
335 | + */ | |
336 | + public void setPopupMenu(PopupMenu menu) { | |
337 | + if (popupMenu != null) | |
338 | + view.remove(popupMenu); | |
339 | + popupMenu = menu; | |
340 | + if (popupMenu != null) | |
341 | + view.add(popupMenu); | |
342 | + } | |
343 | + | |
344 | + | |
345 | + // ================ TextComponent APIs ================ | |
346 | + | |
347 | + /** | |
348 | + * Returns the string of the view of this controller. | |
349 | + * @see #getString(java.awt.String) | |
350 | + * @see #setString(java.awt.String) | |
351 | + */ | |
352 | + public String getString() { | |
353 | + return getString(System.getProperty("line.separator", "\n")); | |
354 | + } | |
355 | + | |
356 | + /** | |
357 | + * Returns the string of the view of this controller. | |
358 | + * @param separator the separator string. | |
359 | + * @see #getString() | |
360 | + * @see #setString(java.awt.String) | |
361 | + * @see jp.kyasu.graphics.Text#getSystemString(java.awt.String, java.awt.String) | |
362 | + */ | |
363 | + public String getString(String separator) { | |
364 | + return Text.getSystemString(getText().toString(), separator); | |
365 | + } | |
366 | + | |
367 | + /** | |
368 | + * Sets the string of the view of this controller. | |
369 | + * @see #getString() | |
370 | + */ | |
371 | + public synchronized void setString(String str) { | |
372 | + if (str == null) str = ""; | |
373 | + setText( | |
374 | + new Text(Text.getJavaString(str), | |
375 | + model.getRichText().getRichTextStyle().getTextStyle())); | |
376 | + } | |
377 | + | |
378 | + /** | |
379 | + * Returns the selected string from the view of this controller. | |
380 | + */ | |
381 | + public String getSelectedString() { | |
382 | + return Text.getSystemString(getSelectedText().toString()); | |
383 | + } | |
384 | + | |
385 | + /** | |
386 | + * Indicates whether or not the view of this controller is editable. | |
387 | + * @see #setEditable(boolean) | |
388 | + */ | |
389 | + public boolean isEditable() { | |
390 | + return view.isEditable(); | |
391 | + } | |
392 | + | |
393 | + /** | |
394 | + * Sets the flag that determines whether or not the view this controller | |
395 | + * is editable. | |
396 | + * @see #isEditable() | |
397 | + */ | |
398 | + public synchronized void setEditable(boolean b) { | |
399 | + view.setEditable(b); | |
400 | + setMenuEnabled(editMenu, b); | |
401 | + setMenuEnabled(popupMenu, b); | |
402 | + } | |
403 | + | |
404 | + /** | |
405 | + * Returns the start position of the selected text. | |
406 | + * @see #setSelectionStart(int) | |
407 | + * @see #getSelectionEnd() | |
408 | + */ | |
409 | + public int getSelectionStart() { | |
410 | + TextPositionInfo posInfo = view.getSelectionBegin(); | |
411 | + return (posInfo != null ? posInfo.textIndex : 0); | |
412 | + } | |
413 | + | |
414 | + /** | |
415 | + * Sets the selection start for the view of this controller to the | |
416 | + * specified position. | |
417 | + * @see #getSelectionStart() | |
418 | + * @see #setSelectionEnd(int) | |
419 | + */ | |
420 | + public void setSelectionStart(int selectionStart) { | |
421 | + select(selectionStart, getSelectionEnd()); | |
422 | + } | |
423 | + | |
424 | + /** | |
425 | + * Returns the end position of the selected text. | |
426 | + * @see #setSelectionEnd(int) | |
427 | + * @see #getSelectionStart() | |
428 | + */ | |
429 | + public int getSelectionEnd() { | |
430 | + TextPositionInfo posInfo = view.getSelectionEnd(); | |
431 | + return (posInfo != null ? posInfo.textIndex : 0); | |
432 | + } | |
433 | + | |
434 | + /** | |
435 | + * Sets the selection end for the view of this controller to the | |
436 | + * specified position. | |
437 | + * @see #getSelectionEnd() | |
438 | + * @see #setSelectionStart(int) | |
439 | + */ | |
440 | + public void setSelectionEnd(int selectionEnd) { | |
441 | + select(getSelectionStart(), selectionEnd); | |
442 | + } | |
443 | + | |
444 | + /** | |
445 | + * Selects the text between the specified start and end positions. | |
446 | + * @param selectionStart the start position of the text to select. | |
447 | + * @param selectionEnd the end position of the text to select. | |
448 | + * @see #setSelectionStart(int) | |
449 | + * @see #setSelectionEnd(int) | |
450 | + * @see #select(int, int, boolean, boolean) | |
451 | + * @see #selectAll() | |
452 | + */ | |
453 | + public void select(int selectionStart, int selectionEnd) { | |
454 | + select(selectionStart, selectionEnd, true, false); | |
455 | + } | |
456 | + | |
457 | + /** | |
458 | + * Selects all the text. | |
459 | + * @see #select(int, int) | |
460 | + */ | |
461 | + public void selectAll() { | |
462 | + select(0, model.getRichText().length()); | |
463 | + } | |
464 | + | |
465 | + /** | |
466 | + * Returns the position of the text insertion caret. | |
467 | + * @see #setCaretPosition(int) | |
468 | + */ | |
469 | + public int getCaretPosition() { | |
470 | + return getSelectionStart(); | |
471 | + } | |
472 | + | |
473 | + /** | |
474 | + * Sets the position of the text insertion caret. | |
475 | + * @see #getCaretPosition() | |
476 | + * @see #setCaretPosition(int, boolean) | |
477 | + */ | |
478 | + public void setCaretPosition(int position) { | |
479 | + setCaretPosition(position, false); | |
480 | + } | |
481 | + | |
482 | + /** | |
483 | + * Tests if the selection is caret, i.e., null selection. | |
484 | + */ | |
485 | + public boolean selectionIsCaret() { | |
486 | + return view.selectionIsCaret(); | |
487 | + } | |
488 | + | |
489 | + | |
490 | + // ================ TextArea APIs ================ | |
491 | + | |
492 | + /** | |
493 | + * Inserts the specified string at the specified position in the view | |
494 | + * of this controller. | |
495 | + * @param str the string to insert. | |
496 | + * @param pos the position at which to insert. | |
497 | + * @see #replaceRange(java.lang.String, int, int) | |
498 | + */ | |
499 | + public void insert(String str, int pos) { | |
500 | + if (str == null) str = ""; | |
501 | + replaceRange(str, pos, pos); | |
502 | + } | |
503 | + | |
504 | + /** | |
505 | + * Appends the given string to the current text. | |
506 | + * @param str the string to append. | |
507 | + * @see #insert(java.lang.String, int) | |
508 | + */ | |
509 | + public void append(String str) { | |
510 | + if (str == null) str = ""; | |
511 | + insert(str, model.getRichText().length()); | |
512 | + } | |
513 | + | |
514 | + /** | |
515 | + * Replaces string between the indicated start and end positions | |
516 | + * with the specified replacement string. | |
517 | + * @param str the string to use as the replacement. | |
518 | + * @param start the start position, inclusive. | |
519 | + * @param end the end position, exclusive. | |
520 | + * @see #replaceRange(jp.kyasu.graphics.Text, int, int) | |
521 | + */ | |
522 | + public void replaceRange(String str, int start, int end) { | |
523 | + if (str == null) str = ""; | |
524 | + replaceRange(new Text(str, typeInStyle), start, end); | |
525 | + } | |
526 | + | |
527 | + /** | |
528 | + * Returns the number of rows in the view of this controller. | |
529 | + * @see #getColumns() | |
530 | + */ | |
531 | + public int getRows() { | |
532 | + return view.getRows(); | |
533 | + } | |
534 | + | |
535 | + /** | |
536 | + * Returns the number of columns in the view of this controller. | |
537 | + * @see #getRows() | |
538 | + */ | |
539 | + public int getColumns() { | |
540 | + return view.getColumns(); | |
541 | + } | |
542 | + | |
543 | + | |
544 | + // ================ TextField APIs ================ | |
545 | + | |
546 | + /** | |
547 | + * Returns the character that is to be used for echoing. | |
548 | + * @see #setEchoChar(char) | |
549 | + * @see #echoCharIsSet() | |
550 | + */ | |
551 | + public char getEchoChar() { | |
552 | + return view.layout.getEchoChar(); | |
553 | + } | |
554 | + | |
555 | + /** | |
556 | + * Sets the echo character for the view of this controller. | |
557 | + * @see #getEchoChar() | |
558 | + * @see #echoCharIsSet() | |
559 | + */ | |
560 | + public synchronized void setEchoChar(char c) { | |
561 | + view.layout.setEchoChar(c); | |
562 | + model.setRichText(model.getRichText()); | |
563 | + } | |
564 | + | |
565 | + /** | |
566 | + * Indicates whether or not the view of this controller has a character | |
567 | + * set for echoing. | |
568 | + * @see #getEchoChar() | |
569 | + * @see #setEchoChar(char) | |
570 | + */ | |
571 | + public boolean echoCharIsSet() { | |
572 | + return view.layout.echoCharIsSet(); | |
573 | + } | |
574 | + | |
575 | + | |
576 | + // ================ Enhanced APIs ================ | |
577 | + | |
578 | + /** | |
579 | + * Returns the current text style of this controller. | |
580 | + */ | |
581 | + public TextStyle getCurrentTextStyle() { | |
582 | + return typeInStyle; | |
583 | + } | |
584 | + | |
585 | + /** | |
586 | + * Returns the text style at the specified index in the view of this | |
587 | + * controller. | |
588 | + * @return the text style at the specified index, or <code>null</code> | |
589 | + * if the index is out of range. | |
590 | + */ | |
591 | + public TextStyle getTextStyleAt(int index) { | |
592 | + RichText richText = model.getRichText(); | |
593 | + if (index < 0 || index >= richText.length()) | |
594 | + return null; | |
595 | + return richText.getTextStyleAt(index); | |
596 | + } | |
597 | + | |
598 | + /** | |
599 | + * Returns the number of the text styles in the text of the view of | |
600 | + * this controller. | |
601 | + */ | |
602 | + public int getTextStyleCount() { | |
603 | + return model.getRichText().getText().getTextStyleCount(); | |
604 | + } | |
605 | + | |
606 | + /** | |
607 | + * Returns all text styles in the text of the view of this controller. | |
608 | + */ | |
609 | + public TextStyle[] getTextStyles() { | |
610 | + return model.getRichText().getText().getTextStyles(); | |
611 | + } | |
612 | + | |
613 | + /** | |
614 | + * Returns the text styles in the text of the view of this controller. | |
615 | + * | |
616 | + * @param begin the beginning index to get text styles, inclusive. | |
617 | + * @param end the ending index to get text styles, exclusive. | |
618 | + */ | |
619 | + public TextStyle[] getTextStyles(int begin, int end) { | |
620 | + return model.getRichText().getText().getTextStyles(begin, end); | |
621 | + } | |
622 | + | |
623 | + /** | |
624 | + * Returns an enumeration of the text styles of text of the view of | |
625 | + * this controller. | |
626 | + */ | |
627 | + public Enumeration textStyles() { | |
628 | + return model.getRichText().getText().textStyles(); | |
629 | + } | |
630 | + | |
631 | + /** | |
632 | + * Returns an enumeration of the text styles of text of the view of | |
633 | + * this controller. | |
634 | + * | |
635 | + * @param begin the beginning index to get styles, inclusive. | |
636 | + * @param end the ending index to get styles, exclusive. | |
637 | + */ | |
638 | + public Enumeration textStyles(int begin, int end) { | |
639 | + return model.getRichText().getText().textStyles(begin, end); | |
640 | + } | |
641 | + | |
642 | + /** | |
643 | + * Returns the paragraph style at the specified index in the view of | |
644 | + * this controller. | |
645 | + * @return the paragraph style at the specified index, or <code>null</code> | |
646 | + * if the index is out of range. | |
647 | + */ | |
648 | + public ParagraphStyle getParagraphStyleAt(int index) { | |
649 | + RichText richText = model.getRichText(); | |
650 | + if (index < 0 || index > richText.length()) | |
651 | + return null; | |
652 | + return richText.getParagraphStyleAt(index); | |
653 | + } | |
654 | + | |
655 | + /** | |
656 | + * Returns the number of the paragraph styles in the text of the view | |
657 | + * of this controller. | |
658 | + */ | |
659 | + public int getParagraphStyleCount() { | |
660 | + return model.getRichText().getParagraphStyleCount(); | |
661 | + } | |
662 | + | |
663 | + /** | |
664 | + * Returns all paragraph styles in the text of the view of this controller. | |
665 | + */ | |
666 | + public ParagraphStyle[] getParagraphStyles() { | |
667 | + return model.getRichText().getParagraphStyles(); | |
668 | + } | |
669 | + | |
670 | + /** | |
671 | + * Returns the paragraph styles in the text of the view of this controller. | |
672 | + * | |
673 | + * @param begin the beginning index of the text to get paragraph styles, | |
674 | + * inclusive. | |
675 | + * @param end the ending index of the text to get paragraph styles, | |
676 | + * exclusive. | |
677 | + */ | |
678 | + public ParagraphStyle[] getParagraphStyles(int begin, int end) { | |
679 | + return model.getRichText().getParagraphStyles(begin, end); | |
680 | + } | |
681 | + | |
682 | + /** | |
683 | + * Returns an enumeration of the paragraph styles of the text of the | |
684 | + * view of this controller. | |
685 | + */ | |
686 | + public Enumeration paragraphStyles() { | |
687 | + return model.getRichText().paragraphStyles(); | |
688 | + } | |
689 | + | |
690 | + /** | |
691 | + * Returns an enumeration of the paragraph styles of the text of the | |
692 | + * view of this controller. | |
693 | + * | |
694 | + * @param begin the beginning index to get styles, inclusive. | |
695 | + * @param end the ending index to get styles, exclusive. | |
696 | + */ | |
697 | + public Enumeration paragraphStyles(int begin, int end) { | |
698 | + return model.getRichText().paragraphStyles(begin, end); | |
699 | + } | |
700 | + | |
701 | + /** | |
702 | + * Returns the text of the view of this controller. | |
703 | + * @see #setText(jp.kyasu.graphics.Text) | |
704 | + */ | |
705 | + public Text getText() { | |
706 | + return model.getRichText().getText(); | |
707 | + } | |
708 | + | |
709 | + /** | |
710 | + * Sets the text of the view of this controller. | |
711 | + * @see #getText() | |
712 | + */ | |
713 | + public synchronized void setText(Text text) { | |
714 | + view.setText(text); | |
715 | + clearUndo(); | |
716 | + } | |
717 | + | |
718 | + /** | |
719 | + * Returns the rich text of the view of this controller. | |
720 | + * @see #setRichText(jp.kyasu.graphics.RichText) | |
721 | + */ | |
722 | + public RichText getRichText() { | |
723 | + return model.getRichText(); | |
724 | + } | |
725 | + | |
726 | + /** | |
727 | + * Sets the rich text of the view of this controller. | |
728 | + * @see #getRichText() | |
729 | + */ | |
730 | + public synchronized void setRichText(RichText richText) { | |
731 | + view.setRichText(richText); | |
732 | + clearUndo(); | |
733 | + } | |
734 | + | |
735 | + /** | |
736 | + * Returns the selected text from the view of this controller. | |
737 | + */ | |
738 | + public Text getSelectedText() { | |
739 | + return view.getSelectedText(); | |
740 | + } | |
741 | + | |
742 | + /** | |
743 | + * Selects the text between the specified start and end positions. | |
744 | + * @param selectionStart the start position of the text to select. | |
745 | + * @param selectionEnd the end position of the text to select. | |
746 | + * @param scroll if true, scrolls the view after selection done. | |
747 | + * @see #select(int, int, boolean, boolean) | |
748 | + */ | |
749 | + public void select(int selectionStart, int selectionEnd, boolean scroll) { | |
750 | + select(selectionStart, selectionEnd, scroll, false); | |
751 | + } | |
752 | + | |
753 | + /** | |
754 | + * Selects the text between the specified start and end positions. | |
755 | + * @param selectionStart the start position of the text to select. | |
756 | + * @param selectionEnd the end position of the text to select. | |
757 | + * @param scroll if true, scrolls the view after selection done. | |
758 | + * @param top if true, scrolls to the top of the view. | |
759 | + * @see #select(int, int) | |
760 | + * @see #select(int, int, boolean) | |
761 | + */ | |
762 | + public void select(int selectionStart, int selectionEnd, | |
763 | + boolean scroll, boolean top) | |
764 | + { | |
765 | + int len = model.getRichText().length(); | |
766 | + if (selectionStart < 0) { | |
767 | + selectionStart = 0; | |
768 | + } | |
769 | + if (selectionEnd > len) { | |
770 | + selectionEnd = len; | |
771 | + } | |
772 | + if (selectionEnd < selectionStart) { | |
773 | + selectionEnd = selectionStart; | |
774 | + } | |
775 | + if (selectionStart > selectionEnd) { | |
776 | + selectionStart = selectionEnd; | |
777 | + } | |
778 | + TextPositionInfo begin = view.getTextPositionAt(selectionStart); | |
779 | + TextPositionInfo end = view.getTextPositionNearby(begin, selectionEnd); | |
780 | + select(begin, end, scroll, top); | |
781 | + } | |
782 | + | |
783 | + protected synchronized void select(TextPositionInfo begin, | |
784 | + TextPositionInfo end, | |
785 | + boolean scroll, boolean top) | |
786 | + { | |
787 | + view.hideSelection(); | |
788 | + setSelectionBeginEnd(begin, end); | |
789 | + if (scroll) { | |
790 | + if (top) { | |
791 | + view.scrollTo(new Point(0, -view.getSelectionBegin().y)); | |
792 | + } | |
793 | + else { | |
794 | + view.scrollTo(view.getSelectionBegin()); | |
795 | + } | |
796 | + } | |
797 | + view.showSelection(); | |
798 | + notifyTextPositionListeners(); | |
799 | + } | |
800 | + | |
801 | + /** | |
802 | + * Sets the position of the text insertion caret. | |
803 | + * @param position the position of the caret. | |
804 | + * @param top if true, scrolls to the top of the view. | |
805 | + * @see #setCaretPosition(int) | |
806 | + */ | |
807 | + public void setCaretPosition(int position, boolean top) { | |
808 | + if (position < 0) { | |
809 | + throw new IllegalArgumentException("position less than zero."); | |
810 | + } | |
811 | + int len = model.getRichText().length(); | |
812 | + if (position > len) { | |
813 | + position = len; | |
814 | + } | |
815 | + select(position, position, true, top); | |
816 | + } | |
817 | + | |
818 | + /** | |
819 | + * Inserts the specified text at the specified position in the view | |
820 | + * of this controller. | |
821 | + * @param text the text to insert. | |
822 | + * @param pos the position at which to insert. | |
823 | + * @see #insert(jp.kyasu.graphics.Text, int, boolean) | |
824 | + */ | |
825 | + public void insert(Text text, int pos) { | |
826 | + insert(text, pos, true); | |
827 | + } | |
828 | + | |
829 | + /** | |
830 | + * Inserts the specified text at the specified position in the view | |
831 | + * of this controller. | |
832 | + * @param text the text to insert. | |
833 | + * @param pos the position at which to insert. | |
834 | + * @param scroll if true, scrolls the view after the insertion. | |
835 | + * @see #insert(jp.kyasu.graphics.Text, int) | |
836 | + * @see #replaceRange(jp.kyasu.graphics.Text, int, int, boolean) | |
837 | + */ | |
838 | + public void insert(Text text, int pos, boolean scroll) { | |
839 | + replaceRange(text, pos, pos, scroll); | |
840 | + } | |
841 | + | |
842 | + /** | |
843 | + * Appends the given text to the current text. | |
844 | + * @param text the text to append. | |
845 | + * @see #append(jp.kyasu.graphics.Text, boolean) | |
846 | + */ | |
847 | + public void append(Text text) { | |
848 | + append(text, true); | |
849 | + } | |
850 | + | |
851 | + /** | |
852 | + * Appends the given text to the current text. | |
853 | + * @param text the text to append. | |
854 | + * @param scroll if true, scrolls the view after the appending. | |
855 | + * @see #insert(jp.kyasu.graphics.Text, int, boolean) | |
856 | + */ | |
857 | + public void append(Text text, boolean scroll) { | |
858 | + insert(text, model.getRichText().length(), scroll); | |
859 | + } | |
860 | + | |
861 | + /** | |
862 | + * Replaces text between the indicated start and end positions | |
863 | + * with the specified replacement text. | |
864 | + * @param text the text to use as the replacement. | |
865 | + * @param start the start position, inclusive. | |
866 | + * @param end the end position, exclusive. | |
867 | + * @see #replaceRange(jp.kyasu.graphics.Text, int, int, boolean) | |
868 | + */ | |
869 | + public void replaceRange(Text text, int start, int end) { | |
870 | + replaceRange(text, start, end, true); | |
871 | + } | |
872 | + | |
873 | + /** | |
874 | + * Replaces text between the indicated start and end positions | |
875 | + * with the specified replacement text. | |
876 | + * @param text the text to use as the replacement. | |
877 | + * @param start the start position, inclusive. | |
878 | + * @param end the end position, exclusive. | |
879 | + * @param scroll if true, scrolls the view after replace done. | |
880 | + * @see #replaceRange(java.lang.String, int, int) | |
881 | + * @see #replaceRange(jp.kyasu.graphics.Text, int, int) | |
882 | + * @see #replaceSelection(jp.kyasu.graphics.Text, boolean) | |
883 | + */ | |
884 | + public void replaceRange(Text text, int start, int end, boolean scroll) { | |
885 | + if (start > end) { | |
886 | + int i = start; | |
887 | + start = end; | |
888 | + end = i; | |
889 | + } | |
890 | + replaceRange(text, | |
891 | + view.getTextPositionAt(start), | |
892 | + view.getTextPositionAt(end), | |
893 | + scroll); | |
894 | + } | |
895 | + | |
896 | + /** | |
897 | + * Replaces string in the range of the current selection with the | |
898 | + * specified replacement string. | |
899 | + * @param str the string to use as the replacement. | |
900 | + * @see #replaceSelection(jp.kyasu.graphics.Text) | |
901 | + */ | |
902 | + public void replaceSelection(String str) { | |
903 | + if (str == null) str = ""; | |
904 | + replaceSelection(new Text(str, typeInStyle)); | |
905 | + } | |
906 | + | |
907 | + /** | |
908 | + * Replaces text in the range of the current selection with the | |
909 | + * specified replacement text. | |
910 | + * @param text the text to use as the replacement. | |
911 | + * @see #replaceSelection(jp.kyasu.graphics.Text, boolean) | |
912 | + */ | |
913 | + public void replaceSelection(Text text) { | |
914 | + replaceSelection(text, true); | |
915 | + } | |
916 | + | |
917 | + /** | |
918 | + * Replaces text in the range of the current selection with the | |
919 | + * specified replacement text. | |
920 | + * @param text the text to use as the replacement. | |
921 | + * @param scroll if true, scrolls the view after replace done. | |
922 | + * @see #replaceSelection(java.lang.String) | |
923 | + * @see #replaceSelection(jp.kyasu.graphics.Text) | |
924 | + * @see #replaceRange(jp.kyasu.graphics.Text, int, int, boolean) | |
925 | + */ | |
926 | + public void replaceSelection(Text text, boolean scroll) { | |
927 | + replaceRange(text, | |
928 | + view.getSelectionBegin(), | |
929 | + view.getSelectionEnd(), | |
930 | + scroll); | |
931 | + } | |
932 | + | |
933 | + /** | |
934 | + * Sets the text style between the indicated start and end positions | |
935 | + * to the specified text style. | |
936 | + * @param style the text style to be set. | |
937 | + * @param start the start position, inclusive. | |
938 | + * @param end the end position, exclusive. | |
939 | + * @see #setRangeTextStyle(jp.kyasu.graphics.TextStyle, int, int, boolean) | |
940 | + */ | |
941 | + public void setRangeTextStyle(TextStyle style, int start, int end) { | |
942 | + setRangeTextStyle(style, start, end, true); | |
943 | + } | |
944 | + | |
945 | + /** | |
946 | + * Sets the text style between the indicated start and end positions | |
947 | + * to the specified text style. | |
948 | + * @param style the text style to be set. | |
949 | + * @param start the start position, inclusive. | |
950 | + * @param end the end position, exclusive. | |
951 | + * @param scroll if true, scrolls the view after set done. | |
952 | + * @see #setRangeTextStyle(jp.kyasu.graphics.TextStyle, int, int) | |
953 | + * @see #setSelectionTextStyle(jp.kyasu.graphics.TextStyle, boolean) | |
954 | + */ | |
955 | + public void setRangeTextStyle(TextStyle style, int start, int end, | |
956 | + boolean scroll) | |
957 | + { | |
958 | + if (style == null) | |
959 | + return; | |
960 | + if (start > end) { | |
961 | + int i = start; | |
962 | + start = end; | |
963 | + end = i; | |
964 | + } | |
965 | + changeTextStyle(style, null, | |
966 | + view.getTextPositionAt(start), | |
967 | + view.getTextPositionAt(end), | |
968 | + scroll); | |
969 | + } | |
970 | + | |
971 | + /** | |
972 | + * Sets the text style in the range of the current selection to the | |
973 | + * specified text style. | |
974 | + * @param style the text style to be set. | |
975 | + * @see #setSelectionTextStyle(jp.kyasu.graphics.TextStyle, boolean) | |
976 | + */ | |
977 | + public void setSelectionTextStyle(TextStyle style) { | |
978 | + setSelectionTextStyle(style, true); | |
979 | + } | |
980 | + | |
981 | + /** | |
982 | + * Sets the text style in the range of the current selection to the | |
983 | + * specified text style. | |
984 | + * @param style the text style to be set. | |
985 | + * @param scroll if true, scrolls the view after set done. | |
986 | + * @see #setSelectionTextStyle(jp.kyasu.graphics.TextStyle) | |
987 | + * @see #setRangeTextStyle(jp.kyasu.graphics.TextStyle, int, int, boolean) | |
988 | + */ | |
989 | + public void setSelectionTextStyle(TextStyle style, boolean scroll) { | |
990 | + if (style == null) | |
991 | + return; | |
992 | + changeTextStyle(style, null, | |
993 | + view.getSelectionBegin(), | |
994 | + view.getSelectionEnd(), | |
995 | + scroll); | |
996 | + } | |
997 | + | |
998 | + | |
999 | + /** | |
1000 | + * Modifies the text style between the indicated start and end positions | |
1001 | + * by the specified text style modifier. | |
1002 | + * @param modifier the text style modifier. | |
1003 | + * @param start the start position, inclusive. | |
1004 | + * @param end the end position, exclusive. | |
1005 | + * @see #modifyRangeTextStyle(jp.kyasu.graphics.TextStyleModifier, int, int, boolean) | |
1006 | + */ | |
1007 | + public void modifyRangeTextStyle(TextStyleModifier modifier, | |
1008 | + int start, int end) | |
1009 | + { | |
1010 | + modifyRangeTextStyle(modifier, start, end, true); | |
1011 | + } | |
1012 | + | |
1013 | + /** | |
1014 | + * Modifies the text style between the indicated start and end positions | |
1015 | + * by the specified text style modifier. | |
1016 | + * @param modifier the text style modifier. | |
1017 | + * @param start the start position, inclusive. | |
1018 | + * @param end the end position, exclusive. | |
1019 | + * @param scroll if true, scrolls the view after set done. | |
1020 | + * @see #modifyRangeTextStyle(jp.kyasu.graphics.TextStyleModifier, int, int) | |
1021 | + * @see #modifySelectionTextStyle(jp.kyasu.graphics.TextStyleModifier, boolean) | |
1022 | + */ | |
1023 | + public void modifyRangeTextStyle(TextStyleModifier modifier, | |
1024 | + int start, int end, boolean scroll) | |
1025 | + { | |
1026 | + if (modifier == null) | |
1027 | + return; | |
1028 | + if (start > end) { | |
1029 | + int i = start; | |
1030 | + start = end; | |
1031 | + end = i; | |
1032 | + } | |
1033 | + changeTextStyle(null, modifier, | |
1034 | + view.getTextPositionAt(start), | |
1035 | + view.getTextPositionAt(end), | |
1036 | + scroll); | |
1037 | + } | |
1038 | + | |
1039 | + /** | |
1040 | + * Modifies the text style in the range of the current selection by the | |
1041 | + * specified text style modifier. | |
1042 | + * @param modifier the text style modifier. | |
1043 | + * @see #modifySelectionTextStyle(jp.kyasu.graphics.TextStyleModifier, boolean) | |
1044 | + */ | |
1045 | + public void modifySelectionTextStyle(TextStyleModifier modifier) { | |
1046 | + modifySelectionTextStyle(modifier, true); | |
1047 | + } | |
1048 | + | |
1049 | + /** | |
1050 | + * Modifies the text style in the range of the current selection by the | |
1051 | + * specified text style modifier. | |
1052 | + * @param modifier the text style modifier. | |
1053 | + * @param scroll if true, scrolls the view after set done. | |
1054 | + * @see #modifySelectionTextStyle(jp.kyasu.graphics.TextStyleModifier) | |
1055 | + * @see #modifyRangeTextStyle(jp.kyasu.graphics.TextStyleModifier, int, int, boolean) | |
1056 | + */ | |
1057 | + public void modifySelectionTextStyle(TextStyleModifier modifier, | |
1058 | + boolean scroll) | |
1059 | + { | |
1060 | + if (modifier == null) | |
1061 | + return; | |
1062 | + changeTextStyle(null, modifier, | |
1063 | + view.getSelectionBegin(), | |
1064 | + view.getSelectionEnd(), | |
1065 | + scroll); | |
1066 | + } | |
1067 | + | |
1068 | + /** | |
1069 | + * Sets the paragraph style between the indicated start and end positions | |
1070 | + * to the specified paragraph style. | |
1071 | + * @param style the paragraph style to be set. | |
1072 | + * @param start the start position, inclusive. | |
1073 | + * @param end the end position, exclusive. | |
1074 | + * @see #setRangeParagraphStyle(jp.kyasu.graphics.ParagraphStyle, int, int, boolean) | |
1075 | + */ | |
1076 | + public void setRangeParagraphStyle(ParagraphStyle style, int start, int end) | |
1077 | + { | |
1078 | + setRangeParagraphStyle(style, start, end, true); | |
1079 | + } | |
1080 | + | |
1081 | + /** | |
1082 | + * Sets the paragraph style between the indicated start and end positions | |
1083 | + * to the specified paragraph style. | |
1084 | + * @param style the paragraph style to be set. | |
1085 | + * @param start the start position, inclusive. | |
1086 | + * @param end the end position, exclusive. | |
1087 | + * @param scroll if true, scrolls the view after set done. | |
1088 | + * @see #setRangeParagraphStyle(jp.kyasu.graphics.ParagraphStyle, int, int) | |
1089 | + * @see #setSelectionParagraphStyle(jp.kyasu.graphics.ParagraphStyle, boolean) | |
1090 | + */ | |
1091 | + public void setRangeParagraphStyle(ParagraphStyle style, | |
1092 | + int start, int end, boolean scroll) | |
1093 | + { | |
1094 | + if (style == null) | |
1095 | + return; | |
1096 | + if (start > end) { | |
1097 | + int i = start; | |
1098 | + start = end; | |
1099 | + end = i; | |
1100 | + } | |
1101 | + changeParagraphStyle(style, null, | |
1102 | + view.getTextPositionAt(start), | |
1103 | + view.getTextPositionAt(end), | |
1104 | + scroll); | |
1105 | + } | |
1106 | + | |
1107 | + /** | |
1108 | + * Sets the paragraph style in the range of the current selection to the | |
1109 | + * specified paragraph style. | |
1110 | + * @param style the paragraph style to be set. | |
1111 | + * @see #setSelectionParagraphStyle(jp.kyasu.graphics.ParagraphStyle, boolean) | |
1112 | + */ | |
1113 | + public void setSelectionParagraphStyle(ParagraphStyle style) { | |
1114 | + setSelectionParagraphStyle(style, true); | |
1115 | + } | |
1116 | + | |
1117 | + /** | |
1118 | + * Sets the paragraph style in the range of the current selection to the | |
1119 | + * specified paragraph style. | |
1120 | + * @param style the paragraph style to be set. | |
1121 | + * @param scroll if true, scrolls the view after set done. | |
1122 | + * @see #setSelectionParagraphStyle(jp.kyasu.graphics.ParagraphStyle) | |
1123 | + * @see #setRangeParagraphStyle(jp.kyasu.graphics.ParagraphStyle, int, int, boolean) | |
1124 | + */ | |
1125 | + public void setSelectionParagraphStyle(ParagraphStyle style, boolean scroll) | |
1126 | + { | |
1127 | + if (style == null) | |
1128 | + return; | |
1129 | + changeParagraphStyle(style, null, | |
1130 | + view.getSelectionBegin(), | |
1131 | + view.getSelectionEnd(), | |
1132 | + scroll); | |
1133 | + } | |
1134 | + | |
1135 | + | |
1136 | + /** | |
1137 | + * Modifies the paragraph style between the indicated start and end | |
1138 | + * positions by the specified paragraph style modifier. | |
1139 | + * @param modifier the paragraph style modifier. | |
1140 | + * @param start the start position, inclusive. | |
1141 | + * @param end the end position, exclusive. | |
1142 | + * @see #modifyRangeParagraphStyle(jp.kyasu.graphics.ParagraphStyleModifier, int, int, boolean) | |
1143 | + */ | |
1144 | + public void modifyRangeParagraphStyle(ParagraphStyleModifier modifier, | |
1145 | + int start, int end) | |
1146 | + { | |
1147 | + modifyRangeParagraphStyle(modifier, start, end, true); | |
1148 | + } | |
1149 | + | |
1150 | + /** | |
1151 | + * Modifies the paragraph style between the indicated start and end | |
1152 | + * positions by the specified paragraph style modifier. | |
1153 | + * @param modifier the paragraph style modifier. | |
1154 | + * @param start the start position, inclusive. | |
1155 | + * @param end the end position, exclusive. | |
1156 | + * @param scroll if true, scrolls the view after set done. | |
1157 | + * @see #modifyRangeParagraphStyle(jp.kyasu.graphics.ParagraphStyleModifier, int, int) | |
1158 | + * @see #modifySelectionParagraphStyle(jp.kyasu.graphics.ParagraphStyleModifier, boolean) | |
1159 | + */ | |
1160 | + public void modifyRangeParagraphStyle(ParagraphStyleModifier modifier, | |
1161 | + int start, int end, boolean scroll) | |
1162 | + { | |
1163 | + if (modifier == null) | |
1164 | + return; | |
1165 | + if (start > end) { | |
1166 | + int i = start; | |
1167 | + start = end; | |
1168 | + end = i; | |
1169 | + } | |
1170 | + changeParagraphStyle(null, modifier, | |
1171 | + view.getTextPositionAt(start), | |
1172 | + view.getTextPositionAt(end), | |
1173 | + scroll); | |
1174 | + } | |
1175 | + | |
1176 | + /** | |
1177 | + * Modifies the paragraph style in the range of the current selection by | |
1178 | + * the specified paragraph style modifier. | |
1179 | + * @param modifier the paragraph style modifier. | |
1180 | + * @see #modifySelectionParagraphStyle(jp.kyasu.graphics.ParagraphStyleModifier, boolean) | |
1181 | + */ | |
1182 | + public void modifySelectionParagraphStyle(ParagraphStyleModifier modifier) | |
1183 | + { | |
1184 | + modifySelectionParagraphStyle(modifier, true); | |
1185 | + } | |
1186 | + | |
1187 | + /** | |
1188 | + * Modifies the paragraph style in the range of the current selection by | |
1189 | + * the specified paragraph style modifier. | |
1190 | + * @param modifier the paragraph style modifier. | |
1191 | + * @param scroll if true, scrolls the view after set done. | |
1192 | + * @see #modifySelectionParagraphStyle(jp.kyasu.graphics.ParagraphStyleModifier) | |
1193 | + * @see #modifyRangeParagraphStyle(jp.kyasu.graphics.ParagraphStyleModifier, int, int, boolean) | |
1194 | + */ | |
1195 | + public void modifySelectionParagraphStyle(ParagraphStyleModifier modifier, | |
1196 | + boolean scroll) | |
1197 | + { | |
1198 | + if (modifier == null) | |
1199 | + return; | |
1200 | + changeParagraphStyle(null, modifier, | |
1201 | + view.getSelectionBegin(), | |
1202 | + view.getSelectionEnd(), | |
1203 | + scroll); | |
1204 | + } | |
1205 | + | |
1206 | + /** | |
1207 | + * Prints the text in the view of this controller with the specified | |
1208 | + * flag determining to print a page number in footer. | |
1209 | + * | |
1210 | + * @param printPageNum if true, prints a page number in footer. | |
1211 | + */ | |
1212 | + public void print(boolean printPageNum) { | |
1213 | + print(RichText.DEFAULT_PRINT_INSETS, null, printPageNum); | |
1214 | + } | |
1215 | + | |
1216 | + /** | |
1217 | + * Prints the text in the view of this controller with the specified | |
1218 | + * header string and flag determining to print a page number in footer. | |
1219 | + * | |
1220 | + * @param header the header string. | |
1221 | + * @param printPageNum if true, prints a page number in footer. | |
1222 | + */ | |
1223 | + public void print(String header, boolean printPageNum) { | |
1224 | + print(RichText.DEFAULT_PRINT_INSETS, header, printPageNum); | |
1225 | + } | |
1226 | + | |
1227 | + /** | |
1228 | + * Prints the text in the view of this controller with the specified | |
1229 | + * insets, header string, and flag determining to print a page number | |
1230 | + * in footer. | |
1231 | + * | |
1232 | + * @param insets the insets of a printing medium (paper). | |
1233 | + * @param header the header string. | |
1234 | + * @param printPageNum if true, prints a page number in footer. | |
1235 | + */ | |
1236 | + public void print(Insets insets, String header, boolean printPageNum) { | |
1237 | + PrintJob job = | |
1238 | + view.getToolkit().getPrintJob(view.getFrame(), "Print", null); | |
1239 | + if (job == null) | |
1240 | + return; | |
1241 | + print(job, insets, header, printPageNum); | |
1242 | + } | |
1243 | + | |
1244 | + /** | |
1245 | + * Prints the text in the view of this controller to a print device | |
1246 | + * provided from the specified print job, with the specified header string | |
1247 | + * and flag determining to print a page number in footer. | |
1248 | + * | |
1249 | + * @param job the print job. | |
1250 | + * @param header the header string. | |
1251 | + * @param printPageNum if true, prints a page number in footer. | |
1252 | + */ | |
1253 | + public void print(PrintJob job, String header, boolean printPageNum) { | |
1254 | + print(job, RichText.DEFAULT_PRINT_INSETS, header, printPageNum); | |
1255 | + } | |
1256 | + | |
1257 | + /** | |
1258 | + * Prints the text in the view of this controller to a print device | |
1259 | + * provided from the specified print job, with the specified insets, | |
1260 | + * header string, and flag determining to print a page number in footer. | |
1261 | + * | |
1262 | + * @param job the print job. | |
1263 | + * @param insets the insets of a printing medium (paper). | |
1264 | + * @param header the header string. | |
1265 | + * @param printPageNum if true, prints a page number in footer. | |
1266 | + */ | |
1267 | + public synchronized void print(PrintJob job, Insets insets, | |
1268 | + String header, boolean printPageNum) | |
1269 | + { | |
1270 | + if (job == null || insets == null) | |
1271 | + return; | |
1272 | + | |
1273 | + Cursor save = view.getCursor(); | |
1274 | + try { | |
1275 | + view.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); | |
1276 | + model.getRichText().print(job, insets, view.getLineWrap(), | |
1277 | + header, printPageNum); | |
1278 | + } | |
1279 | + finally { view.setCursor(save); } | |
1280 | + } | |
1281 | + | |
1282 | + | |
1283 | + // ================ Listener ================ | |
1284 | + | |
1285 | + /** | |
1286 | + * Invoked when the mouse has been clicked on a component. | |
1287 | + * @see java.awt.event.MouseListener | |
1288 | + */ | |
1289 | + public void mouseClicked(MouseEvent e) { | |
1290 | + // mousePressed -> mouseReleased -> mouseClicked | |
1291 | + | |
1292 | + if (!view.isEnabled()) | |
1293 | + return; | |
1294 | + | |
1295 | + if (e.isConsumed()) | |
1296 | + return; | |
1297 | + | |
1298 | + int clickCount = e.getClickCount(); | |
1299 | + if (clickCount < 2) | |
1300 | + return; | |
1301 | + | |
1302 | + e.consume(); | |
1303 | + | |
1304 | + TextPositionInfo posInfo = view.getTextPositionNearby( | |
1305 | + view.getVisibleBegin(), | |
1306 | + e.getPoint()); | |
1307 | + | |
1308 | + if (clickCount == 2) { | |
1309 | + if (posInfo.textIndex == 0 || | |
1310 | + posInfo.textIndex >= model.getRichText().length()) | |
1311 | + { | |
1312 | + select_all(); | |
1313 | + return; | |
1314 | + } | |
1315 | + if (select_braces(posInfo)) { | |
1316 | + return; | |
1317 | + } | |
1318 | + select_word(posInfo); | |
1319 | + return; | |
1320 | + } | |
1321 | + else if (clickCount == 3) { | |
1322 | + select_line(posInfo); | |
1323 | + return; | |
1324 | + } | |
1325 | + else { | |
1326 | + if (view.selectionIsCaret() && | |
1327 | + view.getSelectionBegin().textIndex == posInfo.textIndex) | |
1328 | + { | |
1329 | + return; | |
1330 | + } | |
1331 | + view.scrollTo(posInfo); | |
1332 | + view.hideSelection(); | |
1333 | + setSelectionBeginEnd(posInfo); | |
1334 | + view.showSelection(); | |
1335 | + notifyTextPositionListeners(); | |
1336 | + dragOrigin = dragCurrent = posInfo; | |
1337 | + } | |
1338 | + } | |
1339 | + | |
1340 | + /** | |
1341 | + * Invoked when the mouse has been pressed on a component. | |
1342 | + * @see java.awt.event.MouseListener | |
1343 | + */ | |
1344 | + public void mousePressed(MouseEvent e) { | |
1345 | + if (!view.isEnabled()) | |
1346 | + return; | |
1347 | + | |
1348 | + if (e.isConsumed()) | |
1349 | + return; | |
1350 | + | |
1351 | + //if (clickToFocus) { | |
1352 | + if (view.isEditable()) { | |
1353 | + view.requestFocus(); | |
1354 | + view.startTextCaret(); | |
1355 | + e.consume(); | |
1356 | + } | |
1357 | + | |
1358 | + if (e.getClickCount() > 1) | |
1359 | + return; | |
1360 | + | |
1361 | + e.consume(); | |
1362 | + | |
1363 | + //if (view.isEditable() && (e.isPopupTrigger() || e.isMetaDown())) { | |
1364 | + if (e.isPopupTrigger() || e.isMetaDown()) { | |
1365 | + if (popupMenu != null) { | |
1366 | + popupMenu.show(e.getComponent(), e.getX(), e.getY()); | |
1367 | + } | |
1368 | + return; | |
1369 | + } | |
1370 | + | |
1371 | + TextPositionInfo posInfo = view.getTextPositionNearby( | |
1372 | + view.getVisibleBegin(), | |
1373 | + e.getPoint()); | |
1374 | + | |
1375 | + if (e.isShiftDown()) { | |
1376 | + TextPositionInfo begin = view.getSelectionBegin(); | |
1377 | + TextPositionInfo end = view.getSelectionEnd(); | |
1378 | + if (begin == null || end == null) { | |
1379 | + view.scrollTo(posInfo); | |
1380 | + view.hideSelection(); | |
1381 | + setSelectionBeginEnd(posInfo); | |
1382 | + view.showSelection(); | |
1383 | + dragOrigin = dragCurrent = posInfo; | |
1384 | + } | |
1385 | + else if (begin.textIndex <= posInfo.textIndex) { | |
1386 | + dragOrigin = begin; | |
1387 | + dragCurrent = end; | |
1388 | + } | |
1389 | + else { | |
1390 | + dragOrigin = end; | |
1391 | + dragCurrent = begin; | |
1392 | + } | |
1393 | + dragSelectionTo(posInfo); | |
1394 | + return; | |
1395 | + } | |
1396 | + | |
1397 | + view.scrollTo(posInfo); | |
1398 | + view.hideSelection(); | |
1399 | + setSelectionBeginEnd(posInfo); | |
1400 | + view.showSelection(); | |
1401 | + dragOrigin = dragCurrent = posInfo; | |
1402 | + | |
1403 | + /* | |
1404 | + if (clickable && !e.isControlDown()) { | |
1405 | + int textIndex = posInfo.textIndex; | |
1406 | + if ((e.getX() - view.offset.x) < posInfo.x) { | |
1407 | + --textIndex; | |
1408 | + } | |
1409 | + Text text = model.getRichText().getText(); | |
1410 | + if (textIndex >= 0 && textIndex < text.length()) { | |
1411 | + TextStyle ts = text.getTextStyleAt(textIndex); | |
1412 | + ClickableTextAction action = ts.getClickableTextAction(); | |
1413 | + if (action != null) { | |
1414 | + view.performClickableTextAction(action); | |
1415 | + } | |
1416 | + } | |
1417 | + } | |
1418 | + */ | |
1419 | + } | |
1420 | + | |
1421 | + /** | |
1422 | + * Invoked when the mouse has been released on a component. | |
1423 | + * @see java.awt.event.MouseListener | |
1424 | + */ | |
1425 | + public void mouseReleased(MouseEvent e) { | |
1426 | + if (!view.isEnabled()) | |
1427 | + return; | |
1428 | + | |
1429 | + if (e.isConsumed()) | |
1430 | + return; | |
1431 | + | |
1432 | + e.consume(); | |
1433 | + | |
1434 | + dragOrigin = dragCurrent = null; | |
1435 | + | |
1436 | + if (e.getClickCount() < 2) | |
1437 | + notifyTextPositionListeners(); | |
1438 | + | |
1439 | + if (clickable && !e.isControlDown() && view.selectionIsCaret()) { | |
1440 | + if (e.getClickCount() > 1) | |
1441 | + return; | |
1442 | + if (e.isPopupTrigger() || e.isMetaDown()) | |
1443 | + return; | |
1444 | + if (e.isShiftDown()) | |
1445 | + return; | |
1446 | + | |
1447 | + TextPositionInfo posInfo = view.getSelectionBegin(); | |
1448 | + int textIndex = posInfo.textIndex; | |
1449 | + if ((e.getX() - view.offset.x) < posInfo.x) { | |
1450 | + --textIndex; | |
1451 | + } | |
1452 | + Text text = model.getRichText().getText(); | |
1453 | + if (textIndex >= 0 && textIndex < text.length()) { | |
1454 | + TextStyle ts = text.getTextStyleAt(textIndex); | |
1455 | + ClickableTextAction action = ts.getClickableTextAction(); | |
1456 | + if (action != null) { | |
1457 | + view.performClickableTextAction(action); | |
1458 | + } | |
1459 | + } | |
1460 | + } | |
1461 | + } | |
1462 | + | |
1463 | + /** | |
1464 | + * Invoked when the mouse enters a component. | |
1465 | + * @see java.awt.event.MouseListener | |
1466 | + */ | |
1467 | + public void mouseEntered(MouseEvent e) { | |
1468 | + if (!view.isEnabled()) | |
1469 | + return; | |
1470 | + if (!view.isEditable()) | |
1471 | + return; | |
1472 | + if (!clickToFocus) { | |
1473 | + view.requestFocus(); | |
1474 | + } | |
1475 | + } | |
1476 | + | |
1477 | + /** | |
1478 | + * Invoked when the mouse button is pressed on a component and then dragged. | |
1479 | + * @see java.awt.event.MouseMotionListener | |
1480 | + */ | |
1481 | + public void mouseDragged(MouseEvent e) { | |
1482 | + // mousePressed -> mouseDragged* -> mouseReleased | |
1483 | + | |
1484 | + if (!view.isEnabled()) | |
1485 | + return; | |
1486 | + | |
1487 | + if (e.isConsumed()) | |
1488 | + return; | |
1489 | + | |
1490 | + e.consume(); | |
1491 | + | |
1492 | + dragSelectionTo(view.getTextPositionNearby( | |
1493 | + view.getVisibleBegin(), | |
1494 | + e.getPoint())); | |
1495 | + } | |
1496 | + | |
1497 | + /** | |
1498 | + * Invoked when a key has been typed. | |
1499 | + * @see java.awt.event.KeyListener | |
1500 | + */ | |
1501 | + public void keyTyped(KeyEvent e) { | |
1502 | + // keyPressed -> keyTyped -> keyReleased | |
1503 | + // This method is NOT invoked by the special keys (CTL, SHIFT, etc.). | |
1504 | + | |
1505 | + if (!view.isEnabled()) | |
1506 | + return; | |
1507 | + | |
1508 | + if (e.isConsumed()) | |
1509 | + return; | |
1510 | + | |
1511 | + if (!view.isEditable()) { | |
1512 | + e.consume(); | |
1513 | + return; | |
1514 | + } | |
1515 | + | |
1516 | + /* | |
1517 | + if (e.getKeyCode() != KeyEvent.VK_UNDEFINED) | |
1518 | + return; | |
1519 | + */ | |
1520 | + | |
1521 | + KeyAction a = keyBinding.getKeyAction(e); | |
1522 | + if (a != null) { | |
1523 | + a.perform(e.getKeyChar()); | |
1524 | + e.consume(); | |
1525 | + } | |
1526 | + } | |
1527 | + | |
1528 | + /** | |
1529 | + * Invoked when a key has been pressed. | |
1530 | + * @see java.awt.event.KeyListener | |
1531 | + */ | |
1532 | + public void keyPressed(KeyEvent e) { | |
1533 | + // This method is invoked by the special keys (CTL, SHIFT, etc.). | |
1534 | + | |
1535 | + if (!view.isEnabled()) | |
1536 | + return; | |
1537 | + | |
1538 | + if (e.isConsumed()) | |
1539 | + return; | |
1540 | + | |
1541 | + if (!view.isEditable()) { | |
1542 | + //view.getToolkit().beep(); | |
1543 | + e.consume(); | |
1544 | + return; | |
1545 | + } | |
1546 | + | |
1547 | + /* | |
1548 | + if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) | |
1549 | + return; | |
1550 | + */ | |
1551 | + | |
1552 | + KeyAction a = keyBinding.getKeyAction(e); | |
1553 | + if (a != null) { | |
1554 | + a.perform(e.getKeyChar()); | |
1555 | + e.consume(); | |
1556 | + } | |
1557 | + } | |
1558 | + | |
1559 | + /** | |
1560 | + * Invoked when a component gains the keyboard focus. | |
1561 | + * @see java.awt.event.FocusListener | |
1562 | + */ | |
1563 | + public void focusGained(FocusEvent e) { | |
1564 | + //synchronized (view) { | |
1565 | + if (!view.isEnabled()) | |
1566 | + return; | |
1567 | + //if (e.isTemporary()) return; | |
1568 | + super.focusGained(e); | |
1569 | + view.startTextCaret(); | |
1570 | + //} | |
1571 | + } | |
1572 | + | |
1573 | + /** | |
1574 | + * Invoked when a component loses the keyboard focus. | |
1575 | + * @see java.awt.event.FocusListener | |
1576 | + */ | |
1577 | + public void focusLost(FocusEvent e) { | |
1578 | + //synchronized (view) { | |
1579 | + if (!view.isEnabled()) | |
1580 | + return; | |
1581 | + //if (e.isTemporary()) return; | |
1582 | + super.focusLost(e); | |
1583 | + view.stopTextCaret(); | |
1584 | + //} | |
1585 | + } | |
1586 | + | |
1587 | + /** | |
1588 | + * Invoked when an action occurs. | |
1589 | + * @see java.awt.event.ActionListener | |
1590 | + */ | |
1591 | + public void actionPerformed(ActionEvent e) { | |
1592 | + if (!view.isEnabled()) | |
1593 | + return; | |
1594 | + | |
1595 | + String command = e.getActionCommand(); | |
1596 | + boolean isShiftDown = ((e.getModifiers() & Event.SHIFT_MASK) != 0); | |
1597 | + if (command.equals(A_COPY)) { | |
1598 | + copy_clipboard(); | |
1599 | + } | |
1600 | + else if (command.equals(A_CUT)) { | |
1601 | + cut_clipboard(); | |
1602 | + } | |
1603 | + else if (command.equals(A_PASTE)) { | |
1604 | + if (!isShiftDown) | |
1605 | + paste_clipboard(); | |
1606 | + else | |
1607 | + paste_cutbuffer(); | |
1608 | + } | |
1609 | + else if (command.equals(A_FIND)) { | |
1610 | + find_word(); | |
1611 | + } | |
1612 | + else if (command.equals(A_UNDO)) { | |
1613 | + undo(); | |
1614 | + } | |
1615 | + else if (command.equals(A_PRINT)) { | |
1616 | + print(true); | |
1617 | + } | |
1618 | + } | |
1619 | + | |
1620 | + | |
1621 | + // ================ Functions ================ | |
1622 | + | |
1623 | + static protected final String OPEN_BRACES = "({[<\"'"; | |
1624 | + static protected final String CLOSE_BRACES = ")}]>\"'"; | |
1625 | + | |
1626 | + /** | |
1627 | + * If the cursor is inside the selection, deletes the entire selection. | |
1628 | + * Inserts character at the insertion cursor. | |
1629 | + */ | |
1630 | + public void insert_character(char c) { | |
1631 | + if (c == Text.LINE_BREAK_CHAR && | |
1632 | + !model.getRichText().getRichTextStyle(). | |
1633 | + isJavaLineSeparatorWithBreak()) | |
1634 | + { | |
1635 | + return; | |
1636 | + } | |
1637 | + typeInText.setChar(0, c); | |
1638 | + replaceSelection(typeInText); | |
1639 | + } | |
1640 | + | |
1641 | + /** | |
1642 | + * Undo the last change. | |
1643 | + */ | |
1644 | + public void undo() { | |
1645 | + if (lastUndo != null) { | |
1646 | + lastUndo = lastUndo.undo(); | |
1647 | + } | |
1648 | + } | |
1649 | + | |
1650 | + /** | |
1651 | + * Clears the undo of the last change. | |
1652 | + */ | |
1653 | + public void clearUndo() { | |
1654 | + lastUndo = null; | |
1655 | + } | |
1656 | + | |
1657 | + /** | |
1658 | + * Sets the undo. | |
1659 | + */ | |
1660 | + public void setUndo(Undo undo) { | |
1661 | + lastUndo = undo; | |
1662 | + } | |
1663 | + | |
1664 | + /** | |
1665 | + * Copies the current selection to the clipboard. | |
1666 | + */ | |
1667 | + public void copy_clipboard() { | |
1668 | + Text text = view.getSelectedText(); | |
1669 | + if (!text.isEmpty()) { | |
1670 | + setCutBuffer(text); | |
1671 | + setClipboardText(text); | |
1672 | + } | |
1673 | + } | |
1674 | + | |
1675 | + /** | |
1676 | + * Cuts the current selection to the clipboard. | |
1677 | + */ | |
1678 | + public void cut_clipboard() { | |
1679 | + Text text = view.getSelectedText(); | |
1680 | + if (!text.isEmpty()) { | |
1681 | + setCutBuffer(text); | |
1682 | + setClipboardText(text); | |
1683 | + replaceSelection(new Text()); | |
1684 | + } | |
1685 | + } | |
1686 | + | |
1687 | + /** | |
1688 | + * Pastes the the cut buffer before the insertion cursor. | |
1689 | + */ | |
1690 | + public void paste_cutbuffer() { | |
1691 | + Text text = getCutBufferText(); | |
1692 | + if (text != null) { | |
1693 | + replaceSelection(text); | |
1694 | + } | |
1695 | + } | |
1696 | + | |
1697 | + /** | |
1698 | + * Pastes the the clipboard before the insertion cursor. | |
1699 | + */ | |
1700 | + public void paste_clipboard() { | |
1701 | + if (lostClipboardOwnership) { | |
1702 | + Text text = getClipboardText(); | |
1703 | + if (text != null) { | |
1704 | + replaceSelection(text); | |
1705 | + } | |
1706 | + } | |
1707 | + else { | |
1708 | + paste_cutbuffer(); | |
1709 | + } | |
1710 | + } | |
1711 | + | |
1712 | + /** | |
1713 | + * Select all text. | |
1714 | + */ | |
1715 | + public void select_all() { | |
1716 | + view.hideSelection(); | |
1717 | + setSelectionBeginEnd( | |
1718 | + view.getTextPositionAt(0), | |
1719 | + view.getTextPositionAt(model.getRichText().length())); | |
1720 | + view.showSelection(); | |
1721 | + view.scrollTo(view.getSelectionBegin()); | |
1722 | + notifyTextPositionListeners(); | |
1723 | + } | |
1724 | + | |
1725 | + /** | |
1726 | + * Select braces at the selection start position. | |
1727 | + * @see #select_braces(jp.kyasu.graphics.text.TextPositionInfo) | |
1728 | + */ | |
1729 | + public boolean select_braces() { | |
1730 | + return select_braces(view.getSelectionBegin()); | |
1731 | + } | |
1732 | + | |
1733 | + /** | |
1734 | + * Select braces at the specified text position. | |
1735 | + * @see #select_braces() | |
1736 | + */ | |
1737 | + public boolean select_braces(TextPositionInfo posInfo) { | |
1738 | + int index = posInfo.textIndex; | |
1739 | + Text text = model.getRichText().getText(); | |
1740 | + int bIndex; | |
1741 | + if (index > 0 && | |
1742 | + (bIndex = OPEN_BRACES.indexOf(text.getChar(index - 1))) >= 0) | |
1743 | + { | |
1744 | + int matchCount = 0; | |
1745 | + char ob = OPEN_BRACES.charAt(bIndex); | |
1746 | + char cb = CLOSE_BRACES.charAt(bIndex); | |
1747 | + CharacterIterator iterator = text.getCharacterIterator(index); | |
1748 | + for (char c = iterator.current(); | |
1749 | + c != CharacterIterator.DONE; | |
1750 | + c = iterator.next()) | |
1751 | + { | |
1752 | + if (c == cb) { | |
1753 | + if (matchCount == 0) { | |
1754 | + view.hideSelection(); | |
1755 | + setSelectionBeginEnd( | |
1756 | + posInfo, | |
1757 | + view.getTextPositionNearby(posInfo, | |
1758 | + iterator.getIndex())); | |
1759 | + view.showSelection(); | |
1760 | + view.scrollTo(view.getSelectionBegin()); | |
1761 | + notifyTextPositionListeners(); | |
1762 | + return true; | |
1763 | + } | |
1764 | + --matchCount; | |
1765 | + } | |
1766 | + else if (c == ob) { | |
1767 | + matchCount++; | |
1768 | + } | |
1769 | + } | |
1770 | + } | |
1771 | + if (index > 0 && index < text.length() && | |
1772 | + (bIndex = CLOSE_BRACES.indexOf(text.getChar(index))) >= 0) | |
1773 | + { | |
1774 | + int matchCount = 0; | |
1775 | + char ob = OPEN_BRACES.charAt(bIndex); | |
1776 | + char cb = CLOSE_BRACES.charAt(bIndex); | |
1777 | + CharacterIterator iterator = text.getCharacterIterator(index - 1); | |
1778 | + for (char c = iterator.current(); | |
1779 | + c != CharacterIterator.DONE; | |
1780 | + c = iterator.previous()) | |
1781 | + { | |
1782 | + if (c == ob) { | |
1783 | + if (matchCount == 0) { | |
1784 | + view.hideSelection(); | |
1785 | + setSelectionBeginEnd( | |
1786 | + view.getTextPositionNearby(posInfo, | |
1787 | + iterator.getIndex() + 1), | |
1788 | + posInfo); | |
1789 | + view.showSelection(); | |
1790 | + view.scrollTo(view.getSelectionBegin()); | |
1791 | + notifyTextPositionListeners(); | |
1792 | + return true; | |
1793 | + } | |
1794 | + --matchCount; | |
1795 | + } | |
1796 | + else if (c == cb) { | |
1797 | + matchCount++; | |
1798 | + } | |
1799 | + } | |
1800 | + } | |
1801 | + return false; | |
1802 | + } | |
1803 | + | |
1804 | + /** | |
1805 | + * Select a line at the selection start position. | |
1806 | + * @see #select_line(jp.kyasu.graphics.text.TextPositionInfo) | |
1807 | + */ | |
1808 | + public void select_line() { | |
1809 | + select_line(view.getSelectionBegin()); | |
1810 | + } | |
1811 | + | |
1812 | + /** | |
1813 | + * Select a line at the specified text position. | |
1814 | + * @see #select_line() | |
1815 | + */ | |
1816 | + public void select_line(TextPositionInfo posInfo) { | |
1817 | + int paraBegin = | |
1818 | + model.getRichText().paragraphBeginIndexOf(posInfo.textIndex); | |
1819 | + int paraEnd = | |
1820 | + model.getRichText().paragraphEndIndexOf(posInfo.textIndex); | |
1821 | + view.hideSelection(); | |
1822 | + setSelectionBeginEnd( | |
1823 | + view.getTextPositionNearby(posInfo, paraBegin), | |
1824 | + view.getTextPositionNearby(posInfo, paraEnd)); | |
1825 | + view.showSelection(); | |
1826 | + view.scrollTo(view.getSelectionBegin()); | |
1827 | + notifyTextPositionListeners(); | |
1828 | + } | |
1829 | + | |
1830 | + /** | |
1831 | + * Select a word at the selection start position. | |
1832 | + * @see #select_word(jp.kyasu.graphics.text.TextPositionInfo) | |
1833 | + */ | |
1834 | + public void select_word() { | |
1835 | + select_word(view.getSelectionBegin()); | |
1836 | + } | |
1837 | + | |
1838 | + /** | |
1839 | + * Select a word at the specified text position. | |
1840 | + * @see #select_word() | |
1841 | + */ | |
1842 | + public void select_word(TextPositionInfo posInfo) { | |
1843 | + Text text = model.getRichText().getText(); | |
1844 | + int index = posInfo.textIndex; | |
1845 | + if (index >= text.length()) | |
1846 | + index = text.length() - 1; | |
1847 | + BreakIterator boundary= BreakIterator.getWordInstance(view.getLocale()); | |
1848 | + boundary.setText(text.getCharacterIterator(index)); | |
1849 | + int end = boundary.following(index); | |
1850 | + int begin = boundary.previous(); | |
1851 | + if (begin != BreakIterator.DONE && | |
1852 | + end != BreakIterator.DONE && | |
1853 | + begin <= index && index < end) | |
1854 | + { | |
1855 | + view.hideSelection(); | |
1856 | + setSelectionBeginEnd( | |
1857 | + view.getTextPositionNearby(posInfo, begin), | |
1858 | + view.getTextPositionNearby(posInfo, end)); | |
1859 | + view.showSelection(); | |
1860 | + view.scrollTo(view.getSelectionBegin()); | |
1861 | + notifyTextPositionListeners(); | |
1862 | + } | |
1863 | + } | |
1864 | + | |
1865 | + /** | |
1866 | + * Finds the word and move the insertion cursor to the founded word. | |
1867 | + * @see #find_word(java.lang.String) | |
1868 | + * @see #find_word(java.lang.String, java.lang.String) | |
1869 | + * @see #find_word(java.lang.String, java.lang.String, int) | |
1870 | + */ | |
1871 | + public void find_word() { | |
1872 | + String sel = view.getSelectedText().toString(); | |
1873 | + if (sel.length() > 32) | |
1874 | + sel = sel.substring(0, 32); | |
1875 | + Dialog dialog = createFindDialog(sel); | |
1876 | + dialog.setVisible(true); | |
1877 | + } | |
1878 | + | |
1879 | + /** | |
1880 | + * Finds the word and move the insertion cursor to the founded word. | |
1881 | + * @param find the word to be found. | |
1882 | + * @return true if the word has been found, false otherwise. | |
1883 | + * @see #find_word() | |
1884 | + * @see #find_word(java.lang.String, java.lang.String) | |
1885 | + * @see #find_word(java.lang.String, java.lang.String, int) | |
1886 | + */ | |
1887 | + public boolean find_word(String find) { | |
1888 | + return find_word(find, null); | |
1889 | + } | |
1890 | + | |
1891 | + /** | |
1892 | + * Finds the word and move the insertion cursor to the founded word. | |
1893 | + * @param find the word to be found. | |
1894 | + * @param rep the replacement string. | |
1895 | + * @return true if the word has been found, false otherwise. | |
1896 | + * @see #find_word() | |
1897 | + * @see #find_word(java.lang.String) | |
1898 | + * @see #find_word(java.lang.String, java.lang.String, int) | |
1899 | + */ | |
1900 | + public boolean find_word(String find, String rep) { | |
1901 | + return find_word(find, rep, view.getSelectionBegin().textIndex + 1); | |
1902 | + } | |
1903 | + | |
1904 | + /** | |
1905 | + * Finds the word and move the insertion cursor to the founded word. | |
1906 | + * @param find the word to be found. | |
1907 | + * @param rep the replacement string. | |
1908 | + * @param startIndex the starting index to find. | |
1909 | + * @return true if the word has been found, false otherwise. | |
1910 | + * @see #find_word(java.lang.String) | |
1911 | + * @see #find_word(java.lang.String, java.lang.String) | |
1912 | + * @see #find_word(java.lang.String, java.lang.String, int) | |
1913 | + */ | |
1914 | + public boolean find_word(String find, String rep, int startIndex) { | |
1915 | + if (find == null || find.length() == 0) | |
1916 | + return false; | |
1917 | + Text text = model.getRichText().getText(); | |
1918 | + if (startIndex >= text.length()) { | |
1919 | + startIndex = 0; | |
1920 | + } | |
1921 | + int textIndex = text.indexOf(find, startIndex); | |
1922 | + if (textIndex >= 0) { | |
1923 | + TextPositionInfo posInfo = view.getTextPositionAt(textIndex); | |
1924 | + view.hideSelection(); | |
1925 | + setSelectionBeginEnd( | |
1926 | + posInfo, | |
1927 | + view.getTextPositionNearby(posInfo, textIndex + find.length())); | |
1928 | + if (rep != null) { | |
1929 | + view.showSelection(); | |
1930 | + replaceSelection(rep); | |
1931 | + } | |
1932 | + else { | |
1933 | + view.scrollTo(view.getSelectionBegin()); | |
1934 | + view.showSelection(); | |
1935 | + notifyTextPositionListeners(); | |
1936 | + } | |
1937 | + return true; | |
1938 | + } | |
1939 | + else { | |
1940 | + if (Dialog.confirm(view.getFrame(), | |
1941 | + getResourceString( | |
1942 | + "kfc.text.findContinueLabel", | |
1943 | + "End of text reached; continue from beggining?"))) | |
1944 | + { | |
1945 | + return find_word(find, rep, 0); | |
1946 | + } | |
1947 | + } | |
1948 | + return false; | |
1949 | + } | |
1950 | + | |
1951 | + /** | |
1952 | + * Creates a dialog for finding a word. | |
1953 | + * @param initStr the initial string to prompt. | |
1954 | + * @see #find_word() | |
1955 | + */ | |
1956 | + protected Dialog createFindDialog(String initStr) { | |
1957 | + final Dialog dialog = new Dialog( | |
1958 | + view.getFrame(), | |
1959 | + getResourceString("kfc.text.findTitle", "Find"), | |
1960 | + true); | |
1961 | + GridBagLayout gridbag = new GridBagLayout(); | |
1962 | + GridBagConstraints c = new GridBagConstraints(); | |
1963 | + Panel p1 = new Panel(); | |
1964 | + p1.setLayout(gridbag); | |
1965 | + Label label = new Label( | |
1966 | + getResourceString("kfc.text.findFieldLabel", "Find:")); | |
1967 | + c.gridwidth = GridBagConstraints.RELATIVE; | |
1968 | + gridbag.setConstraints(label, c); | |
1969 | + p1.add(label); | |
1970 | + final TextField ffield = new TextField(30); | |
1971 | + // Do not transfer focus. | |
1972 | + Keymap keymap = ffield.getKeymap(); | |
1973 | + keymap.setKeyCodeMap(KeyEvent.VK_TAB, "tab"); | |
1974 | + ffield.setKeymap(keymap); | |
1975 | + if (initStr != null) { | |
1976 | + ffield.setText(initStr); | |
1977 | + } | |
1978 | + c.gridwidth = GridBagConstraints.REMAINDER; | |
1979 | + gridbag.setConstraints(ffield, c); | |
1980 | + p1.add(ffield); | |
1981 | + label = new Label( | |
1982 | + getResourceString("kfc.text.replaceFieldLabel", "Replace:")); | |
1983 | + if (!view.isEditable()) label.setEnabled(false); | |
1984 | + c.gridwidth = GridBagConstraints.RELATIVE; | |
1985 | + gridbag.setConstraints(label, c); | |
1986 | + p1.add(label); | |
1987 | + final TextField rfield = new TextField(30); | |
1988 | + // Do not transfer focus. | |
1989 | + keymap = rfield.getKeymap(); | |
1990 | + keymap.setKeyCodeMap(KeyEvent.VK_TAB, "tab"); | |
1991 | + rfield.setKeymap(keymap); | |
1992 | + if (!view.isEditable()) rfield.setEditable(false); | |
1993 | + c.gridwidth = GridBagConstraints.REMAINDER; | |
1994 | + gridbag.setConstraints(rfield, c); | |
1995 | + p1.add(rfield); | |
1996 | + dialog.add(p1, BorderLayout.CENTER); | |
1997 | + | |
1998 | + Panel p2 = new Panel(); | |
1999 | + Button b = new Button( | |
2000 | + getResourceString("kfc.text.findStartLabel", "Find")); | |
2001 | + ActionListener al = new ActionListener() { | |
2002 | + public void actionPerformed(ActionEvent e) { | |
2003 | + view.setSelectionVisible(true); | |
2004 | + find_word(ffield.getText(), null); | |
2005 | + } | |
2006 | + }; | |
2007 | + ffield.addActionListener(al); | |
2008 | + b.addActionListener(al); | |
2009 | + p2.add(b); | |
2010 | + /* | |
2011 | + Button repB = new Button( | |
2012 | + getResourceString("kfc.text.replaceStartLabel", "Replace")); | |
2013 | + */ | |
2014 | + Button repB = new Button(new VActiveButton( | |
2015 | + getResourceString("kfc.text.replaceStartLabel", "Replace"))); | |
2016 | + repB.addActionListener(new ActionListener() { | |
2017 | + public void actionPerformed(ActionEvent e) { | |
2018 | + view.setSelectionVisible(true); | |
2019 | + find_word(ffield.getText(), rfield.getText()); | |
2020 | + } | |
2021 | + }); | |
2022 | + if (!view.isEditable()) repB.setEnabled(false); | |
2023 | + p2.add(repB); | |
2024 | + /* | |
2025 | + Button repAllB = new Button( | |
2026 | + getResourceString("kfc.text.replaceAllLabel", "Replace All")); | |
2027 | + */ | |
2028 | + Button repAllB = new Button(new VActiveButton( | |
2029 | + getResourceString("kfc.text.replaceAllLabel", "Replace All"))); | |
2030 | + repAllB.addActionListener(new ActionListener() { | |
2031 | + public void actionPerformed(ActionEvent e) { | |
2032 | + view.setSelectionVisible(true); | |
2033 | + while (find_word(ffield.getText(), rfield.getText())) | |
2034 | + view.setSelectionVisible(true); | |
2035 | + } | |
2036 | + }); | |
2037 | + if (!view.isEditable()) repAllB.setEnabled(false); | |
2038 | + p2.add(repAllB); | |
2039 | + b = new Button( | |
2040 | + getResourceString("kfc.text.findEndLabel", "Close")); | |
2041 | + b.addActionListener(new ActionListener() { | |
2042 | + public void actionPerformed(ActionEvent e) { | |
2043 | + dialog.setVisible(false); | |
2044 | + dialog.dispose(); | |
2045 | + } | |
2046 | + }); | |
2047 | + p2.add(b); | |
2048 | + | |
2049 | + dialog.add(p2, BorderLayout.SOUTH); | |
2050 | + dialog.pack(); | |
2051 | + return dialog; | |
2052 | + } | |
2053 | + | |
2054 | + | |
2055 | + // ================ Protected ================ | |
2056 | + | |
2057 | + /** | |
2058 | + * Creates a menu for the editing. | |
2059 | + */ | |
2060 | + protected Menu createEditMenu() { | |
2061 | + Menu menu = new Menu(L_EDIT); | |
2062 | + menu.add(createMenuItem(L_COPY, A_COPY, null)); | |
2063 | + menu.add(createMenuItem(L_CUT, A_CUT, null)); | |
2064 | + menu.add(createMenuItem(L_PASTE, A_PASTE, null)); | |
2065 | + menu.addSeparator(); | |
2066 | + menu.add(createMenuItem(L_FIND, A_FIND, null)); | |
2067 | + menu.addSeparator(); | |
2068 | + menu.add(createMenuItem(L_UNDO, A_UNDO, null)); | |
2069 | + menu.addSeparator(); | |
2070 | + menu.add(createMenuItem(L_PRINT, A_PRINT, null)); | |
2071 | + return menu; | |
2072 | + } | |
2073 | + | |
2074 | + /** | |
2075 | + * Creates a popup menu. | |
2076 | + */ | |
2077 | + protected PopupMenu createPopupMenu() { | |
2078 | + PopupMenu menu = new PopupMenu(); | |
2079 | + menu.add(createMenuItem(L_COPY, A_COPY, null)); | |
2080 | + menu.add(createMenuItem(L_CUT, A_CUT, null)); | |
2081 | + menu.add(createMenuItem(L_PASTE, A_PASTE, null)); | |
2082 | + menu.addSeparator(); | |
2083 | + menu.add(createMenuItem(L_FIND, A_FIND, null)); | |
2084 | + menu.addSeparator(); | |
2085 | + menu.add(createMenuItem(L_UNDO, A_UNDO, null)); | |
2086 | + menu.addSeparator(); | |
2087 | + menu.add(createMenuItem(L_PRINT, A_PRINT, null)); | |
2088 | + return menu; | |
2089 | + } | |
2090 | + | |
2091 | + /** | |
2092 | + * Creates a menu item. | |
2093 | + */ | |
2094 | + protected MenuItem createMenuItem(String label, String action, | |
2095 | + String shortcut) | |
2096 | + { | |
2097 | + MenuItem mi = new MenuItem(label); | |
2098 | + if (shortcut != null && shortcut.length() > 0) { | |
2099 | + mi.setShortcut(new MenuShortcut(shortcut.charAt(0))); | |
2100 | + } | |
2101 | + mi.setActionCommand(action); | |
2102 | + mi.addActionListener(this); | |
2103 | + return mi; | |
2104 | + } | |
2105 | + | |
2106 | + /** | |
2107 | + * Enables or disaples the specified menu. | |
2108 | + */ | |
2109 | + protected void setMenuEnabled(Menu menu, boolean b) { | |
2110 | + int count = menu.getItemCount(); | |
2111 | + for (int i = 0; i < count; i++) { | |
2112 | + MenuItem item = menu.getItem(i); | |
2113 | + /* | |
2114 | + if (!L_COPY.equals(item.getLabel()) && | |
2115 | + !L_FIND.equals(item.getLabel()) && | |
2116 | + !L_PRINT.equals(item.getLabel())) | |
2117 | + */ | |
2118 | + if (!A_COPY.equals(item.getActionCommand()) && | |
2119 | + !A_FIND.equals(item.getActionCommand()) && | |
2120 | + !A_PRINT.equals(item.getActionCommand())) | |
2121 | + { | |
2122 | + item.setEnabled(b); | |
2123 | + } | |
2124 | + } | |
2125 | + } | |
2126 | + | |
2127 | + /** | |
2128 | + * Sets the beginning position of the selection. | |
2129 | + */ | |
2130 | + protected void setSelectionBegin(TextPositionInfo posInfo) { | |
2131 | + view.selectionBegin = posInfo; | |
2132 | + setCurrentTypeIn(posInfo); | |
2133 | + } | |
2134 | + | |
2135 | + /** | |
2136 | + * Sets the ending position of the selection. | |
2137 | + */ | |
2138 | + protected void setSelectionEnd(TextPositionInfo posInfo) { | |
2139 | + view.selectionEnd = posInfo; | |
2140 | + } | |
2141 | + | |
2142 | + /** | |
2143 | + * Sets the beginning and ending positions of the selection. | |
2144 | + */ | |
2145 | + protected void setSelectionBeginEnd(TextPositionInfo posInfo) { | |
2146 | + view.selectionBegin = view.selectionEnd = posInfo; | |
2147 | + setCurrentTypeIn(posInfo); | |
2148 | + } | |
2149 | + | |
2150 | + /** | |
2151 | + * Sets the beginning and ending positions of the selection. | |
2152 | + */ | |
2153 | + protected void setSelectionBeginEnd(TextPositionInfo begin, | |
2154 | + TextPositionInfo end) | |
2155 | + { | |
2156 | + view.selectionBegin = begin; | |
2157 | + view.selectionEnd = end; | |
2158 | + setCurrentTypeIn(begin); | |
2159 | + } | |
2160 | + | |
2161 | + /** | |
2162 | + * Notifies the text position event to the text position listeners. | |
2163 | + */ | |
2164 | + protected void notifyTextPositionListeners() { | |
2165 | + if (selectionVisibleAtFocus && !view.selectionIsCaret() && | |
2166 | + !view.isSelectionVisible()) | |
2167 | + { | |
2168 | + view.setSelectionVisible(true); | |
2169 | + } | |
2170 | + view.notifyTextPositionListeners(); | |
2171 | + } | |
2172 | + | |
2173 | + /** | |
2174 | + * Sets the current (type in) text style from the specified text position. | |
2175 | + */ | |
2176 | + protected void setCurrentTypeIn(TextPositionInfo posInfo) { | |
2177 | + Text text = model.getRichText().getText(); | |
2178 | + int length = text.length(); | |
2179 | + if (posInfo == null || length == 0) { | |
2180 | + typeInStyle = model.getRichText().getRichTextStyle().getTextStyle(); | |
2181 | + typeInText = new Text(" ", typeInStyle); | |
2182 | + return; | |
2183 | + } | |
2184 | + int index = posInfo.textIndex; | |
2185 | + if (index > 0) --index; | |
2186 | + typeInStyle = text.getTextStyleAt(index); | |
2187 | + typeInText = new Text(" ", typeInStyle); | |
2188 | + } | |
2189 | + | |
2190 | + /** | |
2191 | + * Expands and moves the selection according to the specified text position. | |
2192 | + */ | |
2193 | + protected void dragSelectionTo(TextPositionInfo posInfo) { | |
2194 | + if (dragOrigin == null || dragCurrent == null) { | |
2195 | + // not happen | |
2196 | + dragOrigin = dragCurrent = posInfo; | |
2197 | + view.scrollTo(posInfo); | |
2198 | + view.hideSelection(); | |
2199 | + setSelectionBeginEnd(posInfo); | |
2200 | + view.showSelection(); | |
2201 | + return; | |
2202 | + } | |
2203 | + if (dragCurrent.textIndex == posInfo.textIndex) { | |
2204 | + return; | |
2205 | + } | |
2206 | + | |
2207 | + view.scrollTo(posInfo); | |
2208 | + | |
2209 | + if (dragOrigin.textIndex == posInfo.textIndex) { | |
2210 | + dragCurrent = posInfo; | |
2211 | + view.hideSelection(); | |
2212 | + setSelectionBeginEnd(posInfo); | |
2213 | + view.showSelection(); | |
2214 | + return; | |
2215 | + } | |
2216 | + if (dragOrigin.textIndex == dragCurrent.textIndex) { | |
2217 | + view.hideSelection(); | |
2218 | + dragCurrent = posInfo; | |
2219 | + if (dragOrigin.textIndex < dragCurrent.textIndex) | |
2220 | + setSelectionBeginEnd(dragOrigin, dragCurrent); | |
2221 | + else | |
2222 | + setSelectionBeginEnd(dragCurrent, dragOrigin); | |
2223 | + view.showSelection(); | |
2224 | + return; | |
2225 | + } | |
2226 | + if (dragOrigin.textIndex < dragCurrent.textIndex) { | |
2227 | + if (posInfo.textIndex < dragOrigin.textIndex) { | |
2228 | + view.hideSelection(); | |
2229 | + setSelectionBeginEnd(posInfo, dragOrigin); | |
2230 | + view.showSelection(); | |
2231 | + dragCurrent = posInfo; | |
2232 | + } | |
2233 | + else if (dragCurrent.textIndex < posInfo.textIndex) { | |
2234 | + view.paintSelection(dragCurrent, | |
2235 | + posInfo, | |
2236 | + view.getSelectionForeground(), | |
2237 | + view.getSelectionBackground()); | |
2238 | + setSelectionBeginEnd(dragOrigin, posInfo); | |
2239 | + dragCurrent = posInfo; | |
2240 | + } | |
2241 | + else { // dragOrigin < posInfo < dragCurrent | |
2242 | + view.paintSelection(posInfo, | |
2243 | + dragCurrent, | |
2244 | + view.getForeground(), | |
2245 | + view.getBackground()); | |
2246 | + setSelectionBeginEnd(dragOrigin, posInfo); | |
2247 | + dragCurrent = posInfo; | |
2248 | + } | |
2249 | + } | |
2250 | + else { // dragCurrent.textIndex < dragOrigin.textIndex | |
2251 | + if (posInfo.textIndex < dragCurrent.textIndex) { | |
2252 | + view.paintSelection(posInfo, | |
2253 | + dragCurrent, | |
2254 | + view.getSelectionForeground(), | |
2255 | + view.getSelectionBackground()); | |
2256 | + setSelectionBeginEnd(posInfo, dragOrigin); | |
2257 | + dragCurrent = posInfo; | |
2258 | + } | |
2259 | + else if (dragOrigin.textIndex < posInfo.textIndex) { | |
2260 | + view.hideSelection(); | |
2261 | + setSelectionBeginEnd(dragOrigin, posInfo); | |
2262 | + view.showSelection(); | |
2263 | + dragCurrent = posInfo; | |
2264 | + } | |
2265 | + else { // dragCurrent < posInfo < dragOrigin | |
2266 | + view.paintSelection(dragCurrent, | |
2267 | + posInfo, | |
2268 | + view.getForeground(), | |
2269 | + view.getBackground()); | |
2270 | + setSelectionBeginEnd(posInfo, dragOrigin); | |
2271 | + dragCurrent = posInfo; | |
2272 | + } | |
2273 | + } | |
2274 | + } | |
2275 | + | |
2276 | + /** | |
2277 | + * Replaces the text of the model with the specified text. | |
2278 | + * | |
2279 | + * @param text the replacement text. | |
2280 | + * @param begin the beginning position to replace, inclusive. | |
2281 | + * @param end the endign position to replace, exclusive. | |
2282 | + * @param scroll if true, scrolls the view after replace done. | |
2283 | + */ | |
2284 | + protected synchronized void replaceRange(Text text, | |
2285 | + TextPositionInfo begin, | |
2286 | + TextPositionInfo end, | |
2287 | + boolean scroll) | |
2288 | + { | |
2289 | + if (!view.isEditable() || begin == null || end == null) | |
2290 | + return; | |
2291 | + if (begin.textIndex == end.textIndex && text.length() == 0) | |
2292 | + return; | |
2293 | + | |
2294 | + lastUndo = model.replace(begin.textIndex, end.textIndex, text); | |
2295 | + | |
2296 | + if (view.isShowing()) { | |
2297 | + if (scroll) { | |
2298 | + view.scrollTo(view.getSelectionBegin()); | |
2299 | + } | |
2300 | + } | |
2301 | + | |
2302 | + /* | |
2303 | + int newIndex = begin.textIndex + text.length(); | |
2304 | + if (newIndex < 0) | |
2305 | + newIndex = 0; | |
2306 | + else if (newIndex > model.getRichText().length) | |
2307 | + newIndex = model.getRichText().length; | |
2308 | + TextPositionInfo newPos = null; | |
2309 | + if (view.getSelectionBegin().textIndex != newIndex) { | |
2310 | + if (view.getSelectionEnd().textIndex == newIndex) | |
2311 | + newPos = view.getSelectionEnd(); | |
2312 | + else | |
2313 | + newPos = view.getTextPositionAt(newIndex); | |
2314 | + } | |
2315 | + else if (view.getSelectionEnd().textIndex != newIndex) { | |
2316 | + // view.getSelectionBegin().textIndex == newIndex | |
2317 | + newPos = view.getSelectionBegin(); | |
2318 | + } | |
2319 | + | |
2320 | + if (!view.isShowing()) { | |
2321 | + if (newPos != null) { | |
2322 | + setSelectionBeginEnd(newPos); | |
2323 | + } | |
2324 | + } | |
2325 | + else { | |
2326 | + if (newPos != null) { | |
2327 | + view.hideSelection(); | |
2328 | + setSelectionBeginEnd(newPos); | |
2329 | + view.showSelection(); // show caret | |
2330 | + } | |
2331 | + if (scroll) { | |
2332 | + view.scrollTo(view.getSelectionBegin()); | |
2333 | + } | |
2334 | + } | |
2335 | + */ | |
2336 | + } | |
2337 | + | |
2338 | + /** | |
2339 | + * Changes the text style of the model with the specified style or modifier. | |
2340 | + * | |
2341 | + * @param style the new text style or <code>null</code>. | |
2342 | + * @param modifier the text style modifier or <code>null</code>. | |
2343 | + * @param begin the beginning position to change, inclusive. | |
2344 | + * @param end the endign position to change, exclusive. | |
2345 | + * @param scroll if true, scrolls the view after change done. | |
2346 | + */ | |
2347 | + protected synchronized void changeTextStyle(TextStyle style, | |
2348 | + TextStyleModifier modifier, | |
2349 | + TextPositionInfo begin, | |
2350 | + TextPositionInfo end, | |
2351 | + boolean scroll) | |
2352 | + { | |
2353 | + if (!view.isEditable() || begin == null || end == null) | |
2354 | + return; | |
2355 | + if (begin.textIndex == end.textIndex) | |
2356 | + return; | |
2357 | + | |
2358 | + /* | |
2359 | + int selBegin = view.getSelectionBegin().textIndex; | |
2360 | + int selEnd = view.getSelectionEnd().textIndex; | |
2361 | + */ | |
2362 | + | |
2363 | + if (style != null) { | |
2364 | + lastUndo = | |
2365 | + model.setTextStyle(begin.textIndex, end.textIndex, style); | |
2366 | + } | |
2367 | + else if (modifier != null) { | |
2368 | + lastUndo = | |
2369 | + model.modifyTextStyle(begin.textIndex, end.textIndex, modifier); | |
2370 | + } | |
2371 | + else { | |
2372 | + return; | |
2373 | + } | |
2374 | + | |
2375 | + if (view.isShowing()) { | |
2376 | + if (scroll) { | |
2377 | + view.scrollTo(view.getSelectionBegin()); | |
2378 | + } | |
2379 | + } | |
2380 | + | |
2381 | + /* | |
2382 | + TextPositionInfo selBeginPos = null; | |
2383 | + TextPositionInfo selEndPos = null; | |
2384 | + if (view.getSelectionBegin().textIndex != selBegin) { | |
2385 | + selBeginPos = view.getTextPositionAt(selBegin); | |
2386 | + } | |
2387 | + if (view.getSelectionEnd().textIndex != selEnd) { | |
2388 | + selEndPos = view.getTextPositionAt(selEnd); | |
2389 | + } | |
2390 | + | |
2391 | + if (!view.isShowing()) { | |
2392 | + if (selBeginPos != null || selEndPos != null) { | |
2393 | + setSelectionBeginEnd(selBeginPos, selEndPos); | |
2394 | + } | |
2395 | + } | |
2396 | + else { | |
2397 | + if (selBeginPos != null || selEndPos != null) { | |
2398 | + view.hideSelection(); | |
2399 | + setSelectionBeginEnd(selBeginPos, selEndPos); | |
2400 | + view.showSelection(); | |
2401 | + } | |
2402 | + if (scroll) { | |
2403 | + view.scrollTo(view.getSelectionBegin()); | |
2404 | + } | |
2405 | + } | |
2406 | + */ | |
2407 | + } | |
2408 | + | |
2409 | + /** | |
2410 | + * Changes the paragraph style of the model with the specified style or | |
2411 | + * modifier. | |
2412 | + * | |
2413 | + * @param style the new paragraph style or <code>null</code>. | |
2414 | + * @param modifier the paragraph style modifier or <code>null</code>. | |
2415 | + * @param begin the beginning position to change, inclusive. | |
2416 | + * @param end the endign position to change, exclusive. | |
2417 | + * @param scroll if true, scrolls the view after change done. | |
2418 | + */ | |
2419 | + protected synchronized void changeParagraphStyle(ParagraphStyle style, | |
2420 | + ParagraphStyleModifier modifier, | |
2421 | + TextPositionInfo begin, | |
2422 | + TextPositionInfo end, | |
2423 | + boolean scroll) | |
2424 | + { | |
2425 | + if (!view.isEditable() || begin == null || end == null) | |
2426 | + return; | |
2427 | + | |
2428 | + /* | |
2429 | + int selBegin = view.getSelectionBegin().textIndex; | |
2430 | + int selEnd = view.getSelectionEnd().textIndex; | |
2431 | + */ | |
2432 | + | |
2433 | + if (style != null) { | |
2434 | + lastUndo = | |
2435 | + model.setParagraphStyle(begin.textIndex, end.textIndex, style); | |
2436 | + } | |
2437 | + else if (modifier != null) { | |
2438 | + lastUndo = model.modifyParagraphStyle(begin.textIndex, | |
2439 | + end.textIndex, modifier); | |
2440 | + } | |
2441 | + else { | |
2442 | + return; | |
2443 | + } | |
2444 | + | |
2445 | + if (view.isShowing()) { | |
2446 | + if (scroll) { | |
2447 | + view.scrollTo(view.getSelectionBegin()); | |
2448 | + } | |
2449 | + } | |
2450 | + | |
2451 | + /* | |
2452 | + TextPositionInfo selBeginPos = null; | |
2453 | + TextPositionInfo selEndPos = null; | |
2454 | + if (view.getSelectionBegin().textIndex != selBegin) { | |
2455 | + selBeginPos = view.getTextPositionAt(selBegin); | |
2456 | + } | |
2457 | + if (view.getSelectionEnd().textIndex != selEnd) { | |
2458 | + if (selBegin == selEnd && selBeginPos != null) | |
2459 | + selEndPos = selBeginPos; | |
2460 | + else | |
2461 | + selEndPos = view.getTextPositionAt(selEnd); | |
2462 | + } | |
2463 | + | |
2464 | + if (!view.isShowing()) { | |
2465 | + if (selBeginPos != null || selEndPos != null) { | |
2466 | + setSelectionBeginEnd(selBeginPos, selEndPos); | |
2467 | + } | |
2468 | + } | |
2469 | + else { | |
2470 | + if (selBeginPos != null || selEndPos != null) { | |
2471 | + view.hideSelection(); | |
2472 | + setSelectionBeginEnd(selBeginPos, selEndPos); | |
2473 | + view.showSelection(); | |
2474 | + } | |
2475 | + if (scroll) { | |
2476 | + view.scrollTo(view.getSelectionBegin()); | |
2477 | + } | |
2478 | + } | |
2479 | + */ | |
2480 | + } | |
2481 | + | |
2482 | + /*if[JDK1.2]*/ | |
2483 | + | |
2484 | + /** | |
2485 | + * Adds this controller to the view. | |
2486 | + */ | |
2487 | + /*if[JDK1.2]*/ | |
2488 | + protected void addToView() { | |
2489 | + super.addToView(); | |
2490 | + getView().addInputMethodListener(this); | |
2491 | + } | |
2492 | + | |
2493 | + /** | |
2494 | + * Invoked when the text entered through an input method has changed. | |
2495 | + */ | |
2496 | + /*if[JDK1.2]*/ | |
2497 | + public void inputMethodTextChanged(InputMethodEvent e) { | |
2498 | + if (!view.isEnabled()) | |
2499 | + return; | |
2500 | + | |
2501 | + if (e.isConsumed()) | |
2502 | + return; | |
2503 | + | |
2504 | + if (!view.isEditable()) { | |
2505 | + //view.getToolkit().beep(); | |
2506 | + e.consume(); | |
2507 | + return; | |
2508 | + } | |
2509 | + | |
2510 | + replaceInputMethodText(e); | |
2511 | + setInputMethodCaretPosition(e); | |
2512 | + | |
2513 | + e.consume(); | |
2514 | + } | |
2515 | + | |
2516 | + /** | |
2517 | + * Invoked when the caret within composed text has changed. | |
2518 | + */ | |
2519 | + /*if[JDK1.2]*/ | |
2520 | + public void caretPositionChanged(InputMethodEvent e) { | |
2521 | + if (!view.isEnabled()) | |
2522 | + return; | |
2523 | + | |
2524 | + if (e.isConsumed()) | |
2525 | + return; | |
2526 | + | |
2527 | + if (!view.isEditable()) { | |
2528 | + //view.getToolkit().beep(); | |
2529 | + e.consume(); | |
2530 | + return; | |
2531 | + } | |
2532 | + | |
2533 | + setInputMethodCaretPosition(e); | |
2534 | + | |
2535 | + e.consume(); | |
2536 | + } | |
2537 | + | |
2538 | + | |
2539 | + protected TextStyle savedTypeInStyle; | |
2540 | + protected TextPositionInfo composedTextBegin; | |
2541 | + protected TextPositionInfo composedTextEnd; | |
2542 | + | |
2543 | + | |
2544 | + // | |
2545 | + // Replaces the current input method (composed) text according to | |
2546 | + // the passed input method event. This method also inserts the | |
2547 | + // committed text into the document. | |
2548 | + // | |
2549 | + protected void replaceInputMethodText(InputMethodEvent e) { | |
2550 | + int commitCount = e.getCommittedCharacterCount(); | |
2551 | + AttributedCharacterIterator text = e.getText(); | |
2552 | + | |
2553 | + if (text == null) { | |
2554 | + // old composed text deletion | |
2555 | + if (composedTextBegin != null) { | |
2556 | + replaceRange(new Text("", savedTypeInStyle), | |
2557 | + composedTextBegin, composedTextEnd, true); | |
2558 | + composedTextBegin = composedTextEnd = null; | |
2559 | + } | |
2560 | + if (savedTypeInStyle != null) { | |
2561 | + typeInStyle = savedTypeInStyle; | |
2562 | + typeInText = new Text(" ", typeInStyle); | |
2563 | + savedTypeInStyle = null; | |
2564 | + } | |
2565 | + } | |
2566 | + else { | |
2567 | + if (savedTypeInStyle == null) { | |
2568 | + savedTypeInStyle = typeInStyle; | |
2569 | + } | |
2570 | + TextBuffer buffer = new TextBuffer(); | |
2571 | + buffer.setTextStyle(savedTypeInStyle); | |
2572 | + | |
2573 | + // committed text insertion | |
2574 | + if (commitCount > 0) { | |
2575 | + for (char c = text.first(); | |
2576 | + commitCount > 0; | |
2577 | + c = text.next(), commitCount--) | |
2578 | + { | |
2579 | + buffer.append(c); | |
2580 | + } | |
2581 | + } | |
2582 | + | |
2583 | + int composedTextIndex = text.getIndex(); | |
2584 | + if (composedTextIndex >= text.getEndIndex()) { | |
2585 | + if (composedTextBegin != null) { | |
2586 | + replaceRange(buffer.toText(), | |
2587 | + composedTextBegin, composedTextEnd, true); | |
2588 | + composedTextBegin = composedTextEnd = null; | |
2589 | + } | |
2590 | + else { | |
2591 | + replaceSelection(buffer.toText()); | |
2592 | + } | |
2593 | + if (savedTypeInStyle != null) { | |
2594 | + typeInStyle = savedTypeInStyle; | |
2595 | + typeInText = new Text(" ", typeInStyle); | |
2596 | + savedTypeInStyle = null; | |
2597 | + } | |
2598 | + } | |
2599 | + else { // composedTextIndex < text.getEndIndex() | |
2600 | + // new composed text insertion | |
2601 | + ComposedTextStyle composedStyle = | |
2602 | + new ComposedTextStyle(savedTypeInStyle, null); | |
2603 | + BasicTSModifier modifier = new BasicTSModifier(); | |
2604 | + modifier.put(BasicTSModifier.COLOR, | |
2605 | + view.getSelectionForeground()); | |
2606 | + ComposedTextStyle selectedStyle = | |
2607 | + new ComposedTextStyle( | |
2608 | + modifier.modify(savedTypeInStyle), | |
2609 | + view.getSelectionBackground()); | |
2610 | + buffer.setTextStyle(composedStyle); | |
2611 | + | |
2612 | + int commitLen = buffer.length(); | |
2613 | + char c = text.setIndex(composedTextIndex); | |
2614 | + while (c != CharacterIterator.DONE) { | |
2615 | + Map attrs = text.getAttributes(); | |
2616 | + InputMethodHighlight hl = | |
2617 | + (InputMethodHighlight)attrs.get( | |
2618 | + TextAttribute.INPUT_METHOD_HIGHLIGHT); | |
2619 | + if (hl == null) { | |
2620 | + buffer.setTextStyle(composedStyle); | |
2621 | + } | |
2622 | + else { | |
2623 | + if (hl.isSelected()) { | |
2624 | + buffer.setTextStyle(selectedStyle); | |
2625 | + } | |
2626 | + else { | |
2627 | + buffer.setTextStyle(composedStyle); | |
2628 | + } | |
2629 | + switch (hl.getState()) { | |
2630 | + case InputMethodHighlight.RAW_TEXT: | |
2631 | + case InputMethodHighlight.CONVERTED_TEXT: | |
2632 | + } | |
2633 | + } | |
2634 | + int runLimit = text.getRunLimit(); | |
2635 | + while (text.getIndex() < runLimit) { | |
2636 | + buffer.append(c); | |
2637 | + c = text.next(); | |
2638 | + } | |
2639 | + c = text.setIndex(runLimit); | |
2640 | + } | |
2641 | + | |
2642 | + if (composedTextBegin != null) { | |
2643 | + replaceRange(buffer.toText(), | |
2644 | + composedTextBegin, composedTextEnd, true); | |
2645 | + composedTextEnd = | |
2646 | + view.getTextPositionAt( | |
2647 | + composedTextBegin.textIndex + buffer.length()); | |
2648 | + } | |
2649 | + else { | |
2650 | + replaceSelection(buffer.toText()); | |
2651 | + composedTextEnd = view.getSelectionEnd(); | |
2652 | + } | |
2653 | + composedTextBegin = | |
2654 | + view.getTextPositionNearby(composedTextEnd, | |
2655 | + composedTextEnd.textIndex - | |
2656 | + (buffer.length() - commitLen)); | |
2657 | + } | |
2658 | + } | |
2659 | + } | |
2660 | + | |
2661 | + // | |
2662 | + // Sets the caret position according to the passed input method | |
2663 | + // event. Also, sets/resets composed text caret appropriately. | |
2664 | + // | |
2665 | + protected void setInputMethodCaretPosition(InputMethodEvent e) { | |
2666 | + if (composedTextBegin != null) { | |
2667 | + int index = composedTextBegin.textIndex; | |
2668 | + TextHitInfo caretPos = e.getCaret(); | |
2669 | + if (caretPos != null) { | |
2670 | + index += caretPos.getInsertionIndex(); | |
2671 | + } | |
2672 | + TextPositionInfo pos = | |
2673 | + view.getTextPositionNearby(composedTextBegin, index); | |
2674 | + select(pos, pos, true, false); | |
2675 | + } | |
2676 | + } | |
2677 | + | |
2678 | + | |
2679 | + static class ComposedTextStyle extends TextStyle { | |
2680 | + static Stroke DashedStroke = | |
2681 | + new BasicStroke(1.0f, | |
2682 | + BasicStroke.CAP_SQUARE, | |
2683 | + BasicStroke.JOIN_MITER, | |
2684 | + 10.0f, | |
2685 | + new float[]{ 2.0f, 2.0f }, | |
2686 | + 0.0f); | |
2687 | + | |
2688 | + Color selectionColor; | |
2689 | + | |
2690 | + public ComposedTextStyle(TextStyle textStyle, Color selectionColor) { | |
2691 | + super(textStyle.getFont(), | |
2692 | + textStyle.getExtendedFont().getColor(), | |
2693 | + false); | |
2694 | + this.selectionColor = selectionColor; | |
2695 | + } | |
2696 | + | |
2697 | + public void drawText(Graphics g, char text[], int offset, int length, | |
2698 | + boolean isRunStart, boolean isRunEnd, | |
2699 | + int x, int y, int width, int height, | |
2700 | + int baseLine) | |
2701 | + { | |
2702 | + if (selectionColor != null) { | |
2703 | + Color color = g.getColor(); | |
2704 | + g.setColor(selectionColor); | |
2705 | + g.fillRect(x, y, width, height); | |
2706 | + g.setColor(color); | |
2707 | + } | |
2708 | + | |
2709 | + super.drawText(g, text, offset, length, isRunStart, isRunEnd, | |
2710 | + x, y, width, height, baseLine); | |
2711 | + | |
2712 | + Graphics2D g2 = (Graphics2D)g; | |
2713 | + Stroke s = g2.getStroke(); | |
2714 | + g2.setStroke(DashedStroke); | |
2715 | + g2.drawLine(x, y + height - 1, x + width, y + height - 1); | |
2716 | + g2.setStroke(s); | |
2717 | + } | |
2718 | + | |
2719 | + public int hashCode() { | |
2720 | + int hash = super.hashCode(); | |
2721 | + if (selectionColor != null) { | |
2722 | + hash ^= selectionColor.hashCode(); | |
2723 | + } | |
2724 | + return hash; | |
2725 | + } | |
2726 | + | |
2727 | + public boolean equals(Object anObject) { | |
2728 | + if (this == anObject) | |
2729 | + return true; | |
2730 | + if (anObject == null) | |
2731 | + return false; | |
2732 | + if (getClass() == anObject.getClass()) { | |
2733 | + ComposedTextStyle style = (ComposedTextStyle)anObject; | |
2734 | + return equalsFontAndAction(style) && | |
2735 | + (selectionColor == null ? | |
2736 | + style.selectionColor == null : | |
2737 | + selectionColor.equals(style.selectionColor)); | |
2738 | + } | |
2739 | + return false; | |
2740 | + } | |
2741 | + } | |
2742 | + | |
2743 | + /*end[JDK1.2]*/ | |
2744 | + | |
2745 | +} |
@@ -0,0 +1,1156 @@ | ||
1 | +/* | |
2 | + * TextEditView.java | |
3 | + * | |
4 | + * Copyright (c) 1997, 1998 Kazuki YASUMATSU. All Rights Reserved. | |
5 | + * | |
6 | + * Permission to use, copy, modify, and distribute this software and its | |
7 | + * documentation for any purpose and without fee or royalty is hereby | |
8 | + * granted, provided that both the above copyright notice and this | |
9 | + * permission notice appear in all copies of the software and | |
10 | + * documentation or portions thereof, including modifications, that you | |
11 | + * make. | |
12 | + * | |
13 | + * THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO | |
14 | + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, | |
15 | + * BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR | |
16 | + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR | |
17 | + * THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY | |
18 | + * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. | |
19 | + * COPYRIGHT HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE | |
20 | + * OR DOCUMENTATION. | |
21 | + */ | |
22 | + | |
23 | +package jp.kyasu.awt.text; | |
24 | + | |
25 | +import jp.kyasu.awt.AWTResources; | |
26 | +import jp.kyasu.awt.TextModel; | |
27 | +import jp.kyasu.awt.TextEditModel; | |
28 | +import jp.kyasu.awt.event.TextModelEvent; | |
29 | +import jp.kyasu.awt.event.TextModelListener; | |
30 | +import jp.kyasu.awt.event.TextPositionEvent; | |
31 | +import jp.kyasu.awt.event.TextPositionListener; | |
32 | +import jp.kyasu.graphics.ClickableTextAction; | |
33 | +import jp.kyasu.graphics.RichText; | |
34 | +import jp.kyasu.graphics.Text; | |
35 | +import jp.kyasu.graphics.TextLayout; | |
36 | +import jp.kyasu.graphics.TextStyle; | |
37 | +import jp.kyasu.graphics.text.TextChange; | |
38 | +import jp.kyasu.graphics.text.TextLayoutChange; | |
39 | +import jp.kyasu.graphics.text.TextPositionInfo; | |
40 | + | |
41 | +import java.awt.AWTEvent; | |
42 | +import java.awt.Color; | |
43 | +import java.awt.Cursor; | |
44 | +import java.awt.Dimension; | |
45 | +import java.awt.Font; | |
46 | +import java.awt.Graphics; | |
47 | +import java.util.Enumeration; | |
48 | +import java.util.Vector; | |
49 | + | |
50 | +/*if[JDK1.2]*/ | |
51 | +import java.awt.Point; | |
52 | +import java.awt.Rectangle; | |
53 | +import java.awt.im.InputMethodRequests; | |
54 | +import java.text.AttributedCharacterIterator; | |
55 | +import java.text.AttributedCharacterIterator.Attribute; | |
56 | +import java.text.AttributedString; | |
57 | +import java.awt.font.TextHitInfo; | |
58 | +/*end[JDK1.2]*/ | |
59 | + | |
60 | +/** | |
61 | + * The <code>TextEditView</code> class implements a view of a MVC model for | |
62 | + * the text editing. The model of the MVC model is a <code>TextEditModel</code> | |
63 | + * object and the controller of the MVC model is a | |
64 | + * <code>TextEditController</code> object. | |
65 | + * | |
66 | + * @see jp.kyasu.awt.TextEditModel | |
67 | + * @see jp.kyasu.awt.text.TextEditController | |
68 | + * | |
69 | + * @version 12 Dec 1998 | |
70 | + * @author Kazuki YASUMATSU | |
71 | + */ | |
72 | +public class TextEditView extends TextView implements TextModelListener { | |
73 | + protected TextEditModel model; | |
74 | + protected TextEditController controller; | |
75 | + protected boolean editable; | |
76 | + protected Color caretColor; | |
77 | + protected TextCaret textCaret; | |
78 | + transient protected TextPositionInfo selectionBegin; | |
79 | + transient protected TextPositionInfo selectionEnd; | |
80 | + | |
81 | + transient protected Vector textPositionListeners; | |
82 | + | |
83 | + | |
84 | + /** | |
85 | + * The default cursor. | |
86 | + */ | |
87 | + static public final Cursor DEFAULT_CURSOR = | |
88 | + Cursor.getPredefinedCursor(AWTResources.getResourceInteger( | |
89 | + "kfc.text.cursor", Cursor.TEXT_CURSOR)); | |
90 | + | |
91 | + | |
92 | + /** | |
93 | + * Constructs a text edit view with the specified text edit model. | |
94 | + * | |
95 | + * @param textEditModel the text edit model. | |
96 | + */ | |
97 | + public TextEditView(TextEditModel textEditModel) { | |
98 | + super(textEditModel.getRichText().getRichTextStyle().getLineWrap()); | |
99 | + if (textEditModel == null) | |
100 | + throw new NullPointerException(); | |
101 | + | |
102 | + model = textEditModel; | |
103 | + model.addTextModelListener(this); | |
104 | + controller = new TextEditController(this); | |
105 | + controller.addToView(); | |
106 | + | |
107 | + editable = true; | |
108 | + | |
109 | + caretColor = TextCaret.DEFAULT_CARET_COLOR; | |
110 | + setTextCaret(new TextCaret()); | |
111 | + | |
112 | + setCursor(DEFAULT_CURSOR); | |
113 | + | |
114 | + setTextLayout(createTextLayout()); | |
115 | + | |
116 | + //enableInputMethods(true); | |
117 | + } | |
118 | + | |
119 | + | |
120 | + /** | |
121 | + * Returns the <code>RichText</code> object being viewed. | |
122 | + */ | |
123 | + public RichText getRichText() { | |
124 | + return model.getRichText(); | |
125 | + } | |
126 | + | |
127 | + /** | |
128 | + * Returns the model of this view. | |
129 | + */ | |
130 | + public TextEditModel getModel() { | |
131 | + return model; | |
132 | + } | |
133 | + | |
134 | + /** | |
135 | + * Returns the controller of this view. | |
136 | + */ | |
137 | + public TextEditController getController() { | |
138 | + return controller; | |
139 | + } | |
140 | + | |
141 | + /** | |
142 | + * Sets the controller of this view. | |
143 | + */ | |
144 | + public synchronized void setController(TextEditController controller) { | |
145 | + if (controller == null) | |
146 | + throw new NullPointerException(); | |
147 | + if (controller.view != this) | |
148 | + throw new IllegalArgumentException("view of controller is not valid"); | |
149 | + this.controller.removeFromView(); | |
150 | + this.controller = controller; | |
151 | + this.controller.addToView(); | |
152 | + } | |
153 | + | |
154 | + /** | |
155 | + * Invoked when the text model has been changed. | |
156 | + * @see jp.kyasu.awt.event.TextModelListener | |
157 | + */ | |
158 | + public void textModelChanged(TextModelEvent event) { | |
159 | + switch (event.getID()) { | |
160 | + case TextModelEvent.TEXT_MODEL_UPDATED: | |
161 | + setTextLayout(createTextLayout()); | |
162 | + controller.lastUndo = null; | |
163 | + controller.setCurrentTypeIn(getSelectionBegin()); | |
164 | + break; | |
165 | + case TextModelEvent.TEXT_MODEL_EDITED: | |
166 | + updateAfterEdited(event); | |
167 | + break; | |
168 | + } | |
169 | + } | |
170 | + | |
171 | + /** | |
172 | + * Sets the <code>TextLayout</code> object of this view. | |
173 | + */ | |
174 | + protected synchronized void setTextLayout(TextLayout layout) { | |
175 | + if (layout == null) | |
176 | + throw new NullPointerException(); | |
177 | + if (isShowing() && needsToRedrawSelection()) { | |
178 | + hideSelection(); | |
179 | + } | |
180 | + | |
181 | + selectionBegin = selectionEnd = null; | |
182 | + | |
183 | + super.setTextLayout(layout); | |
184 | + | |
185 | + if (isValid()) { | |
186 | + selectionBegin = selectionEnd = layout.getTextPositionAt(0); | |
187 | + } | |
188 | + } | |
189 | + | |
190 | + /** | |
191 | + * Sets the <code>Text</code> object of this view. | |
192 | + * @param text the <code>Text</code> object. | |
193 | + */ | |
194 | + public void setText(Text text) { | |
195 | + if (text == null) | |
196 | + throw new NullPointerException(); | |
197 | + setRichText(new RichText(text, model.getRichText().getRichTextStyle())); | |
198 | + } | |
199 | + | |
200 | + /** | |
201 | + * Sets the <code>RichText</code> object of this view. | |
202 | + * @param richText the <code>RichText</code> object. | |
203 | + */ | |
204 | + public void setRichText(RichText richText) { | |
205 | + if (richText == null) | |
206 | + throw new NullPointerException(); | |
207 | + model.setRichText(richText); | |
208 | + } | |
209 | + | |
210 | + /** | |
211 | + * Returns the beginning text position of the selection, inclusive. | |
212 | + */ | |
213 | + public TextPositionInfo getSelectionBegin() { | |
214 | + return selectionBegin; | |
215 | + } | |
216 | + | |
217 | + /** | |
218 | + * Sets the beginning text position of the selection, inclusive. | |
219 | + */ | |
220 | + protected void setSelectionBegin(TextPositionInfo posInfo) { | |
221 | + selectionBegin = posInfo; | |
222 | + } | |
223 | + | |
224 | + /** | |
225 | + * Returns the ending text position of the selection, exclusive. | |
226 | + */ | |
227 | + public TextPositionInfo getSelectionEnd() { | |
228 | + return selectionEnd; | |
229 | + } | |
230 | + | |
231 | + /** | |
232 | + * Sets the ending text position of the selection, exclusive. | |
233 | + */ | |
234 | + protected void setSelectionEnd(TextPositionInfo posInfo) { | |
235 | + selectionEnd = posInfo; | |
236 | + } | |
237 | + | |
238 | + /** | |
239 | + * Sets the text position of the selection. | |
240 | + */ | |
241 | + protected void setSelectionBeginEnd(TextPositionInfo posInfo) { | |
242 | + selectionBegin = selectionEnd = posInfo; | |
243 | + } | |
244 | + | |
245 | + /** | |
246 | + * Sets the range of the selection. | |
247 | + */ | |
248 | + protected void setSelectionBeginEnd(TextPositionInfo begin, | |
249 | + TextPositionInfo end) | |
250 | + { | |
251 | + selectionBegin = begin; | |
252 | + selectionEnd = end; | |
253 | + } | |
254 | + | |
255 | + /** | |
256 | + * Tests if this view is editable. | |
257 | + */ | |
258 | + public boolean isEditable() { | |
259 | + return editable; | |
260 | + } | |
261 | + | |
262 | + /** | |
263 | + * Makes this view editable. | |
264 | + */ | |
265 | + public void setEditable(boolean b) { | |
266 | + editable = b; | |
267 | + } | |
268 | + | |
269 | + /** | |
270 | + * Enables or disables this view. | |
271 | + */ | |
272 | + public synchronized void setEnabled(boolean b) { | |
273 | + if (isEnabled() == b) | |
274 | + return; | |
275 | + super.setEnabled(b); | |
276 | + if (isEnabled()) { | |
277 | + startTextCaret(); | |
278 | + } | |
279 | + else { | |
280 | + stopTextCaret(); | |
281 | + } | |
282 | + } | |
283 | + | |
284 | + /** | |
285 | + * Returns the caret color. | |
286 | + * @see #setCaretColor(java.awt.Color) | |
287 | + */ | |
288 | + public Color getCaretColor() { | |
289 | + return caretColor; | |
290 | + } | |
291 | + | |
292 | + /** | |
293 | + * Sets the caret color. | |
294 | + * @see #getCaretColor() | |
295 | + */ | |
296 | + public synchronized void setCaretColor(Color c) { | |
297 | + if (c == null) | |
298 | + return; | |
299 | + | |
300 | + if (caretColor.equals(c)) { | |
301 | + return; | |
302 | + } | |
303 | + | |
304 | + boolean showing = | |
305 | + isShowing() && selectionShowing && selectionIsCaret(); | |
306 | + if (showing) hideSelection(); | |
307 | + caretColor = c; | |
308 | + if (showing) showSelection(); | |
309 | + } | |
310 | + | |
311 | + /** | |
312 | + * Returns the text caret of this view. | |
313 | + * @see #setTextCaret(jp.kyasu.awt.text.TextCaret) | |
314 | + */ | |
315 | + public TextCaret getTextCaret() { | |
316 | + return textCaret; | |
317 | + } | |
318 | + | |
319 | + /** | |
320 | + * Sets the text caret of this view. | |
321 | + * @see #getTextCaret() | |
322 | + */ | |
323 | + public synchronized void setTextCaret(TextCaret textCaret) { | |
324 | + if (textCaret == null) | |
325 | + throw new NullPointerException(); | |
326 | + boolean showing = | |
327 | + isShowing() && selectionShowing && selectionIsCaret(); | |
328 | + boolean blinking = | |
329 | + this.textCaret != null && this.textCaret.isBlinking(); | |
330 | + if (showing) hideSelection(); | |
331 | + if (blinking) stopTextCaret(); | |
332 | + | |
333 | + if (this.textCaret != null) this.textCaret.setTarget(null); | |
334 | + this.textCaret = textCaret; | |
335 | + this.textCaret.setTarget(this); | |
336 | + | |
337 | + if (blinking) startTextCaret(); | |
338 | + if (showing) showSelection(); | |
339 | + } | |
340 | + | |
341 | + /** | |
342 | + * Starts the text caret of this view. | |
343 | + * @see #stopTextCaret() | |
344 | + */ | |
345 | + protected synchronized void startTextCaret() { | |
346 | + if (textCaret != null) { | |
347 | + boolean showing = | |
348 | + isShowing() && selectionShowing && selectionIsCaret(); | |
349 | + if (showing) hideSelection(); | |
350 | + textCaret.start(); | |
351 | + if (showing) showSelection(); | |
352 | + } | |
353 | + } | |
354 | + | |
355 | + /** | |
356 | + * Stops the text caret of this view. | |
357 | + * @see #startTextCaret() | |
358 | + */ | |
359 | + protected synchronized void stopTextCaret() { | |
360 | + if (textCaret != null) { | |
361 | + boolean showing = | |
362 | + isShowing() && selectionShowing && selectionIsCaret(); | |
363 | + if (showing) hideSelection(); | |
364 | + textCaret.stop(); | |
365 | + if (showing) showSelection(); | |
366 | + } | |
367 | + } | |
368 | + | |
369 | + /** | |
370 | + * Returns the selected text. | |
371 | + */ | |
372 | + public Text getSelectedText() { | |
373 | + if (selectionBegin == null || selectionEnd == null || | |
374 | + selectionBegin.textIndex == selectionEnd.textIndex) | |
375 | + { | |
376 | + return new Text(); | |
377 | + } | |
378 | + return model.getRichText().getText().subtext(selectionBegin.textIndex, | |
379 | + selectionEnd.textIndex); | |
380 | + } | |
381 | + | |
382 | + /** | |
383 | + * Resets the location of the layout text. | |
384 | + * This method is called from setBounds(). | |
385 | + */ | |
386 | + protected void resetLocationOfText() { | |
387 | + if (selectionBegin == null || selectionEnd == null) { | |
388 | + selectionBegin = selectionEnd = layout.getTextPositionAt(0); | |
389 | + offset.x = offset.y = 0; | |
390 | + } | |
391 | + else { | |
392 | + selectionBegin = layout.getTextPositionAt(selectionBegin.textIndex); | |
393 | + selectionEnd = layout.getTextPositionAt(selectionEnd.textIndex); | |
394 | + offset.x = getScrollXTo(selectionBegin); | |
395 | + offset.y = getScrollYTo(selectionBegin); | |
396 | + } | |
397 | + } | |
398 | + | |
399 | + /** | |
400 | + * Notifies this view that it has been added to a container. | |
401 | + */ | |
402 | + public void addNotify() { | |
403 | + /* | |
404 | + if (textCaret != null) { | |
405 | + textCaret.start(); | |
406 | + } | |
407 | + */ | |
408 | + super.addNotify(); | |
409 | + } | |
410 | + | |
411 | + /** | |
412 | + * Notifies this view that it has been removed from its container. | |
413 | + */ | |
414 | + public void removeNotify() { | |
415 | + if (textCaret != null) { | |
416 | + textCaret.stop(); | |
417 | + } | |
418 | + super.removeNotify(); | |
419 | + } | |
420 | + | |
421 | + /** | |
422 | + * Sets the font of this view. | |
423 | + */ | |
424 | + public void setFont(Font f) { | |
425 | + super.setFont(f); | |
426 | + RichText richText = model.getRichText(); | |
427 | + richText.setBaseTextStyle(new TextStyle(f)); | |
428 | + model.setRichText(richText); | |
429 | + } | |
430 | + | |
431 | + /** | |
432 | + * Add a listener to recieve text position events when the selection | |
433 | + * of the text view changes. | |
434 | + * @param l the listener to recieve events. | |
435 | + */ | |
436 | + public void addTextPositionListener(TextPositionListener l) { | |
437 | + if (l == null) | |
438 | + return; | |
439 | + if (textPositionListeners == null) | |
440 | + textPositionListeners = new Vector(); | |
441 | + textPositionListeners.addElement(l); | |
442 | + } | |
443 | + | |
444 | + /** | |
445 | + * Removes an text position listener. | |
446 | + * @param l the listener being removed. | |
447 | + */ | |
448 | + public void removeTextPositionListener(TextPositionListener l) { | |
449 | + if (textPositionListeners == null) | |
450 | + return; | |
451 | + textPositionListeners.removeElement(l); | |
452 | + if (textPositionListeners.size() == 0) | |
453 | + textPositionListeners = null; | |
454 | + } | |
455 | + | |
456 | + /** | |
457 | + * Notifies the text position event to the text position listeners. | |
458 | + */ | |
459 | + protected void notifyTextPositionListeners() { | |
460 | + if (textPositionListeners == null) | |
461 | + return; | |
462 | + if (selectionBegin == null || selectionEnd == null) | |
463 | + return; | |
464 | + TextPositionEvent event = | |
465 | + new TextPositionEvent(this, | |
466 | + TextPositionEvent.TEXT_POSITION_CHANGED, | |
467 | + selectionBegin, selectionEnd); | |
468 | + for (Enumeration e = textPositionListeners.elements(); | |
469 | + e.hasMoreElements(); | |
470 | + ) | |
471 | + { | |
472 | + ((TextPositionListener)e.nextElement()).textPositionChanged(event); | |
473 | + } | |
474 | + } | |
475 | + | |
476 | + /** | |
477 | + * Paints this component. | |
478 | + */ | |
479 | + public void paint(Graphics g) { | |
480 | + if (!isShowing()) | |
481 | + return; | |
482 | + | |
483 | + super.paint(g); | |
484 | + | |
485 | + if (needsToRedrawSelection()) | |
486 | + showSelection(); | |
487 | + } | |
488 | + | |
489 | + /** | |
490 | + * Paints this view with the specified range. | |
491 | + */ | |
492 | + protected void paint(Graphics g, | |
493 | + TextPositionInfo begin, TextPositionInfo end) | |
494 | + { | |
495 | + if (!selectionVisible || selectionBegin == null || selectionEnd == null) | |
496 | + { | |
497 | + selectionShowing = false; | |
498 | + g.setColor(getForeground()); | |
499 | + layout.draw(g, offset, begin, end); | |
500 | + return; | |
501 | + } | |
502 | + | |
503 | + if (selectionBegin.textIndex == selectionEnd.textIndex) { | |
504 | + hideSelection(); // hide caret | |
505 | + g.setColor(getForeground()); | |
506 | + layout.draw(g, offset, begin, end); | |
507 | + if (!doubleBuffered) { | |
508 | + showSelection(); // show caret | |
509 | + } | |
510 | + return; | |
511 | + } | |
512 | + | |
513 | + selectionShowing = true; | |
514 | + | |
515 | + if (selectionBegin.textIndex > end.textIndex || | |
516 | + selectionEnd.textIndex < begin.textIndex) | |
517 | + { | |
518 | + g.setColor(getForeground()); | |
519 | + layout.draw(g, offset, begin, end); | |
520 | + return; | |
521 | + } | |
522 | + | |
523 | + // begin <= selectionEnd && selectionBegin <= end | |
524 | + TextPositionInfo newBegin; | |
525 | + if (selectionBegin.textIndex <= begin.textIndex) | |
526 | + newBegin = begin; | |
527 | + else { // begin.textIndex < selectionBegin.textIndex | |
528 | + g.setColor(getForeground()); | |
529 | + layout.draw(g, offset, begin, selectionBegin); | |
530 | + newBegin = selectionBegin; | |
531 | + } | |
532 | + if (end.textIndex <= selectionEnd.textIndex) { | |
533 | + g.setColor(selectionForeground); | |
534 | + layout.draw(g, offset, newBegin, end, | |
535 | + selectionBackground, | |
536 | + selectionBegin.lineIndex < newBegin.lineIndex, | |
537 | + (end.textIndex == end.lineBegin ? | |
538 | + false : end.lineIndex < selectionEnd.lineIndex)); | |
539 | + g.setColor(getForeground()); | |
540 | + } | |
541 | + else { | |
542 | + g.setColor(selectionForeground); | |
543 | + layout.draw(g, offset, newBegin, selectionEnd, | |
544 | + selectionBackground, | |
545 | + selectionBegin.lineIndex < newBegin.lineIndex, | |
546 | + false); | |
547 | + g.setColor(getForeground()); | |
548 | + layout.draw(g, offset, selectionEnd, end); | |
549 | + } | |
550 | + } | |
551 | + | |
552 | + /** | |
553 | + * Shows the selection. | |
554 | + */ | |
555 | + protected synchronized void showSelection() { | |
556 | + if (!isShowing()) | |
557 | + return; | |
558 | + if (!selectionVisible || selectionShowing) | |
559 | + return; | |
560 | + if (selectionBegin == null || selectionEnd == null) | |
561 | + return; | |
562 | + /* | |
563 | + if (!selectionVisible) { | |
564 | + return; | |
565 | + if (selectionIsCaret()) | |
566 | + return; | |
567 | + selectionVisible = true; | |
568 | + } | |
569 | + */ | |
570 | + selectionShowing = !selectionShowing; | |
571 | + if (selectionBegin.textIndex == selectionEnd.textIndex) { | |
572 | + if (textCaret != null) { | |
573 | + Graphics g = getGraphics(); | |
574 | + if (g != null) { | |
575 | + textCaret.showCaret(g, | |
576 | + offset, | |
577 | + selectionBegin, | |
578 | + caretColor); | |
579 | + g.dispose(); | |
580 | + } | |
581 | + } | |
582 | + } | |
583 | + else | |
584 | + paintSelection(selectionForeground, selectionBackground); | |
585 | + } | |
586 | + | |
587 | + /** | |
588 | + * Hides the selection. | |
589 | + */ | |
590 | + protected synchronized void hideSelection() { | |
591 | + if (!isShowing()) | |
592 | + return; | |
593 | + if (!selectionVisible || !selectionShowing) | |
594 | + return; | |
595 | + if (selectionBegin == null || selectionEnd == null) | |
596 | + return; | |
597 | + selectionShowing = !selectionShowing; | |
598 | + if (selectionBegin.textIndex == selectionEnd.textIndex) { | |
599 | + if (textCaret != null) { | |
600 | + Graphics g = getGraphics(); | |
601 | + if (g != null) { | |
602 | + textCaret.hideCaret(g, | |
603 | + offset, | |
604 | + selectionBegin, | |
605 | + caretColor); | |
606 | + g.dispose(); | |
607 | + } | |
608 | + } | |
609 | + } | |
610 | + else | |
611 | + paintSelection(getForeground(), getBackground()); | |
612 | + } | |
613 | + | |
614 | + /** | |
615 | + * Paints the selection with the specified colors. | |
616 | + */ | |
617 | + protected void paintSelection(Color foreColor, Color backColor) { | |
618 | + paintSelection(selectionBegin, selectionEnd, | |
619 | + foreColor, backColor, true); | |
620 | + } | |
621 | + | |
622 | + /** | |
623 | + * Paints the selection of the specified range with the specified colors. | |
624 | + */ | |
625 | + protected void paintSelection(TextPositionInfo selBegin, | |
626 | + TextPositionInfo selEnd, | |
627 | + Color foreColor, Color backColor) | |
628 | + { | |
629 | + paintSelection(selBegin, selEnd, foreColor, backColor, false); | |
630 | + } | |
631 | + | |
632 | + /** | |
633 | + * Paints the selection of the specified range with the specified colors | |
634 | + * and flag indicating that the specified range is a entire selection. | |
635 | + */ | |
636 | + protected void paintSelection(TextPositionInfo selBegin, | |
637 | + TextPositionInfo selEnd, | |
638 | + Color foreColor, Color backColor, | |
639 | + boolean fullSelection) | |
640 | + { | |
641 | + if (selBegin.textIndex == selEnd.textIndex) | |
642 | + return; | |
643 | + TextPositionInfo begin, end; | |
644 | + begin = getVisibleBegin(); | |
645 | + end = getVisibleEnd(); | |
646 | + if (selBegin.textIndex > end.textIndex || | |
647 | + selEnd.textIndex < begin.textIndex) | |
648 | + { | |
649 | + return; | |
650 | + } | |
651 | + if (begin.textIndex < selBegin.textIndex) | |
652 | + begin = selBegin; | |
653 | + if (selEnd.textIndex < end.textIndex) | |
654 | + end = selEnd; | |
655 | + | |
656 | + Graphics g = getPreferredGraphics(); | |
657 | + if (g == null) | |
658 | + return; | |
659 | + | |
660 | + g.setColor(foreColor); | |
661 | + if (fullSelection) | |
662 | + layout.draw(g, offset, begin, end, backColor, | |
663 | + selectionBegin.lineIndex < begin.lineIndex, | |
664 | + end.lineIndex < selectionEnd.lineIndex); | |
665 | + else | |
666 | + layout.draw(g, offset, begin, end, backColor); | |
667 | + | |
668 | + g.dispose(); | |
669 | + syncGraphics(0, | |
670 | + begin.y + offset.y, | |
671 | + getSize().width, | |
672 | + end.y + end.lineSkip - begin.y); | |
673 | + } | |
674 | + | |
675 | + /** | |
676 | + * Updates this view after the text edit model has been edited. | |
677 | + */ | |
678 | + protected synchronized void updateAfterEdited(TextModelEvent event) { | |
679 | + TextChange change = event.getTextChange(); | |
680 | + | |
681 | + if (isShowing()) { | |
682 | + if (needsToRedrawSelection()) { | |
683 | + hideSelection(); | |
684 | + } | |
685 | + else { | |
686 | + selectionShowing = false; | |
687 | + } | |
688 | + } | |
689 | + | |
690 | + Dimension oldSize = layout.getSize(); | |
691 | + TextLayoutChange layoutChange = layout.updateLayout(change, | |
692 | + selectionBegin, | |
693 | + selectionEnd); | |
694 | + | |
695 | + if (layoutChange.isNoRepaint()) { | |
696 | + // do nothing | |
697 | + } | |
698 | + else if (selectionBegin == null || selectionEnd == null) { | |
699 | + setSelectionBeginEnd(getTextPositionAt(0)); | |
700 | + notifyTextPositionListeners(); | |
701 | + } | |
702 | + else { | |
703 | + int begin = selectionBegin.textIndex; | |
704 | + int end = selectionEnd.textIndex; | |
705 | + if (change.textReplaced) { | |
706 | + if (change.begin <= begin) { | |
707 | + if (begin < change.end) { | |
708 | + begin = change.end + change.lengthChanged; | |
709 | + } | |
710 | + else { | |
711 | + begin += change.lengthChanged; | |
712 | + } | |
713 | + } | |
714 | + if (change.begin <= end) { | |
715 | + if (end < change.end) { | |
716 | + end = change.end + change.lengthChanged; | |
717 | + } | |
718 | + else { | |
719 | + end += change.lengthChanged; | |
720 | + } | |
721 | + } | |
722 | + } | |
723 | + if (layoutChange.isFullRepaint()) { | |
724 | + if (begin == end) { | |
725 | + setSelectionBeginEnd(getTextPositionAt(begin)); | |
726 | + } | |
727 | + else { | |
728 | + setSelectionBegin(getTextPositionAt(begin)); | |
729 | + setSelectionEnd(getTextPositionAt(end)); | |
730 | + } | |
731 | + } | |
732 | + else if (begin == end) { | |
733 | + if (begin < layoutChange.paintBegin.textIndex) { | |
734 | + if (begin == selectionBegin.textIndex) { | |
735 | + setSelectionBeginEnd(selectionBegin); | |
736 | + } | |
737 | + else if (begin == selectionEnd.textIndex) { | |
738 | + setSelectionBeginEnd(selectionEnd); | |
739 | + } | |
740 | + else { | |
741 | + setSelectionBeginEnd(getTextPositionNearby( | |
742 | + layoutChange.paintBegin, begin)); | |
743 | + } | |
744 | + } | |
745 | + else { | |
746 | + setSelectionBeginEnd( | |
747 | + getTextPositionNearby(layoutChange.paintBegin, begin)); | |
748 | + } | |
749 | + } | |
750 | + else { | |
751 | + if (begin < layoutChange.paintBegin.textIndex && | |
752 | + begin == selectionBegin.textIndex) | |
753 | + { | |
754 | + setSelectionBegin(selectionBegin); | |
755 | + } | |
756 | + else { | |
757 | + setSelectionBegin( | |
758 | + getTextPositionNearby(layoutChange.paintBegin, begin)); | |
759 | + } | |
760 | + if (end < layoutChange.paintBegin.textIndex && | |
761 | + end == selectionEnd.textIndex) | |
762 | + { | |
763 | + setSelectionEnd(selectionEnd); | |
764 | + } | |
765 | + else { | |
766 | + setSelectionEnd( | |
767 | + getTextPositionNearby(layoutChange.paintBegin, end)); | |
768 | + } | |
769 | + } | |
770 | + notifyTextPositionListeners(); | |
771 | + } | |
772 | + | |
773 | + if (doubleBuffered || isShowing()) { | |
774 | + if (layoutChange.isPartialRepaint()) { | |
775 | + int paintBegin = event.getPaintBegin(); | |
776 | + int paintEnd = event.getPaintEnd(); | |
777 | + if (paintBegin >= 0 && | |
778 | + paintBegin < layoutChange.paintBegin.textIndex) | |
779 | + { | |
780 | + layoutChange.paintBegin = | |
781 | + layout.getTextPositionNearby(layoutChange.paintBegin, | |
782 | + paintBegin); | |
783 | + } | |
784 | + if (paintEnd >= 0 && paintEnd <= model.getRichText().length() && | |
785 | + paintEnd > layoutChange.paintEnd.textIndex) | |
786 | + { | |
787 | + layoutChange.paintEnd = | |
788 | + layout.getTextPositionNearby(layoutChange.paintEnd, | |
789 | + paintEnd); | |
790 | + } | |
791 | + } | |
792 | + paintAfterEdited(layoutChange); | |
793 | + if (isShowing()) { | |
794 | + showSelection(); // show caret | |
795 | + } | |
796 | + } | |
797 | + if (layoutChange.isFullRepaint()) | |
798 | + layoutResized(-1, -1); | |
799 | + else | |
800 | + layoutResized(oldSize.width, oldSize.height); | |
801 | + } | |
802 | + | |
803 | + /** | |
804 | + * Paints this view after the text edit model has been edited. | |
805 | + */ | |
806 | + protected synchronized void paintAfterEdited(TextLayoutChange change) { | |
807 | + if (change.isNoRepaint()) | |
808 | + return; | |
809 | + | |
810 | + Graphics g = getPreferredGraphics(); | |
811 | + if (g == null) | |
812 | + return; | |
813 | + | |
814 | + if (change.isFullRepaint()) { | |
815 | + _visibleBegin = _visibleEnd = null; | |
816 | + Dimension d = getSize(); | |
817 | + Dimension ld = layout.getSize(); | |
818 | + if (ld.width > d.width && ld.width + offset.x < d.width) | |
819 | + offset.x = d.width - ld.width; | |
820 | + if (ld.height > d.height && ld.height + offset.y < d.height) | |
821 | + offset.y = d.height - ld.height; | |
822 | + paintOn(g); | |
823 | + g.dispose(); | |
824 | + syncGraphics(); | |
825 | + return; | |
826 | + } | |
827 | + | |
828 | + TextPositionInfo paintBegin = change.paintBegin; | |
829 | + TextPositionInfo paintEnd = change.paintEnd; | |
830 | + TextPositionInfo vBegin = null; | |
831 | + TextPositionInfo vEnd = null; | |
832 | + int newCleanTop = paintEnd.y + offset.y; | |
833 | + int oldCleanTop = newCleanTop - change.heightChanged; | |
834 | + int drawWidth = (change.widthChanged < 0 ? | |
835 | + layout.getSize().width - change.widthChanged : | |
836 | + layout.getSize().width); | |
837 | + Dimension d = getSize(); | |
838 | + if (change.heightChanged < 0) { // view up | |
839 | + TextPositionInfo pBegin = paintEnd; | |
840 | + if (oldCleanTop < d.height && newCleanTop >=0) { | |
841 | + // copyArea() may be fail. | |
842 | + scrolledUp = true; | |
843 | + g.copyArea(0, oldCleanTop, d.width, d.height - oldCleanTop, | |
844 | + 0, change.heightChanged); | |
845 | + pBegin = layout.getLineBeginPositionOver( | |
846 | + paintEnd, | |
847 | + -offset.y+newCleanTop+d.height-oldCleanTop-1); | |
848 | + } | |
849 | + if (newCleanTop < d.height) { | |
850 | + vEnd = layout.getLineBeginPositionUnder( | |
851 | + paintEnd, | |
852 | + -offset.y + d.height - 1); | |
853 | + layout.draw(g, offset, pBegin, vEnd, | |
854 | + getBackground(), | |
855 | + true, | |
856 | + (vEnd.textIndex >= getRichText().length()), | |
857 | + true, | |
858 | + drawWidth); | |
859 | + int endY = offset.y + vEnd.y + vEnd.lineSkip; | |
860 | + if (endY < d.height) { | |
861 | + g.setColor(getBackground()); | |
862 | + g.fillRect(0, endY, d.width, d.height - endY); | |
863 | + g.setColor(getForeground()); | |
864 | + } | |
865 | + } | |
866 | + } | |
867 | + else if (change.heightChanged > 0) { // view down | |
868 | + if (oldCleanTop >= 0 && newCleanTop < d.height) { | |
869 | + if (!AWTResources.HAS_COPY_AREA_BUG && | |
870 | + paintEnd.textIndex < getRichText().length()) | |
871 | + { | |
872 | + // copyArea() may be fail. | |
873 | + scrolledDown = true; | |
874 | + g.copyArea(0, oldCleanTop, d.width, d.height - newCleanTop, | |
875 | + 0, change.heightChanged); | |
876 | + } | |
877 | + else { | |
878 | + vBegin = layout.getLineBeginPositionOver(paintEnd.y); | |
879 | + vEnd = layout.getLineBeginPositionUnder( | |
880 | + vBegin, | |
881 | + -offset.y + d.height - 1); | |
882 | + layout.draw(g, offset, | |
883 | + (vBegin.textIndex > paintEnd.textIndex ? | |
884 | + vBegin : paintEnd), | |
885 | + vEnd, | |
886 | + getBackground(), | |
887 | + true, | |
888 | + (vEnd.textIndex >= getRichText().length()), | |
889 | + true, | |
890 | + drawWidth); | |
891 | + vBegin = vEnd = null; | |
892 | + } | |
893 | + } | |
894 | + else if (newCleanTop < d.height) { | |
895 | + vBegin = layout.getLineBeginPositionOver(-offset.y); | |
896 | + vEnd = layout.getLineBeginPositionUnder( | |
897 | + vBegin, | |
898 | + -offset.y + d.height - 1); | |
899 | + layout.draw(g, offset, | |
900 | + (vBegin.textIndex > paintEnd.textIndex ? | |
901 | + vBegin : paintEnd), | |
902 | + vEnd, | |
903 | + getBackground(), | |
904 | + true, | |
905 | + (vEnd.textIndex >= getRichText().length()), | |
906 | + true, | |
907 | + drawWidth); | |
908 | + int endY = offset.y + vEnd.y + vEnd.lineSkip; | |
909 | + if (endY < d.height) { | |
910 | + g.setColor(getBackground()); | |
911 | + g.fillRect(0, endY, d.width, d.height - endY); | |
912 | + g.setColor(getForeground()); | |
913 | + } | |
914 | + } | |
915 | + } | |
916 | + else { // change.heightChanged == 0 | |
917 | + // do nothing | |
918 | + } | |
919 | + | |
920 | + _visibleBegin = vBegin; | |
921 | + _visibleEnd = vEnd; | |
922 | + vBegin = getVisibleBegin(); | |
923 | + vEnd = getVisibleEnd(); | |
924 | + | |
925 | + if (paintEnd.textIndex < vBegin.textIndex || | |
926 | + vEnd.textIndex < paintBegin.textIndex) | |
927 | + { | |
928 | + g.dispose(); | |
929 | + if (change.heightChanged != 0) { | |
930 | + syncGraphics(); | |
931 | + } | |
932 | + return; | |
933 | + } | |
934 | + | |
935 | + TextPositionInfo drawBegin = | |
936 | + (vBegin.textIndex > paintBegin.textIndex ? vBegin : paintBegin); | |
937 | + TextPositionInfo drawEnd = | |
938 | + (vEnd.textIndex < paintEnd.textIndex ? vEnd : paintEnd); | |
939 | + | |
940 | + layout.draw(g, offset, drawBegin, drawEnd, | |
941 | + getBackground(), | |
942 | + (vBegin.textIndex > paintBegin.textIndex ? | |
943 | + true : change.paintFromLineBegin), | |
944 | + (vEnd.textIndex < paintEnd.textIndex ? | |
945 | + true : change.paintToLineEnd), | |
946 | + true, | |
947 | + drawWidth); | |
948 | + | |
949 | + g.dispose(); | |
950 | + if (change.heightChanged == 0) { | |
951 | + syncGraphics(0, | |
952 | + drawBegin.y + offset.y, | |
953 | + d.width, | |
954 | + drawEnd.y + drawEnd.lineSkip - drawBegin.y); | |
955 | + } | |
956 | + else { | |
957 | + syncGraphics(); | |
958 | + } | |
959 | + } | |
960 | + | |
961 | + /** | |
962 | + * Tests if the selection is caret, i.e., null selection. | |
963 | + */ | |
964 | + public boolean selectionIsCaret() { | |
965 | + return (selectionBegin != null && selectionEnd != null && | |
966 | + selectionBegin.textIndex == selectionEnd.textIndex); | |
967 | + } | |
968 | + | |
969 | + /** | |
970 | + * Tests if the selection is needed to be redrawn. | |
971 | + */ | |
972 | + protected boolean needsToRedrawSelection() { | |
973 | + //return selectionVisible && selectionIsCaret(); | |
974 | + return selectionIsCaret(); | |
975 | + } | |
976 | + | |
977 | + protected void performClickableTextAction(ClickableTextAction action) { | |
978 | + if (!action.hasActionListener()) | |
979 | + return; | |
980 | + | |
981 | + if (isDirectNotification()) { | |
982 | + action.performClickableAction(); | |
983 | + } | |
984 | + else { | |
985 | + enableEvents(0); // mark newEventsOnly | |
986 | + ClickableTextActionEvent ce = | |
987 | + new ClickableTextActionEvent(this, action); | |
988 | + jp.kyasu.awt.EventPoster.postEvent(ce); | |
989 | + } | |
990 | + } | |
991 | + | |
992 | + protected void processEvent(AWTEvent e) { | |
993 | + if (e instanceof ClickableTextActionEvent) { | |
994 | + ClickableTextActionEvent ce = (ClickableTextActionEvent)e; | |
995 | + ClickableTextAction action = ce.getClickableTextAction(); | |
996 | + action.performClickableAction(); | |
997 | + } | |
998 | + super.processEvent(e); | |
999 | + } | |
1000 | + | |
1001 | + | |
1002 | + /** | |
1003 | + * Called by the garbage collector on an object when garbage collection | |
1004 | + * determines that there are no more references to the object. | |
1005 | + * @exception java.lang.Throwable if an error was occurred. | |
1006 | + */ | |
1007 | + protected void finalize() throws Throwable { | |
1008 | + if (textCaret != null) { | |
1009 | + textCaret.stop(); | |
1010 | + } | |
1011 | + } | |
1012 | + | |
1013 | + private void writeObject(java.io.ObjectOutputStream s) | |
1014 | + throws java.io.IOException | |
1015 | + { | |
1016 | + s.defaultWriteObject(); | |
1017 | + | |
1018 | + if (selectionBegin == null) { | |
1019 | + s.writeInt(-1); | |
1020 | + } | |
1021 | + else { | |
1022 | + s.writeInt(selectionBegin.textIndex); | |
1023 | + } | |
1024 | + if (selectionEnd == null) { | |
1025 | + s.writeInt(-1); | |
1026 | + } | |
1027 | + else { | |
1028 | + s.writeInt(selectionEnd.textIndex); | |
1029 | + } | |
1030 | + | |
1031 | + if (textPositionListeners != null) { | |
1032 | + for (Enumeration e = textPositionListeners.elements(); | |
1033 | + e.hasMoreElements(); | |
1034 | + ) | |
1035 | + { | |
1036 | + TextPositionListener l = (TextPositionListener)e.nextElement(); | |
1037 | + if (l instanceof java.io.Serializable) { | |
1038 | + s.writeObject(l); | |
1039 | + } | |
1040 | + } | |
1041 | + } | |
1042 | + s.writeObject(null); | |
1043 | + } | |
1044 | + | |
1045 | + private void readObject(java.io.ObjectInputStream s) | |
1046 | + throws java.io.IOException, ClassNotFoundException | |
1047 | + { | |
1048 | + s.defaultReadObject(); | |
1049 | + | |
1050 | + int begin = s.readInt(); | |
1051 | + if (begin < 0) { | |
1052 | + selectionBegin = null; | |
1053 | + } | |
1054 | + else { | |
1055 | + selectionBegin = getTextPositionAt(begin); | |
1056 | + } | |
1057 | + int end = s.readInt(); | |
1058 | + if (end < 0) { | |
1059 | + selectionEnd = null; | |
1060 | + } | |
1061 | + else { | |
1062 | + selectionEnd = getTextPositionAt(end); | |
1063 | + } | |
1064 | + stopTextCaret(); | |
1065 | + | |
1066 | + Object listenerOrNull; | |
1067 | + while ((listenerOrNull = s.readObject()) != null) { | |
1068 | + addTextPositionListener((TextPositionListener)listenerOrNull); | |
1069 | + } | |
1070 | + } | |
1071 | + | |
1072 | + | |
1073 | + /*if[JDK1.2]*/ | |
1074 | + protected InputMethodRequests inputMethodRequestsHandler; | |
1075 | + | |
1076 | + // | |
1077 | + // Overrides this method to become an active input method client. | |
1078 | + // | |
1079 | + public InputMethodRequests getInputMethodRequests() { | |
1080 | + if (inputMethodRequestsHandler == null) { | |
1081 | + inputMethodRequestsHandler = | |
1082 | + (InputMethodRequests)new InputMethodRequestsHandler(); | |
1083 | + } | |
1084 | + | |
1085 | + return inputMethodRequestsHandler; | |
1086 | + } | |
1087 | + | |
1088 | + // | |
1089 | + // An implementation of the InputMethodRequests interface. | |
1090 | + // | |
1091 | + class InputMethodRequestsHandler implements InputMethodRequests { | |
1092 | + public AttributedCharacterIterator cancelLatestCommittedText( | |
1093 | + Attribute[] attributes) { | |
1094 | + return new AttributedString("").getIterator(); | |
1095 | + } | |
1096 | + | |
1097 | + public AttributedCharacterIterator getCommittedText(int beginIndex, | |
1098 | + int endIndex, Attribute[] attributes) { | |
1099 | + return new AttributedString("").getIterator(); | |
1100 | + } | |
1101 | + | |
1102 | + public int getCommittedTextLength() { | |
1103 | + return 0; | |
1104 | + } | |
1105 | + | |
1106 | + public int getInsertPositionOffset() { | |
1107 | + return 0; | |
1108 | + } | |
1109 | + | |
1110 | + public TextHitInfo getLocationOffset(int x, int y) { | |
1111 | + return TextHitInfo.leading(0); | |
1112 | + } | |
1113 | + | |
1114 | + public Rectangle getTextLocation(TextHitInfo hitInfo) { | |
1115 | + Rectangle r; | |
1116 | + | |
1117 | + TextPositionInfo pos = getSelectionBegin(); | |
1118 | + if (pos != null) { | |
1119 | + r = new Rectangle(pos.x + offset.x, pos.y + offset.y, | |
1120 | + 1, pos.lineHeight); | |
1121 | + } | |
1122 | + else { | |
1123 | + r = new Rectangle(); | |
1124 | + } | |
1125 | + Point p = getLocationOnScreen(); | |
1126 | + r.translate(p.x, p.y); | |
1127 | + | |
1128 | + return r; | |
1129 | + } | |
1130 | + | |
1131 | + public AttributedCharacterIterator getSelectedText( | |
1132 | + Attribute[] attributes) { | |
1133 | + return new AttributedString("").getIterator(); | |
1134 | + } | |
1135 | + } | |
1136 | + | |
1137 | + /*end[JDK1.2]*/ | |
1138 | + | |
1139 | +} | |
1140 | + | |
1141 | + | |
1142 | +/** | |
1143 | + * The ClickableTextAction event that is originated from a TextEditView. | |
1144 | + */ | |
1145 | +class ClickableTextActionEvent extends AWTEvent { | |
1146 | + ClickableTextAction action; | |
1147 | + | |
1148 | + ClickableTextActionEvent(Object source, ClickableTextAction action) { | |
1149 | + super(source, AWTEvent.RESERVED_ID_MAX + 1); | |
1150 | + this.action = action; | |
1151 | + } | |
1152 | + | |
1153 | + ClickableTextAction getClickableTextAction() { | |
1154 | + return action; | |
1155 | + } | |
1156 | +} |