source: trunk/src/org/apollo/items/EmulatedTextItem.java@ 1102

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

File size: 12.2 KB
Line 
1package org.apollo.items;
2
3import java.awt.event.KeyEvent;
4import java.awt.event.KeyListener;
5import java.awt.event.MouseEvent;
6import java.awt.event.MouseListener;
7import java.awt.event.MouseMotionListener;
8import java.util.LinkedList;
9import java.util.List;
10
11import org.expeditee.core.Clip;
12import org.expeditee.core.Colour;
13import org.expeditee.core.Cursor;
14import org.expeditee.core.Font;
15import org.expeditee.core.Point;
16import org.expeditee.core.EnforcedClipStack.EnforcedClipKey;
17import org.expeditee.core.bounds.AxisAlignedBoxBounds;
18import org.expeditee.gio.EcosystemManager;
19import org.expeditee.gio.GraphicsManager;
20import org.expeditee.gio.gesture.StandardGestureActions;
21import org.expeditee.gui.DisplayController;
22import org.expeditee.gui.Frame;
23import org.expeditee.gui.Popup;
24import org.expeditee.gui.PopupManager;
25import org.expeditee.items.Text;
26import org.expeditee.items.Item.HighlightMode;
27
28/**
29 * Its a terrible hack - but it will do.
30 *
31 * @author Brook Novak
32 *
33 */
34public class EmulatedTextItem
35{
36 private List<TextChangeListener> textChangeListeners = new LinkedList<TextChangeListener>();
37
38 private RestrictedTextItem emulatedSource;
39 private LabelEditPopup popup = null;
40
41 private static final Font DEFAULT_FONT = new Font("Serif-Plain", Font.Style.PLAIN, 12);
42
43 /**
44 * Constructor.
45 *
46 * @param parent
47 * Must not be null: the parent that lives directly on the content pane.
48 *
49 * @param offset
50 * The offset to add to the label position - from the parents top left corner.
51 * Must not be null.
52 */
53 public EmulatedTextItem(String text, Point position)
54 {
55 assert(position != null);
56
57 emulatedSource = new RestrictedTextItem();
58 emulatedSource.setPosition(position);
59 emulatedSource.setFont(DEFAULT_FONT);
60 emulatedSource.setText(text);
61
62 popup = new LabelEditPopup();
63 PopupManager.getInstance().add(popup);
64 }
65
66 @Override
67 public void finalize()
68 {
69 PopupManager.getInstance().remove(popup);
70 }
71
72 public void setFontStyle(Font.Style style)
73 {
74 emulatedSource.setFont(emulatedSource.getFont().clone().setStyle(style));
75 }
76
77 public void setFontSize(float size)
78 {
79 emulatedSource.setSize(size);
80 }
81
82 public void setPosition(Point position)
83 {
84 emulatedSource.setPosition(position);
85 }
86
87 /**
88 * Determines whether or no a mouse event is on the emulated text item.
89 * @param swingME
90 * Can be null.
91 *
92 * @return
93 * True if the event is over the emulated item.
94 */
95 private boolean isOnEmulatedText()
96 {
97 Point p = EcosystemManager.getInputManager().getCursorPosition();
98
99 return isOnEmulatedText(p.x, p.y);
100 }
101
102 private boolean isOnEmulatedText(int expX, int expY)
103 {
104 return emulatedSource.contains(expX, expY);
105 }
106
107 /**
108 * Applies highlights - shows popup
109 *
110 * @param e
111 * @param parent
112 */
113 public boolean onMouseMoved(MouseEvent e)
114 {
115 if (isOnEmulatedText()) {
116
117 if (emulatedSource.getHighlightMode() != HighlightMode.Normal) {
118 emulatedSource.setHighlightMode(HighlightMode.Normal);
119 emulatedSource.setHighlightColorToDefault();
120 repaint();
121 }
122
123 return true;
124 } else {
125 lostFocus();
126 }
127
128 return false;
129 }
130
131 public boolean onKeyTyped(KeyEvent e)
132 {
133 Point p = EcosystemManager.getInputManager().getCursorPosition();
134
135 // If so and the event really comes from its parent ...
136 if (isOnEmulatedText(p.x, p.y)) {
137
138 int yOffset = -6;
139
140 EcosystemManager.getGraphicsManager().setCursor(new Cursor(Cursor.CursorType.TEXT));
141
142 // If a alphanumeric text entry...
143 if (!e.isActionKey() &&
144 Character.isLetterOrDigit(e.getKeyChar()) ||
145 (e.getKeyChar() == Text.BACKSPACE_CHARACTER && !emulatedSource.isEmpty()) ||
146 (e.getKeyChar() == Text.DELETE_CHARACTER && !emulatedSource.isEmpty()) ||
147 e.getKeyChar() == ' ' ||
148 e.getKeyChar() == '_' ||
149 e.getKeyChar() == '&' ||
150 e.getKeyChar() == '(' ||
151 e.getKeyChar() == ')' ||
152 e.getKeyChar() == '-'
153 )
154 {
155 // Insert the text according to the current mouse position
156 Point newMouse = emulatedSource.insertChar(e.getKeyChar(), p.x, p.y);
157
158 List<String> lines = emulatedSource.getTextList();
159 if (lines != null && lines.size() > 1) {
160 emulatedSource.setText(lines.get(0));
161 }
162
163 // Notify observers
164 fireTextChanged();
165
166 // Move "cursored mouse"
167 if (newMouse != null) {
168 EcosystemManager.getInputManager().setCursorPosition(new Point((int) newMouse.x, (int) newMouse.y + yOffset));
169 }
170 }
171
172 if (emulatedSource.hasSelection()) {
173 emulatedSource.clearSelection();
174 repaint();
175 }
176
177 return true;
178
179 }
180
181 return false;
182 }
183
184 /**
185 *
186 * @param e
187 *
188 * @param parent
189 */
190 public boolean onKeyPressed(KeyEvent e)
191 {
192 Point p = EcosystemManager.getInputManager().getCursorPosition();
193
194 // If so and the event really comes from its parent ...
195 if (isOnEmulatedText(p.x, p.y)) {
196
197 int yOffset = -6;
198
199 EcosystemManager.getGraphicsManager().setCursor(new Cursor(Cursor.CursorType.TEXT));
200
201 // If a alphanumeric text entry...
202 if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_RIGHT) {
203
204 Point newMouse = emulatedSource.moveCursor(
205 (e.getKeyCode() == KeyEvent.VK_LEFT) ? Text.LEFT : Text.RIGHT,
206 (float)p.getX(), (float)p.getY(), false, false);
207
208 if (newMouse != null) {
209 EcosystemManager.getInputManager().setCursorPosition(new Point((int) newMouse.x, (int) newMouse.y + yOffset));
210 }
211
212 }
213
214 if (emulatedSource.hasSelection()) {
215 emulatedSource.clearSelection();
216 repaint();
217 }
218
219 return true;
220
221 }
222
223 return false;
224 }
225
226 /**
227 *
228 * @param e
229 *
230 * @param parent
231 */
232 public boolean onKeyReleased(KeyEvent e)
233 {
234 return isOnEmulatedText();
235 }
236
237
238 public boolean onMouseReleased(MouseEvent e)
239 {
240 Point p = EcosystemManager.getInputManager().getCursorPosition();
241
242 // If so and the event really comes from its parent ...
243 if (isOnEmulatedText(p.x, p.y)) {
244
245 String toMoveIntoFreespace = null;
246
247 if (e.getButton() == MouseEvent.BUTTON1) {
248 // LINK
249 } else if (e.getButton() == MouseEvent.BUTTON2 && emulatedSource.hasSelection() && !emulatedSource.isEmpty()) {
250
251 invalidate();
252
253 toMoveIntoFreespace = emulatedSource.cutSelectedText();
254
255 } else if (e.getButton() == MouseEvent.BUTTON3 && emulatedSource.hasSelection() && !emulatedSource.isEmpty()) {
256 toMoveIntoFreespace = emulatedSource.copySelectedText();
257
258 } else if (e.getButton() == MouseEvent.BUTTON3 &&
259 !emulatedSource.hasSelection() &&
260 !emulatedSource.isEmpty()) {
261 toMoveIntoFreespace = emulatedSource.getText();
262 }
263
264 if (toMoveIntoFreespace != null) {
265
266 Frame target = DisplayController.getCurrentFrame();
267 if (target != null) {
268 Text selectionCopy = new Text(target.getNextItemID(), toMoveIntoFreespace);
269 selectionCopy.setPosition(p.x, p.y);
270 selectionCopy.setSize(emulatedSource.getSize());
271 StandardGestureActions.pickup(selectionCopy);
272 lostFocus();
273 }
274
275
276 }
277
278 emulatedSource.clearSelection();
279 repaint();
280
281 return true;
282 }
283
284 return false;
285 }
286
287 public boolean onMousePressed(MouseEvent e)
288 {
289 Point p = EcosystemManager.getInputManager().getCursorPosition();
290
291 // If so and the event really comes from its parent ...
292 if (isOnEmulatedText(p.x, p.y)) {
293
294 emulatedSource.clearSelection();
295 emulatedSource.setSelectionStart(p.x, p.y, e.getButton());
296
297 repaint();
298
299 return true;
300 }
301
302 return false;
303 }
304
305 public boolean onMouseClicked(MouseEvent e)
306 {
307 return isOnEmulatedText();
308 }
309
310 public boolean onMouseDragged(MouseEvent e)
311 {
312 Point p = EcosystemManager.getInputManager().getCursorPosition();
313
314 // If so and the event really comes from its parent ...
315 if (isOnEmulatedText(p.x, p.y)) {
316
317 EcosystemManager.getGraphicsManager().setCursor(new Cursor(Cursor.CursorType.TEXT));
318
319 emulatedSource.setSelectionEnd(p.x, p.y);
320 repaint();
321
322 return true;
323 }
324
325 return false;
326 }
327
328 public void addTextChangeListener(TextChangeListener listener)
329 {
330 assert(listener != null);
331 if (!textChangeListeners.contains(listener)) textChangeListeners.add(listener);
332 }
333
334 public void gainFocus()
335 {
336 if (popup != null && !PopupManager.getInstance().isShowing(popup)) {
337 popup.show();
338 }
339 }
340
341 public void lostFocus()
342 {
343 EcosystemManager.getGraphicsManager().setCursor(new Cursor(Cursor.CursorType.DEFAULT));
344
345 boolean dirty = false;
346 if (emulatedSource.hasSelection()) {
347 emulatedSource.clearSelection();
348 dirty = true;
349 }
350
351 if (emulatedSource.getHighlightMode() != HighlightMode.None) {
352 emulatedSource.setHighlightMode(HighlightMode.None);
353 emulatedSource.setHighlightColorToDefault();
354 dirty = true;
355 }
356
357 if (popup != null && PopupManager.getInstance().isShowing(popup)) {
358 popup.hide();
359 }
360
361 if (dirty) repaint();
362
363 }
364
365 /**
366 * Sets the text and fires a text changed event if the text has changed.
367 *
368 * @param text
369 * Null is allowed
370 */
371 public void setText(String text)
372 {
373 if (text == null) text = "";
374
375 if (emulatedSource.getText().equals(text)) return;
376
377 invalidate();
378
379 this.emulatedSource.setText(text);
380
381 invalidate();
382
383 fireTextChanged();
384 }
385
386 public String getText()
387 {
388 return emulatedSource.getText();
389 }
390
391 private void fireTextChanged()
392 {
393 for (TextChangeListener listener : textChangeListeners) {
394 listener.onTextChanged(this, this.emulatedSource.getText());
395 }
396 }
397
398 public void setBackgroundColor(Colour c)
399 {
400 emulatedSource.setBackgroundColor(c);
401 }
402
403 private void repaint()
404 {
405 if (popup != null && PopupManager.getInstance().isShowing(popup)) {
406 popup.invalidateAppearance();
407 DisplayController.requestRefresh(true);
408 } else {
409 invalidate();
410 DisplayController.requestRefresh(true);
411 }
412 }
413
414 private void invalidate()
415 {
416 DisplayController.invalidateArea(emulatedSource.getDrawingArea());
417 }
418
419 public void paint()
420 {
421 GraphicsManager gm = EcosystemManager.getGraphicsManager();
422
423 EnforcedClipKey key = gm.pushClip(new Clip(emulatedSource.getDrawingArea()));
424
425 emulatedSource.paint();
426
427 gm.popClip(key);
428 }
429
430 public interface TextChangeListener
431 {
432 public void onTextChanged(Object source, String newLabel);
433 }
434
435 private class LabelEditPopup extends Popup implements KeyListener, MouseListener, MouseMotionListener
436 {
437 LabelEditPopup()
438 {
439 super(null); // No animation (instant show/hide)
440 super.setConsumeBackClick(false);
441 super.setBorderThickness(0);
442 }
443
444 @Override
445 public void onHide()
446 {
447 }
448
449 @Override
450 public void onShow()
451 {
452 }
453
454 @Override
455 public void paintInternal()
456 {
457 EmulatedTextItem.this.paint();
458 }
459
460 @Override
461 public AxisAlignedBoxBounds getFullBounds()
462 {
463 return emulatedSource.getDrawingArea();
464 }
465
466 @Override
467 public void mouseDragged(MouseEvent e)
468 {
469 boolean consume = onMouseDragged(e);
470 if (consume) e.consume();
471 }
472
473 @Override
474 public void mouseMoved(MouseEvent e)
475 {
476 boolean consume = onMouseMoved(e);
477 if (consume) e.consume();
478 }
479
480 @Override
481 public void mouseClicked(MouseEvent e)
482 {
483 boolean consume = onMouseClicked(e);
484 if (consume) e.consume();
485 }
486
487 @Override
488 public void mousePressed(MouseEvent e)
489 {
490 boolean consume = onMousePressed(e);
491 if (consume) e.consume();
492 }
493
494 @Override
495 public void mouseReleased(MouseEvent e)
496 {
497 boolean consume = onMouseReleased(e);
498 if (consume) e.consume();
499 }
500
501 @Override
502 public void mouseEntered(MouseEvent e)
503 {
504 // Not required
505 }
506
507 @Override
508 public void mouseExited(MouseEvent e)
509 {
510 // Not required
511 }
512
513 @Override
514 public void keyTyped(KeyEvent e)
515 {
516 boolean consume = onKeyTyped(e);
517 if (consume) e.consume();
518 }
519
520 @Override
521 public void keyPressed(KeyEvent e)
522 {
523 boolean consume = onKeyPressed(e);
524 if (consume) e.consume();
525 }
526
527 @Override
528 public void keyReleased(KeyEvent e)
529 {
530 boolean consume = onKeyReleased(e);
531 if (consume) e.consume();
532 }
533
534 }
535
536 private class RestrictedTextItem extends Text
537 {
538 private Colour selectionColor = null;
539
540 RestrictedTextItem()
541 {
542 super(1);
543 }
544
545 private void updateSelection()
546 {
547 selectionColor = super.getSelectionColour();
548 }
549
550 public void setSelectionStart(float mouseX, float mouseY, int mouseButton)
551 {
552 super.setSelectionStart(mouseX, mouseY);
553 updateSelection();
554 }
555
556 @Override
557 public Colour getSelectionColour()
558 {
559 if (selectionColor != null) {
560 return selectionColor;
561 } else {
562 return super.getSelectionColour();
563 }
564
565 }
566
567
568 }
569}
Note: See TracBrowser for help on using the repository browser.