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

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

Improved widget linking... track-link coversion command

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