source: trunk/src_apollo/org/apollo/items/EmulatedTextItem.java@ 315

Last change on this file since 315 was 315, checked in by bjn8, 16 years ago

Apollo spin-off added

File size: 15.1 KB
Line 
1package org.apollo.items;
2
3import java.awt.AWTException;
4import java.awt.Color;
5import java.awt.Component;
6import java.awt.Cursor;
7import java.awt.Font;
8import java.awt.Graphics;
9import java.awt.Graphics2D;
10import java.awt.Point;
11import java.awt.Rectangle;
12import java.awt.Robot;
13import java.awt.Shape;
14import java.awt.event.KeyEvent;
15import java.awt.event.KeyListener;
16import java.awt.event.MouseEvent;
17import java.awt.event.MouseListener;
18import java.awt.event.MouseMotionListener;
19import java.awt.geom.Area;
20import java.awt.geom.Point2D;
21import java.util.LinkedList;
22import java.util.List;
23
24import javax.swing.JComponent;
25import javax.swing.SwingUtilities;
26
27import org.expeditee.gui.Browser;
28import org.expeditee.gui.DisplayIO;
29import org.expeditee.gui.Frame;
30import org.expeditee.gui.FrameGraphics;
31import org.expeditee.gui.FrameMouseActions;
32import org.expeditee.gui.MouseEventRouter;
33import org.expeditee.gui.Popup;
34import org.expeditee.gui.PopupManager;
35import org.expeditee.items.Text;
36import org.expeditee.items.Item.HighlightMode;
37
38/**
39 *
40 * Its a terrible hack - but it will do.
41 *
42 * @author Brook Novak
43 *
44 */
45public class EmulatedTextItem {
46
47 private static final long serialVersionUID = 1L;
48
49 private static Robot robot;
50
51 private List<TextChangeListener> textChangeListeners = new LinkedList<TextChangeListener>();
52
53 private RestrictedTextItem emulatedSource;
54 private LabelEditPopup popup = null;
55 private JComponent parentComponant = null;
56 private Point masterOffset;
57
58 private static final Font DEFAULT_FONT = new Font("Serif-Plain", Font.PLAIN, 12);
59
60 static {
61 try {
62 robot = new Robot();
63 } catch (AWTException e) {
64 e.printStackTrace();
65 }
66 }
67
68 /**
69 * Constructor.
70 *
71 * @param parent
72 * Must not be null: the parent that lives directly on the content pane.
73 *
74 * @param offset
75 * The offset to add to the label position - from the parents top left corner.
76 * Must not be null.
77 */
78 public EmulatedTextItem(JComponent parent, Point offset) {
79 assert(parent != null);
80 assert(offset != null);
81
82 parentComponant = parent;
83 masterOffset = offset;
84 emulatedSource = new RestrictedTextItem();
85 emulatedSource.setPosition(parent.getX() + offset.x, parent.getY() + offset.y);
86 emulatedSource.setFont(DEFAULT_FONT);
87
88 }
89
90
91 public void setFontStyle(int style) {
92 emulatedSource.setFont(emulatedSource.getFont().deriveFont(style));
93 }
94
95 public void setFontSize(float size) {
96 emulatedSource.setSize(size);
97 }
98
99
100 /**
101 * Determines whether or no a mouse event is on the emulated text item.
102 * @param swingME
103 * Can be null.
104 *
105 * @return
106 * True if the event is over the emulated item.
107 */
108 private boolean isOnEmulatedText(MouseEvent swingME) {
109 if (swingME == null) return false;
110 Point p = convertToExpediteeSpace(swingME.getLocationOnScreen());
111 return isOnEmulatedText(p.x, p.y);
112 }
113
114 private boolean isOnEmulatedText(int expX, int expY) {
115 return emulatedSource.contains(expX, expY);
116 }
117
118 private Point convertToExpediteeSpace(Point screenPos) {
119 assert(screenPos != null);
120
121 Point expPoint = new Point(0, 0);
122 SwingUtilities.convertPointToScreen(expPoint, Browser._theBrowser.getContentPane());
123
124 return new Point(screenPos.x - expPoint.x, screenPos.y - expPoint.y);
125 }
126
127 /**
128 * Applies highlights - shows popup
129 *
130 * @param e
131 * @param parent
132 */
133 public boolean onMouseMoved(MouseEvent e, Component parent) {
134
135 if (isOnEmulatedText(e)) {
136
137 if (emulatedSource.getHighlightMode() != HighlightMode.Normal) {
138 emulatedSource.setHighlightMode(HighlightMode.Normal);
139 repaint();
140 }
141
142 if (parent != popup &&
143 (emulatedSource.getBoundsWidth() + masterOffset.x) > (parent.getWidth() - 10)) { // have some give to transition over widget edge
144 if (popup == null) {
145 popup = new LabelEditPopup();
146 }
147
148 int x = (emulatedSource.getPolygon() == null) ? parent.getX() + masterOffset.x : emulatedSource.getPolygon().getBounds().x;
149 int y = (emulatedSource.getPolygon() == null) ? parent.getY() + masterOffset.y : emulatedSource.getPolygon().getBounds().y;
150 x -= 2;
151 y -= 2;
152
153 if (!PopupManager.getInstance().isShowing(popup)) {
154 PopupManager.getInstance().showPopup(
155 popup,
156 new Point(x, y),
157 popup);
158 }
159
160 }
161
162 return true;
163
164 } else {
165
166 lostFocus();
167
168 }
169
170 return false;
171
172 }
173
174 /**
175 *
176 * @param e
177 *
178 * @param parent
179 */
180 public boolean onKeyPressed(KeyEvent e, Component parent) {
181
182 MouseEvent currentME = MouseEventRouter.getCurrentMouseEvent();
183
184 if (currentME == null) return false;
185
186 Point p = convertToExpediteeSpace(currentME.getLocationOnScreen());
187
188 // If so and the event really comes from its parent ...
189 if (currentME.getComponent() == parent &&
190 (isOnEmulatedText(p.x, p.y))) {
191
192 int yOffset = (parent == popup) ? -6 : -6;
193
194 Browser._theBrowser.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
195
196 // If a alphanumeric text entry...
197 if (!e.isActionKey() &&
198 (Character.isLetterOrDigit(e.getKeyChar()) ||
199 (e.getKeyChar() == KeyEvent.VK_BACK_SPACE && !emulatedSource.isEmpty()) ||
200 (e.getKeyChar() == KeyEvent.VK_DELETE && !emulatedSource.isEmpty()) ||
201 e.getKeyChar() == KeyEvent.VK_SPACE ||
202 e.getKeyChar() == KeyEvent.VK_UNDERSCORE)) {
203
204
205 Rectangle[] oldArea = emulatedSource.getDrawingArea();
206 int oldWidth = emulatedSource.getBoundsWidth();
207
208 // Insert the text according to the current mouse position
209 Point2D.Float newMouse = emulatedSource.insertChar(
210 e.getKeyChar(),
211 p.x,
212 p.y);
213
214 List<String> lines = emulatedSource.getTextList();
215 if (lines != null && lines.size() > 1) {
216 emulatedSource.setText(lines.get(0));
217 }
218
219 // Notify observers
220 fireTextChanged();
221
222 // Move "cursured mouse"
223 if (newMouse != null) {
224 Point bloc = Browser._theBrowser.getContentPane().getLocationOnScreen();
225 robot.mouseMove(bloc.x + (int)newMouse.x, bloc.y + (int)newMouse.y + yOffset);
226 }
227
228 // Invalidate emulated item if not in popup (popup will naturally handle invalidation)
229 if (parent != popup) {
230
231 if (oldWidth > emulatedSource.getBoundsWidth()) {
232 for (Rectangle r : oldArea)
233 FrameGraphics.invalidateArea(r);
234 }
235
236 repaint();
237
238 }
239
240 } else if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_RIGHT) {
241
242 Point2D.Float newMouse = emulatedSource.moveCursor(
243 (e.getKeyCode() == KeyEvent.VK_LEFT) ? Text.LEFT : Text.RIGHT,
244 (float)p.getX(), (float)p.getY(), false);
245
246 if (newMouse != null) {
247 Point bloc = Browser._theBrowser.getContentPane().getLocationOnScreen();
248 robot.mouseMove(bloc.x + (int)newMouse.x, bloc.y + (int)newMouse.y + yOffset);
249 }
250
251 }
252
253 if (emulatedSource.hasSelection()) {
254 emulatedSource.clearSelection();
255 repaint();
256 }
257
258 return true;
259
260 }
261
262 return false;
263 }
264
265 /**
266 *
267 * @param e
268 *
269 * @param parent
270 */
271 public boolean onKeyReleased(KeyEvent e, Component parent) {
272 return isOnEmulatedText(MouseEventRouter.getCurrentMouseEvent());
273
274 }
275
276
277 public boolean onMouseReleased(MouseEvent e) {
278
279 Point p = convertToExpediteeSpace(e.getLocationOnScreen());
280
281 // If so and the event really comes from its parent ...
282 if (isOnEmulatedText(p.x, p.y)) {
283
284
285 String toMoveIntoFreespace = null;
286
287 if (e.getButton() == MouseEvent.BUTTON1) {
288 // LINK
289 } else if (e.getButton() == MouseEvent.BUTTON2 &&
290 emulatedSource.hasSelection() &&
291 !emulatedSource.isEmpty() &&
292 emulatedSource.selectionMouseButton == MouseEvent.BUTTON2) {
293
294 invalidate();
295
296 toMoveIntoFreespace = emulatedSource.cutSelectedText();
297
298 } else if (e.getButton() == MouseEvent.BUTTON3 &&
299 emulatedSource.hasSelection() &&
300 !emulatedSource.isEmpty() &&
301 emulatedSource.selectionMouseButton == MouseEvent.BUTTON3) {
302 toMoveIntoFreespace = emulatedSource.copySelectedText();
303
304 } else if (e.getButton() == MouseEvent.BUTTON3 &&
305 !emulatedSource.hasSelection() &&
306 !emulatedSource.isEmpty()) {
307 toMoveIntoFreespace = emulatedSource.getText();
308 }
309
310 if (toMoveIntoFreespace != null) {
311
312 Frame target = DisplayIO.getCurrentFrame();
313 if (target != null) {
314 Text selectionCopy = new Text(target.getNextItemID(), toMoveIntoFreespace);
315 selectionCopy.setPosition(p.x, p.y);
316 selectionCopy.setSize(emulatedSource.getSize());
317 FrameMouseActions.pickup(selectionCopy);
318 lostFocus();
319 }
320
321
322 }
323
324 emulatedSource.clearSelection();
325 repaint();
326
327 return true;
328 }
329
330 return false;
331 }
332
333 public boolean onMousePressed(MouseEvent e) {
334
335 Point p = convertToExpediteeSpace(e.getLocationOnScreen());
336
337 // If so and the event really comes from its parent ...
338 if (isOnEmulatedText(p.x, p.y)) {
339
340 emulatedSource.clearSelection();
341 emulatedSource.setSelectionStart(p.x, p.y, e.getButton());
342
343 repaint();
344
345 return true;
346 }
347
348 return false;
349 }
350
351 public boolean onMouseClicked(MouseEvent e) {
352 return isOnEmulatedText(e);
353 }
354 public boolean onMouseDragged(MouseEvent e) {
355
356 Point p = convertToExpediteeSpace(e.getLocationOnScreen());
357
358 // If so and the event really comes from its parent ...
359 if (isOnEmulatedText(p.x, p.y)) {
360
361 Browser._theBrowser.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
362
363 emulatedSource.setSelectionEnd(p.x, p.y);
364 repaint();
365
366 return true;
367 }
368
369 return false;
370 }
371
372 public void addTextChangeListener(TextChangeListener listener) {
373 assert(listener != null);
374 if (!textChangeListeners.contains(listener))
375 textChangeListeners.add(listener);
376 }
377
378 public void lostFocus() {
379 Browser._theBrowser.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
380
381 boolean dirty = false;
382 if (emulatedSource.hasSelection()) {
383 emulatedSource.clearSelection();
384 dirty = true;
385 }
386
387 if (emulatedSource.getHighlightMode() != HighlightMode.None) {
388 emulatedSource.setHighlightMode(HighlightMode.None);
389 dirty = true;
390 }
391
392 if (popup != null &&
393 PopupManager.getInstance().isShowing(popup)) {
394 PopupManager.getInstance().hidePopup(popup);
395 }
396
397 if (dirty) repaint();
398
399 }
400
401 /**
402 * Sets the text and fires a text changed event if the text has changed.
403 *
404 * @param text
405 * Null is allowed
406 */
407 public void setText(String text) {
408 if (text == null) text = "";
409
410 if (emulatedSource.getText().equals(text)) return;
411
412 invalidate();
413
414 this.emulatedSource.setText(text);
415
416 invalidate();
417
418 fireTextChanged();
419 }
420
421 public String getText() {
422 return emulatedSource.getText();
423 }
424
425 private void fireTextChanged() {
426
427 for (TextChangeListener listener : textChangeListeners)
428 listener.onTextChanged(this, this.emulatedSource.getText());
429
430 }
431
432 public void setBackgroundColor(Color c) {
433 emulatedSource.setBackgroundColor(c);
434 }
435
436 private void repaint() {
437
438 if (popup != null && PopupManager.getInstance().isShowing(popup)) {
439 popup.invalidateAppearance();
440 FrameGraphics.refresh(true);
441 } else {
442 invalidate();
443 FrameGraphics.refresh(true);
444 }
445 }
446
447 private void invalidate() {
448 for (Rectangle r : emulatedSource.getDrawingArea())
449 FrameGraphics.invalidateArea(r);
450 }
451
452 public void paint(Graphics g) {
453
454 if ((popup != null && PopupManager.getInstance().isShowing(popup)) ||
455 !parentComponant.isShowing())
456 return;
457
458 Point pos;
459 try {
460 pos = this.convertToExpediteeSpace(parentComponant.getLocationOnScreen());
461 } catch (Exception e) {
462 e.printStackTrace();
463 return;
464 }
465
466 paint( g,
467 pos.x + masterOffset.x,
468 pos.y + masterOffset.y,
469 parentComponant.getWidth() - masterOffset.x);
470 }
471
472 private void paint(Graphics g, int parentX, int parentY, int maxWidth) {
473
474 if (Browser._theBrowser == null) return;
475
476 /*int xoffset = (emulatedSource.getLink() == null) ? 6 : 18;
477 int yoffset = emulatedSource.getBoundsHeight() - 3;
478 parentX += xoffset;
479 parentY += yoffset;*/
480
481 // Set the emulated position according to the parent position - the parent position
482 // can be from the popup or the actual parent componant
483 if (emulatedSource.getX() != parentX || emulatedSource.getY() != parentY)
484 emulatedSource.setPosition(parentX, parentY);
485
486 Shape saveClip = g.getClip();
487
488 if (maxWidth > 0) {
489
490 Area clip = FrameGraphics.getCurrentClip();
491
492 Area tmpClip = (clip != null) ? clip :
493 new Area(new Rectangle(0, 0,
494 Browser._theBrowser.getContentPane().getWidth(),
495 Browser._theBrowser.getContentPane().getHeight()));
496
497 /* tmpClip.intersect(new Area(new Rectangle(0, 0,
498 parentX + maxWidth - xoffset, Browser._theBrowser.getContentPane().getHeight()
499 )));*/
500
501 tmpClip.intersect(new Area(new Rectangle(0, 0,
502 parentX + maxWidth, Browser._theBrowser.getContentPane().getHeight()
503 )));
504
505 // No intersection
506 if (!tmpClip.isEmpty())
507 g.setClip(tmpClip);
508
509 }
510
511 emulatedSource.paint((Graphics2D)g);
512
513 // Restore
514 if (maxWidth > 0) {
515 g.setClip(saveClip);
516 }
517
518 // Restore emulated position for consuming mouse movements
519 Point pos = this.convertToExpediteeSpace(parentComponant.getLocationOnScreen());
520 emulatedSource.setPosition(
521 pos.x + masterOffset.x,
522 pos.y + masterOffset.y);
523 }
524
525 public interface TextChangeListener
526 {
527 public void onTextChanged(Object source, String newLabel);
528 }
529
530 private class LabelEditPopup extends Popup implements KeyListener, MouseListener, MouseMotionListener {
531
532 private static final long serialVersionUID = 1L;
533
534 LabelEditPopup() {
535 super.setAudoHide(true);
536 super.setConsumeBackClick(false);
537 super.setBorderThickness(0);
538
539 updateSize();
540
541 addKeyListener(this);
542 addMouseListener(this);
543 addMouseMotionListener(this);
544 }
545
546 public void updateSize() {
547 setSize(emulatedSource.getBoundsWidth() + 4, emulatedSource.getBoundsHeight() + 4);
548 }
549
550
551 public void keyPressed(KeyEvent e) {
552 onKeyPressed(e, LabelEditPopup.this);
553 updateSize();
554 }
555
556 public void keyReleased(KeyEvent e) {
557 }
558
559
560 public void keyTyped(KeyEvent e) {
561 }
562
563
564 public void mouseClicked(MouseEvent e) {
565 }
566
567 public void mouseEntered(MouseEvent e) {
568 }
569
570 public void mouseExited(MouseEvent e) {
571 }
572
573 public void mousePressed(MouseEvent e) {
574 onMousePressed(e);
575 }
576
577 public void mouseReleased(MouseEvent e) {
578 onMouseReleased(e);
579 updateSize();
580 }
581
582
583 public void mouseDragged(MouseEvent e) {
584 onMouseDragged(e);
585 }
586
587 public void mouseMoved(MouseEvent e) {
588
589 }
590
591 @Override
592 public void onHide() {
593 super.onHide();
594 }
595
596 @Override
597 public void onShow() {
598 super.onShow();
599 }
600
601
602
603 @Override
604 public void paint(Graphics g) {
605
606 EmulatedTextItem.this.paint(g, 7, 16, -1);
607
608 }
609
610 }
611
612 private class RestrictedTextItem extends Text {
613
614 private Color selectionColor = null;
615 private int selectionMouseButton = -1;
616
617 RestrictedTextItem() {
618 super(1);
619 }
620
621 private void updateSelection(int mouseButton) {
622 selectionColor = super.getSelectionColor(mouseButton);
623 selectionMouseButton = mouseButton;
624 }
625
626 public void setSelectionStart(float mouseX, float mouseY, int mouseButton) {
627 super.setSelectionStart(mouseX, mouseY);
628 updateSelection(mouseButton);
629 }
630
631
632
633 @Override
634 protected Color getSelectionColor(int mouseButton) {
635
636 if (selectionColor != null)
637 return selectionColor;
638
639 else return super.getSelectionColor(mouseButton);
640
641 }
642
643
644 }
645}
Note: See TracBrowser for help on using the repository browser.