source: trunk/src/org/expeditee/gui/FrameKeyboardActions.java@ 67

Last change on this file since 67 was 67, checked in by ra33, 16 years ago

Fixed a bunch of problems with rectangles and resizing the window, as well as adding some more unit tests etc.

File size: 37.4 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Point;
5import java.awt.Toolkit;
6import java.awt.datatransfer.DataFlavor;
7import java.awt.datatransfer.StringSelection;
8import java.awt.event.KeyEvent;
9import java.awt.event.KeyListener;
10import java.util.ArrayList;
11import java.util.LinkedList;
12import java.util.List;
13import java.util.StringTokenizer;
14
15import org.expeditee.actions.Actions;
16import org.expeditee.actions.NavigationActions;
17import org.expeditee.actions.Simple;
18import org.expeditee.io.Logger;
19import org.expeditee.items.Dot;
20import org.expeditee.items.Item;
21import org.expeditee.items.ItemUtils;
22import org.expeditee.items.Line;
23import org.expeditee.items.Text;
24import org.expeditee.stats.SessionStats;
25
26public class FrameKeyboardActions implements KeyListener {
27
28 private static Text _toRemove = null;
29
30 // these numbers correspond to the Function Key numbers (0 = Escape key)
31 public static final int DROP_DOWN = 0;
32
33 public static final int SIZE_UP = 1;
34
35 public static final int SIZE_DOWN = 2;
36
37 public static final int COLOR_CHANGE = 3;
38
39 public static final int ANNOTATION_CHANGE = 4;
40
41 public static final int DATE_ITEM = 5;
42
43 public static final int NEW_FRAMESET = 6;
44
45 public static final int FONT_STYLE_CHANGE = 7;
46
47 public static final int FONT_FAMILY_CHANGE = 8;
48
49 public static final int AUDIENCE_MODE = 9;
50
51 public static final int XRAY_MODE = 10;
52
53 // public static final int RUN_MENU = 11;
54
55 public static final int FORCE_REPAINT = 12;
56
57 public static final int MINIMUM_SIZE = 10;
58
59 public synchronized void keyTyped(KeyEvent e) {
60 if (Simple.isProgramRunning()) {
61 Simple.KeyStroke(e.getKeyChar());
62 return;
63 }
64
65 // ignore escape character and control characters
66 if (e.getKeyChar() == KeyEvent.VK_ESCAPE || e.isControlDown()) {
67 return;
68 }
69 // ingnore auto TDFC key sequence
70 if (e.getKeyChar() == KeyEvent.VK_ENTER && e.isControlDown())
71 return;
72
73 e.consume();
74 char ch = e.getKeyChar();
75
76 if (e.isAltDown()) {
77 if (FrameUtils.getCurrentItem() == null
78 && !Frame.itemAttachedToCursor()) {
79 Text t = DisplayIO.getCurrentFrame().createNewText(ch + ": ");
80 FrameMouseActions.pickup(t);
81 FrameGraphics.Repaint();
82 } else {
83 // remove the link for alt+l
84 if (ch == 'l') {
85 FrameUtils.getCurrentItem().setLink(null);
86 FrameGraphics.Repaint();
87 }
88 }
89 } else {
90 processChar(ch);
91 }
92 // FrameGraphics.Repaint();
93 }
94
95 public static void processChar(char ch) {
96
97 Item on = FrameUtils.getCurrentItem();
98
99 // permission check
100 if (on != null && on.Permission < Item.PERMISSION_FULL) {
101 FrameGraphics
102 .DisplayMessage("Insufficient permission to edit this item");
103 return;
104 }
105
106 if (_toRemove != null && on != _toRemove) {
107 assert (_toRemove.getLength() == 0);
108 // This line is to protect mistaken removal of items if there is a
109 // bug...
110 if (_toRemove.getLength() == 0)
111 DisplayIO.getCurrentFrame().removeItem(_toRemove);
112 }
113 _toRemove = null;
114
115 // ignore delete and backspace if in free space
116 if (on == null
117 && (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_TAB || ch == KeyEvent.VK_DELETE))
118 return;
119
120 SessionStats.TypedChar(ch);
121
122 // check for dot's being replaced with text
123 if (on != null && on instanceof Dot) {
124 if (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_DELETE) {
125 return;
126 }
127 replaceDot((Dot) on, ch);
128 ItemUtils.EnclosedCheck(DisplayIO.getCurrentFrame().getItems());
129 return;
130 }
131
132 // only text can interact with keyboard events
133 if (on != null && !(on instanceof Text))
134 on = null;
135
136 // DisplayIO.UpdateTitle();
137
138 Text text = (Text) on;
139 // if this text is empty but has not been removed (such as from
140 // ESC-pushdown)
141 if (text != null && text.isEmpty()
142 && (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_DELETE)) {
143 if (text.getLines().size() > 0)
144 replaceText(text);
145 else {
146 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
147 }
148 return;
149 }
150
151 // if the user is in free space, create a new text item
152 if (on == null || !on.isHighlighted()) {
153 SessionStats.CreatedItem();
154 // DisplayIO.UpdateTitle();
155 text = createText(ch);
156 FrameUtils.LastEdited = text;
157 DisplayIO.getCurrentFrame().addItem(text);
158 DisplayIO.setTextCursor(text.getSize());
159 return;
160 }
161
162 DisplayIO.setTextCursor(text.getSize());
163 Point newMouse = text.insertChar(ch, DisplayIO.getMouseX(), DisplayIO
164 .getMouseY());
165 DisplayIO.setCursorPosition(newMouse.x, newMouse.y, false);
166
167 // a change has occured to the Frame
168 text.getParent().setChanged(true);
169
170 // check that the Text item still exists (hasn't been deleted\backspaced
171 // away)
172 if (text.isEmpty()) {
173 _toRemove = text;
174
175 if (text.getAction() != null)
176 text.setActionMark(true);
177 else if (text.getLink() != null)
178 text.setLinkMark(true);
179 else if (text.getLines().size() > 0)
180 replaceText(text);
181 else {
182 // DisplayIO.getCurrentFrame().removeItem(text);
183 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
184 }
185 }
186
187 FrameUtils.LastEdited = text;
188 }
189
190 public static Text replaceDot(Dot dot, char ch) {
191 SessionStats.CreatedItem();
192 Text text = createText(ch);
193 text.setPosition(dot.getPosition());
194 text.setThickness(dot.getThickness());
195 FrameUtils.LastEdited = text;
196
197 //Copy the lines list so it can be modified
198 List<Line> lines = new LinkedList<Line>(dot.getLines());
199 for (Line line : lines)
200 line.replaceEnd(dot, text);
201
202 DisplayIO.getCurrentFrame().removeItem(dot);
203 DisplayIO.getCurrentFrame().addItem(text);
204 return text;
205 }
206
207 // replaces the given text item with a dot
208 private static void replaceText(Text text) {
209 Dot dot = new Dot(text.getX(), text.getY(), text.getID());
210 Item.DuplicateItem(text, dot);
211
212 List<Line> lines = new LinkedList<Line>();
213 lines.addAll(text.getLines());
214 if (lines.size() > 0)
215 dot.setColor(lines.get(0).getColor());
216 for (Line line : lines){
217 line.replaceEnd(text, dot);
218 }
219 text.delete();
220 DisplayIO.getCurrentFrame().addItem(dot);
221 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
222 }
223
224 /**
225 * Creates a new Text Item whose text contains the given character. This
226 * method also moves the mouse cursor to be pointing at the newly created
227 * Text Item ready to insert the next character.
228 *
229 * @param start
230 * The character to use as the initial text of this Item.
231 * @return The newly created Text Item
232 */
233 private static Text createText(char start) {
234 Text t = DisplayIO.getCurrentFrame().createBlankText("" + start);
235
236 Point newMouse = t.insertChar(start, DisplayIO.getMouseX(), DisplayIO
237 .getMouseY());
238 DisplayIO.setCursorPosition(newMouse.x, newMouse.y, false);
239
240 return t;
241 }
242
243 /**
244 * Creates a new Text Item with no text. The newly created Item is a copy of
245 * any ItemTemplate if one is present, and inherits all the attributes of
246 * the Template
247 *
248 * @return The newly created Text Item
249 */
250 private static Text createText() {
251 return DisplayIO.getCurrentFrame().createNewText();
252 }
253
254 private void move(int direction) {
255 Item ip = FrameUtils.getCurrentItem();
256
257 if (ip == null) {
258 navigateFrame(direction);
259 return;
260 }
261
262 Item on = ip;
263 if (on instanceof Text) {
264 // When the user hits the left and right button with mouse
265 // positions over the the frame name navigation occurs
266 if (on.isFrameName()) {
267 navigateFrame(direction);
268 return;
269 } else {
270 FrameUtils.LastEdited = on;
271 Point newMouse = ((Text) on).moveCursor(direction, DisplayIO
272 .getMouseX(), DisplayIO.getMouseY());
273 DisplayIO.setTextCursor(((Text) on).getSize());
274 DisplayIO.setCursorPosition(newMouse, false);
275 }
276 }
277 }
278
279 private void navigateFrame(int direction) {
280 switch (direction) {
281 case Text.RIGHT:
282 case Text.PAGE_UP:
283 NavigationActions.NextFrame(false);
284 break;
285 case Text.LEFT:
286 case Text.PAGE_DOWN:
287 NavigationActions.PreviousFrame(false);
288 break;
289 case Text.HOME:
290 NavigationActions.ZeroFrame();
291 break;
292 case Text.END:
293 NavigationActions.LastFrame();
294 break;
295 }
296 }
297
298 /**
299 * Receives and processes any Function, Control, and Escape key presses
300 *
301 * @param e
302 * The KeyEvent received from the keyboard
303 */
304 public void keyPressed(KeyEvent e) {
305 SessionStats.AddFrameEvent("k" + KeyEvent.getKeyText(e.getKeyCode()));
306
307 FrameUtils.ResponseTimer.restart();
308 e.consume();
309
310 if (Actions.isAgentRunning()) {
311 if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
312 Actions.stopAgent();
313 else
314 Actions.interruptAgent();
315 return;
316 } else if (Simple.isProgramRunning()) {
317 return;
318 }
319
320 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
321 functionKey(DROP_DOWN);
322 SessionStats.Escape();
323 return;
324 } else if (e.getKeyCode() >= KeyEvent.VK_F1
325 && e.getKeyCode() <= KeyEvent.VK_F12) {
326 functionKey(e.getKeyCode() - KeyEvent.VK_F1 + 1);
327 return;
328 } else if (e.isControlDown() && e.getKeyCode() != KeyEvent.VK_CONTROL)
329 controlChar(e, KeyEvent.getKeyText(e.getKeyCode()));
330
331 switch (e.getKeyCode()) {
332 case KeyEvent.VK_LEFT:
333 move(Text.LEFT);
334 break;
335 case KeyEvent.VK_RIGHT:
336 move(Text.RIGHT);
337 break;
338 case KeyEvent.VK_PAGE_DOWN:
339 navigateFrame(Text.PAGE_DOWN);
340 break;
341 case KeyEvent.VK_PAGE_UP:
342 navigateFrame(Text.PAGE_UP);
343 break;
344 case KeyEvent.VK_UP:
345 if (e.isControlDown() || e.isShiftDown()) {
346 DisplayIO.getCurrentFrame();
347 } else {
348 move(Text.UP);
349 }
350 break;
351 case KeyEvent.VK_DOWN:
352 move(Text.DOWN);
353 break;
354 case KeyEvent.VK_END:
355 move(Text.END);
356 break;
357 case KeyEvent.VK_HOME:
358 move(Text.HOME);
359 break;
360 case KeyEvent.VK_ENTER:
361 if (e.isControlDown()) {
362 FrameMouseActions.leftButton(FrameUtils.getCurrentItem());
363 }
364 break;
365 }
366 }
367
368 /**
369 * Currently ignored.
370 */
371 public void keyReleased(KeyEvent e) {
372
373 }
374
375 /**
376 *
377 * @param text
378 * @param yPos
379 * @return the new yPos for the next item
380 */
381 private static int addTextItemToCurrentFrame(String text, int yPos) {
382 if (text.trim().length() > 0) {
383 Text textItem = createText();
384 textItem.setText(text.substring(0, text.length() - 1));
385 textItem.setY(yPos);
386 DisplayIO.getCurrentFrame().addItem(textItem);
387 return textItem.getY() + textItem.getBoundsHeight();
388 }
389 return yPos;
390 }
391
392 /**
393 * Processes all control character keystrokes. Currently Ctrl+C and Ctrl+V
394 * are copy and paste, all other keystrokes are ignored.
395 *
396 * @param ch
397 * The character being pressed along with the control key
398 */
399 private static void controlChar(KeyEvent e, String ch) {
400 // if this is a paste command
401 if (ch.charAt(0) == KeyEvent.VK_V) {
402 try {
403 // read in the data from the clipboard
404 String clip = ((String) Toolkit.getDefaultToolkit()
405 .getSystemClipboard().getContents(null)
406 .getTransferData(DataFlavor.stringFlavor));
407
408 Item clicked = FrameUtils.getCurrentItem();
409
410 if (clicked != null) {
411 // check permissions
412 if (clicked.Permission < Item.PERMISSION_FULL
413 && clicked.getParent() != null
414 && clicked.getParent().getFrameNameItem() != clicked) {
415 FrameGraphics.DisplayMessage("Insufficient Permission");
416 return;
417 }
418
419 Text text = createText();
420 text.setText(clip);
421 List<Item> clipboard = new ArrayList<Item>();
422 clipboard.add(text);
423 List<Item> left = FrameMouseActions.merge(clipboard,
424 clicked);
425 FrameMouseActions.anchor(left);
426 } else {
427
428 StringTokenizer st = new StringTokenizer(clip, "\n", true);
429
430 String temp = "";
431 int y = DisplayIO.getMouseY();
432
433 // separate the clipboard content into items based on
434 // blank lines
435 while (st.hasMoreTokens()) {
436 String s = st.nextToken();
437 // if this is a blank line, then it is an item separator
438 if (s.trim().length() == 0) {
439 y = addTextItemToCurrentFrame(temp, y);
440 temp = "";
441 } else {
442 temp += s + "\n";
443 if (st.hasMoreTokens())
444 st.nextToken();
445 }
446 }
447 // the last item will not be finished by the above loop
448 addTextItemToCurrentFrame(temp, y);
449 }
450
451 FrameGraphics.Repaint();
452 } catch (Exception ex) {
453 }
454 return;
455
456 // if this is a copy command
457 } else if (ch.charAt(0) == KeyEvent.VK_C) {
458 Item ip = FrameUtils.getCurrentItem();
459
460 if (ip != null && ip.Permission < Item.PERMISSION_COPY) {
461 FrameGraphics
462 .DisplayMessage("Insufficient permission to copy that item");
463 return;
464 }
465
466 Item on = null;
467 if (ip != null)
468 on = ip;
469
470 if (on == null || !(on instanceof Text))
471 return;
472
473 String text = "";
474
475 List<String> lines = ((Text) on).getText();
476 for (String s : lines)
477 text += s + "\n";
478 // remove the last '\n'
479 text = text.substring(0, text.length() - 1);
480
481 // add the text of the item to the clipboard
482 StringSelection selection = new StringSelection(text);
483 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
484 selection, null);
485
486 FrameGraphics.DisplayMessage("Item copied to clipboard");
487 return;
488 } else if (ch.charAt(0) == KeyEvent.VK_D) {
489 // perform a delete operation
490 processChar((char) KeyEvent.VK_DELETE);
491 FrameGraphics.Repaint();
492 } else if (ch.charAt(0) == KeyEvent.VK_J) {
493
494 Text text = getCurrentTextItem();
495 if (text == null)
496 return;
497
498 if (text.getWidth() < 0)
499 text.setWidth(text.getBoundsWidth() - Item.MARGIN_RIGHT
500 - UserSettings.Gravity);
501
502 text.setJustification(Item.JUSTIFICATION_LEFT);
503
504 FrameGraphics.Repaint();
505 return;
506 } else if (ch.charAt(0) == KeyEvent.VK_S) {
507 // split the current text item
508 Text text = getCurrentTextItem();
509 if (text == null)
510 return;
511 List<String> textLines = text.getText();
512 if (textLines.size() <= 1)
513 return;
514 // remove all except the first line of text from the item being
515 // split
516 text.setText(textLines.get(0));
517 int y = text.getY();
518 for (int i = 1; i < textLines.size(); i++) {
519 Text newText = text.copy();
520 newText.setText(textLines.get(i));
521 y += newText.getBoundsHeight();
522 newText.setY(y);
523
524 Frame current = DisplayIO.getCurrentFrame();
525 // update the items ID to prevent conflicts with the current
526 // frame
527 newText.setID(current.getNextItemID());
528
529 current.addItem(newText);
530 }
531 FrameGraphics.Repaint();
532
533 return;
534 }
535
536 Logger.Log(Logger.USER, Logger.CONTROL_CHAR, "User pressing: Ctrl+"
537 + ch);
538 }
539
540 /**
541 * Gets the currently selected item if the user is allowed to modify it.
542 *
543 * @return null if the currently selected item is not a Text item that the
544 * user has permission to modify
545 */
546 private static Text getCurrentTextItem() {
547 Item ip = FrameUtils.getCurrentItem();
548
549 if (ip != null && ip.Permission < Item.PERMISSION_FULL) {
550 FrameGraphics
551 .DisplayMessage("Insufficient permission to copy that item");
552 return null;
553 }
554
555 Item on = null;
556 if (ip != null)
557 on = ip;
558
559 if (on == null || !(on instanceof Text))
560 return null;
561
562 return (Text) on;
563 }
564
565 public static void functionKey(int key) {
566 functionKey(key, 1);
567 }
568
569 /**
570 * Called when a Function key has been pressed, and performs the specific
571 * action based on the key.
572 */
573 public static void functionKey(int key, int repeat) {
574 // get whatever the user is pointing at
575 Item on = FrameUtils.getCurrentItem();
576
577 // check for enclosed mode
578 if (on == null || on == null) {
579 List<Item> enclosed = FrameUtils.getCurrentItems();
580
581 if (enclosed != null && enclosed.size() > 0) {
582 // ensure only one dot\line is present in the list
583 List<Item> dots = FrameUtils.getEnclosingDots();
584 List<Item> connected = dots.get(0).getAllConnected();
585
586 // only resize lines if they are not surrounding some non line
587 // items
588 boolean resizeLines = true;
589 for (Item ip : enclosed) {
590 if (ip != null) {
591 if (!(ip instanceof Line) && !(ip instanceof Dot)) {
592 resizeLines = false;
593 break;
594 }
595 }
596 }
597
598 for (Item ip : enclosed) {
599 if (ip != null && !(connected.indexOf(ip) > 0))
600 switch (key) {
601 case SIZE_UP:
602 if (resizeLines
603 || (!(ip instanceof Line) && !(ip instanceof Dot)))
604 SetSize(ip, repeat, false);
605 break;
606 case SIZE_DOWN:
607 if (resizeLines
608 || (!(ip instanceof Line) && !(ip instanceof Dot)))
609 SetSize(ip, -repeat, false);
610 break;
611 // case COLOR_CHANGE: SetFillColor(ip); break;
612 case ANNOTATION_CHANGE:
613 ToggleAnnotation(ip);
614 break;
615 case FONT_STYLE_CHANGE:
616 ToggleFontStyle(ip);
617 break;
618 case FONT_FAMILY_CHANGE:
619 ToggleFontFamily(ip);
620 break;
621 case DATE_ITEM:
622 AddDate(ip);
623 break;
624 }
625 }
626
627 if (key == COLOR_CHANGE && connected.size() > 0) {
628 for (Item d : dots) {
629 SetFillColor(d);
630 break;
631 }
632 }
633 return;
634 }
635 }
636
637 switch (key) {
638 case DROP_DOWN:
639 Drop(on);
640 break;
641 case SIZE_UP:
642 SetSize(on, repeat, true);
643 break;
644 case SIZE_DOWN:
645 SetSize(on, -repeat, true);
646 break;
647 case COLOR_CHANGE:
648 SetColor(on);
649 break;
650 case ANNOTATION_CHANGE:
651 ToggleAnnotation(on);
652 break;
653 case FONT_STYLE_CHANGE:
654 ToggleFontStyle(on);
655 break;
656 case FONT_FAMILY_CHANGE:
657 ToggleFontFamily(on);
658 break;
659 case DATE_ITEM:
660 AddDate(on);
661 break;
662 case NEW_FRAMESET:
663 CreateFrameset(on);
664 break;
665 case XRAY_MODE:
666 ToggleXRayMode(on);
667 break;
668 case AUDIENCE_MODE:
669 ToggleAudience(on);
670 break;
671 // case RUN_MENU:
672 // RunFirstMenuItem(on);
673 // break;
674 case FORCE_REPAINT:
675 Repaint(on);
676 break;
677 }
678 }
679
680 public static final String DEFAULT_NEW_ITEM_TEXT = "";
681
682 /**
683 * Performs the dropping action: If the cursor is in free space then: the
684 * cursor is repositioned below the last non-annotation text item. If the
685 * cursor is on an item, and has items attached then: the cusor is
686 * positioned below the pointed to item, and the items below are 'pushed
687 * down' to make room.
688 *
689 * @param toDropFrom
690 * The Item being pointed at by the mouse, may be null to
691 * indicate the cursor is in free space.
692 */
693 private static void Drop(Item ip) {
694 String newItemText = DEFAULT_NEW_ITEM_TEXT;
695
696 // if a line is being rubber-banded, this is a no-op
697 if (Frame.rubberbandingLine())
698 return; // No-op
699
700 // drop requires no permissions
701 Item toDropFrom = null;
702 if (ip != null)
703 toDropFrom = ip;
704
705 // if the cursor is in free space then the drop will happen from the
706 // last non annotation text item on the frame
707 if (toDropFrom == null) {
708 toDropFrom = DisplayIO.getCurrentFrame()
709 .getLastNonAnnotationTextItem();
710 }
711
712 // if no item was found, return
713 if (toDropFrom == null) {
714 FrameGraphics.ErrorMessage("No item could be found to drop from");
715 return;
716 }
717
718 if (!(toDropFrom instanceof Text)) {
719 FrameGraphics.DisplayMessage("Only text items can be dropped from");
720 return;
721 }
722
723 // Get the list of items that must be dropped
724 List<Item> column = DisplayIO.getCurrentFrame().getColumn(toDropFrom);
725
726 if (column == null) {
727 FrameGraphics.ErrorMessage("No column found to align items to");
728 return;
729 }
730
731 Text title = DisplayIO.getCurrentFrame().getTitle();
732
733 // We wont do auto bulleting when dropping from titles
734 if (toDropFrom != title) {
735 newItemText = getAutoBullet(((Text) toDropFrom).getFirstLine());
736 }
737
738 Text dummyItem = null;
739
740 if (Frame.textItemAttachedToCursor()) {
741 dummyItem = (Text) Frame.getItemAttachedToCursor();
742 String autoBullet = getAutoBullet(dummyItem.getTextNoList());
743
744 if (autoBullet.length() > 0)
745 newItemText = "";
746 dummyItem.setText(newItemText + dummyItem.getTextNoList());
747 }
748 dummyItem = createText();
749 if (Frame.textItemAttachedToCursor()) {
750 Text t = (Text) Frame.getItemAttachedToCursor();
751 dummyItem.setSize(t.getSize());
752 for (int i = 0; i < t.getText().size(); i++) {
753 newItemText += '\n';
754 }
755 }
756
757 dummyItem.setText(newItemText);
758
759 // If the only item on the frame is the title and the frame name just
760 // drop a specified distance below the title
761 if (column.size() == 0) {
762 Text itemTemplate = DisplayIO.getCurrentFrame().getItemTemplate();
763 int xPos = title.getX();
764 int yPos = title.getY() + title.getBoundsHeight()
765 + itemTemplate.getBoundsHeight();
766 dummyItem.setPosition(xPos, yPos);
767 DisplayIO.setCursorPosition(xPos, yPos);
768 } else {
769 int yPos = column.get(0).getY() + 1;
770 // Either position the new item below the title or just above
771 // the first item below the title
772 if (toDropFrom == title)
773 yPos = Math
774 .min(column.get(0).getY() - 1, title.getY()
775 + title.getBoundsHeight()
776 + dummyItem.getBoundsHeight());
777 dummyItem.setPosition(column.get(0).getX(), yPos);
778 column.add(dummyItem);
779 FrameUtils.Align(column, false, 0);
780 // Check if it will be outside the frame area
781 if (dummyItem.getY() < 0
782 || dummyItem.getY() > FrameGraphics.getMaxFrameSize()
783 .getHeight()) {
784 // Check for the @more tag!
785 Item i = ItemUtils.FindTag(DisplayIO.getCurrentFrame()
786 .getItems(), "@More");
787 if (i != null) {
788 Frame firstFrame = DisplayIO.getCurrentFrame();
789 boolean mouseMoved = FrameMouseActions.tdfc(i);
790 Frame moreFrame = DisplayIO.getCurrentFrame();
791 moreFrame.setTitle(firstFrame.getTitle().getTextNoList());
792 // need to move the mouse to the top of the frame if there
793 // wasnt an @start on it
794 if (!mouseMoved) {
795 Item moreTitle = moreFrame.getTitle();
796 moreTitle.Permission = Item.PERMISSION_FULL;
797 Drop(moreTitle);
798 }
799 // Add the bullet text to the item
800 dummyItem.setPosition(DisplayIO.getMouseX(), DisplayIO
801 .getMouseY());
802 } else {
803 FrameGraphics
804 .WarningMessage("Can not create items outside the frame area");
805 // ensures correct repainting when items don't move
806 DisplayIO.setCursorPosition(DisplayIO.getMouseX(),
807 DisplayIO.getMouseY());
808 return;
809 }
810 }
811 if (!Frame.textItemAttachedToCursor() && !dummyItem.isEmpty()) {
812 DisplayIO.getCurrentFrame().addItem(dummyItem);
813 }
814
815 // Move the item to the cursor position
816 if (Frame.itemAttachedToCursor()) {
817 DisplayIO.setCursorPosition(dummyItem.getX(), dummyItem.getY());
818 Item firstItem = Frame.getItemAttachedToCursor();
819 int deltaX = firstItem.getX() - dummyItem.getX();
820 int deltaY = firstItem.getY() - dummyItem.getY();
821
822 for (Item i : Frame.FreeItems) {
823 i.setPosition(i.getX() - deltaX, i.getY() - deltaY);
824 }
825 } else {
826 DisplayIO
827 .setCursorPosition(
828 dummyItem.getEndParagraphPosition().x,
829 dummyItem.getY());
830 }
831 }
832
833 DisplayIO.resetCursorOffset();
834 FrameGraphics.Repaint();
835 }
836
837 /**
838 * Gets the next letter sequence for a given string to be used in auto
839 * lettering.
840 *
841 * @param s
842 * a sequence of letters
843 * @return the next sequence of letters
844 */
845 static private String nextLetterSequence(String s) {
846 if (s.length() > 1)
847 return s;
848
849 if (s.equals("z"))
850 return "a";
851
852 return (char) ((int) s.charAt(0) + 1) + "";
853 }
854
855 public static String getBullet(String s) {
856 return getBullet(s, false);
857 }
858
859 public static String getAutoBullet(String s) {
860 return getBullet(s, true);
861 }
862
863 private static String getBullet(String s, boolean nextBullet) {
864 String newItemText = DEFAULT_NEW_ITEM_TEXT;
865
866 /*
867 * Item i = ItemUtils.FindTag(DisplayIO.getCurrentFrame().getItems(),
868 * "@NoAutoBullets"); if (i != null) return newItemText;
869 */
870
871 // figure out the type of the text item
872 // This allows us to do auto bulleting
873 if (s != null && s.length() > 1) {
874 // First check for text beginning with * @ # etc
875 // These are simple auto bullets
876 if (!Character.isLetterOrDigit(s.charAt(0))
877 && !Character.isSpaceChar(s.charAt(0))) {
878 if (s.charAt(0) == '*' || s.charAt(0) == '+'
879 || s.charAt(0) == '-' || s.charAt(0) == '>'
880 || s.charAt(0) == '=' || s.charAt(0) == '@'
881 || s.charAt(0) == '~' || s.charAt(0) == '#') {
882 int nonSpaceIndex = 1;
883 while (nonSpaceIndex < s.length()
884 && s.charAt(nonSpaceIndex) == ' ') {
885 nonSpaceIndex++;
886 }
887 // we must have a special char followed by >= 1 space
888 if (nonSpaceIndex > 1)
889 newItemText = s.substring(0, nonSpaceIndex);
890 }
891 // Auto numbering and lettering
892 } else {
893 if (Character.isDigit(s.charAt(0))) {
894 newItemText = getAutoNumber(s, nextBullet);
895 // Auto lettering
896 } else if (Character.isLetter(s.charAt(0))) {
897 newItemText = getAutoLetter(s, nextBullet);
898 }
899 }
900 }
901 return newItemText;
902 }
903
904 private static boolean isAutoNumberOrLetterChar(char c) {
905 return c == ':' || c == '-' || c == '.' || c == ')' || c == '>';
906 }
907
908 /**
909 * Gets the string to be used to start the next auto numbered text item.
910 *
911 * @param s
912 * the previous text item
913 * @return the beginning of the next auto numbered text item
914 */
915 private static String getAutoNumber(String s, boolean nextBullet) {
916 String newItemText = DEFAULT_NEW_ITEM_TEXT;
917
918 int nonDigitIndex = 1;
919 while (Character.isDigit(s.charAt(nonDigitIndex))) {
920 nonDigitIndex++;
921
922 if (nonDigitIndex + 1 >= s.length())
923 return DEFAULT_NEW_ITEM_TEXT;
924 }
925
926 if (isAutoNumberOrLetterChar(s.charAt(nonDigitIndex))) {
927
928 // we must have a number followed one non letter
929 // then one or more spaces
930 int nonSpaceIndex = nonDigitIndex + 1;
931 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
932 nonSpaceIndex++;
933 }
934
935 if (nonSpaceIndex > nonDigitIndex + 1) {
936 if (nextBullet)
937 newItemText = (Integer.parseInt(s.substring(0,
938 nonDigitIndex)) + 1)
939 + s.substring(nonDigitIndex, nonSpaceIndex);
940 else
941 newItemText = s.substring(0, nonSpaceIndex);
942 }
943 }
944 return newItemText;
945 }
946
947 /**
948 * Gets the string to be used to start the next auto lettered text item.
949 *
950 * @param s
951 * the previous text items
952 * @return the initial text for the new text item
953 */
954 private static String getAutoLetter(String s, boolean nextBullet) {
955 String newItemText = DEFAULT_NEW_ITEM_TEXT;
956
957 int nonLetterIndex = 1;
958
959 if (isAutoNumberOrLetterChar(s.charAt(nonLetterIndex))) {
960
961 // Now search for the next non space character
962 int nonSpaceIndex = nonLetterIndex + 1;
963 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
964 nonSpaceIndex++;
965 }
966
967 // If there was a space then we have reached the end of our auto
968 // text
969 if (nonSpaceIndex > nonLetterIndex + 1) {
970 if (nextBullet)
971 newItemText = nextLetterSequence(s.substring(0,
972 nonLetterIndex))
973 + s.substring(nonLetterIndex, nonSpaceIndex);
974 else
975 newItemText = s.substring(0, nonSpaceIndex);
976 }
977 }
978 return newItemText;
979 }
980
981 /**
982 * Adjusts the size of the given Item, by the given amount. Note: The amount
983 * is relative and can be positive or negative.
984 *
985 * @param toSet
986 * The Item whose size is to be adjusted
987 * @param diff
988 * The amount to adjust the Item's size by
989 * @param moveCursor
990 * true if the cursor position should be automatically adjusted
991 * with resizing
992 */
993 private static void SetSize(Item ip, int diff, boolean moveCursor) {
994 List<Item> toSize = new ArrayList<Item>();
995 // the mouse is only moved when the Item is on the frame, not free
996 // boolean moveMouse = false;
997 Item toSet = null;
998
999 // if the user is not pointing to any item
1000 if (ip == null) {
1001 if (Frame.itemAttachedToCursor())
1002 toSize.addAll(Frame.FreeItems);
1003 else {
1004 FrameGraphics
1005 .DisplayMessage("There are no Items selected on the Frame or on the Cursor");
1006 return;
1007 }
1008 } else {
1009 // check permissions
1010 if (ip.Permission < Item.PERMISSION_FULL) {
1011 FrameGraphics
1012 .DisplayMessage("Insufficient permission to change the size of that item");
1013 return;
1014 }
1015 toSet = ip;
1016 Item currentItem = FrameUtils.getCurrentItem();
1017
1018 if (currentItem != null && (currentItem instanceof Text)
1019 && FrameUtils.getCurrentItems() == null) {
1020 toSize.add(currentItem);
1021 } else {
1022 toSize.addAll(toSet.getLines());
1023 }
1024 }
1025
1026 if (toSet instanceof Line) {
1027 Line line = (Line) toSet;
1028 float current = Math.abs(line.getThickness());
1029 current = Math.max(current + diff, 1);
1030 line.setThickness(current);
1031 FrameGraphics.Repaint();
1032 return;
1033 }
1034
1035 /*
1036 * for(Item i : toSize){ if(i instanceof Dot || i instanceof Line){
1037 * toSize.removeAll(i.getConnected()); toSize.add(i); } }
1038 */
1039
1040 int old_width = 0;
1041 int old_height = 0;
1042
1043 if (toSet != null) {
1044 old_width = toSet.getBoundsWidth();
1045 old_height = toSet.getBoundsHeight();
1046 }
1047
1048 // adjust the size of all the items
1049 for (Item i : toSize) {
1050 // Lines and dots use thickness, not size
1051 if (i instanceof Line) {
1052 Line line = (Line) i;
1053 float current = Math.abs(line.getThickness());
1054 current = Math.max(current + diff, 1);
1055 line.setThickness(current);
1056 } else if (i instanceof Dot) {
1057 Dot dot = (Dot) i;
1058 float current = Math.abs(dot.getThickness());
1059 current = Math.max(current + diff, 1);
1060 dot.setThickness(current);
1061 } else {
1062 int current = Math.abs(i.getSize());
1063 current = Math.max(current + diff, 1);
1064 if (current > MINIMUM_SIZE)
1065 i.setSize(current);
1066 }
1067 }
1068
1069 // center the mouse cursor on the item
1070 if (moveCursor && toSet != null) {
1071 if (!toSet.contains(DisplayIO.getMouseX(), DisplayIO.getMouseY())) {
1072 int x = DisplayIO.getMouseX();
1073 int y = DisplayIO.getMouseY();
1074
1075 if (!toSet.contains(x, toSet.getY()))
1076 x = x - (old_width - toSet.getBoundsWidth());
1077
1078 // text grows 'up', pictures grow 'down'
1079 int direction = -1;
1080 if (toSet instanceof Text)
1081 direction = 1;
1082
1083 if (!toSet.contains(toSet.getX(), y))
1084 y = y
1085 + (direction * (old_height - toSet
1086 .getBoundsHeight()));
1087
1088 DisplayIO.setCursorPosition(x, y);
1089 }
1090
1091 // int x = toSet.getX() + toSet.getBoundsWidth() / 2;
1092 // int y = toSet.getY() + toSet.getBoundsHeight() / 2;;
1093 // DisplayIO.setCursorPosition(x, y);
1094
1095 }
1096
1097 if (toSet != null)
1098 toSet.getParent().setChanged(true);
1099
1100 FrameGraphics.Repaint();
1101 }
1102
1103 private static void SetFillColor(Item item) {
1104 if (item == null)
1105 return;
1106
1107 Item toSet = item;// ;
1108 Color color = toSet.getFillColor();
1109
1110 color = ColorUtils.getNextColor(color, Item.FILL_COLOR_WHEEL);
1111
1112 // TODO what happens if the above statement returns null??
1113
1114 toSet.setFillColor(color);
1115 toSet.getParent().setChanged(true);
1116
1117 FrameGraphics.Repaint();
1118 }
1119
1120 /**
1121 * Sets the colour of the current Item based on its current colour. The
1122 * colours proceed in the order stored in COLOR_WHEEL.
1123 *
1124 * @param toSet
1125 * The Item whose colour is to be changed
1126 */
1127 private static void SetColor(Item ip) {
1128 // first determine the next color
1129 Color color = null;
1130
1131 if (ip == null) {
1132 if (Frame.itemAttachedToCursor()) {
1133 color = Frame.FreeItems.get(0).getColor();
1134 } else {
1135 return;
1136 }
1137 // change the background color if the user is pointing on the
1138 // frame name
1139 } else if (ip == DisplayIO.getCurrentFrame().getFrameNameItem()) {
1140 DisplayIO.getCurrentFrame().toggleBackgroundColor();
1141 return;
1142 } else {
1143 // check permissions
1144 if (ip.Permission < Item.PERMISSION_FULL) {
1145 FrameGraphics
1146 .DisplayMessage("Insufficient permission to change that item's color");
1147 return;
1148 }
1149 color = ip.getColor();
1150 }
1151
1152 color = ColorUtils.getNextColor(color, Item.COLOR_WHEEL);
1153
1154 // if we didnt find the color on the wheel
1155 if (color == null) {
1156 color = DisplayIO.getCurrentFrame().getItemTemplate().getColor();
1157 }
1158
1159 if (Frame.itemAttachedToCursor()) {
1160 for (Item i : Frame.FreeItems)
1161 i.setColor(color);
1162 } else {
1163 Item toSet = ip;
1164 toSet.setColor(color);
1165 toSet.getParent().setChanged(true);
1166
1167 }
1168
1169 FrameGraphics.Repaint();
1170 }
1171
1172 /**
1173 * Toggles the given Item's annotation status on\off.
1174 *
1175 * @param toToggle
1176 * The Item to toggle
1177 */
1178 private static void ToggleAnnotation(Item toToggle) {
1179 if (toToggle == null) {
1180 FrameGraphics.DisplayMessage("There is no Item selected to toggle");
1181 return;
1182 }
1183
1184 // check permissions
1185 if (toToggle.Permission < Item.PERMISSION_FULL) {
1186 FrameGraphics
1187 .DisplayMessage("Insufficient permission to toggle that item's annotation");
1188 return;
1189 }
1190
1191 toToggle.setAnnotation(!toToggle.isAnnotation());
1192 toToggle.getParent().setChanged(true);
1193 FrameGraphics.Repaint();
1194 }
1195
1196 /**
1197 * Toggles the face style of a text item
1198 *
1199 * @param toToggle
1200 * The Item to toggle
1201 */
1202 private static void ToggleFontStyle(Item toToggle) {
1203 if (toToggle == null) {
1204 FrameGraphics.DisplayMessage("There is no Item selected to toggle");
1205 return;
1206 }
1207
1208 // check permissions
1209 if (toToggle.Permission < Item.PERMISSION_FULL) {
1210 FrameGraphics
1211 .DisplayMessage("Insufficient permission to toggle that item's annotation");
1212 return;
1213 }
1214
1215 if (toToggle instanceof Text) {
1216 Text text = (Text) toToggle;
1217 text.toggleFontStyle();
1218
1219 text.getParent().setChanged(true);
1220 FrameGraphics.Repaint();
1221 }
1222 }
1223
1224 /**
1225 * Toggles the face style of a text item
1226 *
1227 * @param toToggle
1228 * The Item to toggle
1229 */
1230 private static void ToggleFontFamily(Item toToggle) {
1231 if (toToggle == null) {
1232 FrameGraphics.DisplayMessage("There is no Item selected to toggle");
1233 return;
1234 }
1235
1236 // check permissions
1237 if (toToggle.Permission < Item.PERMISSION_FULL) {
1238 FrameGraphics
1239 .DisplayMessage("Insufficient permission to toggle that item's annotation");
1240 return;
1241 }
1242
1243 if (toToggle instanceof Text) {
1244 Text text = (Text) toToggle;
1245 text.toggleFontFamily();
1246
1247 text.getParent().setChanged(true);
1248 FrameGraphics.Repaint();
1249 }
1250 }
1251
1252 /**
1253 * If the given Item is null, then a new Text item is created with the
1254 * current date If the given Item is not null, then the current date is
1255 * prepended to the Item's text
1256 *
1257 * @param toAdd
1258 * The Item to prepend the date to, or null
1259 */
1260 private static void AddDate(Item toAdd) {
1261 String date1 = Logger.EasyDateFormat("ddMMMyyyy[HH:mm]");
1262 String date2 = Logger.EasyDateFormat("ddMMMyyyy");
1263 final String leftSeparator = " :";
1264 final String rightSeparator = ": ";
1265 String dateToAdd = date1 + rightSeparator;
1266 boolean prepend = false;
1267 boolean append = false;
1268
1269 // if the user is pointing at an item, add the date where ever the
1270 // cursor is pointing
1271 if (toAdd != null) {
1272 if (toAdd instanceof Text) {
1273 // permission check
1274 if (toAdd.Permission < Item.PERMISSION_FULL) {
1275 FrameGraphics
1276 .DisplayMessage("Insufficicent permission to add the date to that item");
1277 return;
1278 }
1279
1280 Text textItem = (Text) toAdd;
1281
1282 String text = textItem.getTextNoList();
1283
1284 // check if the default date has already been put on this item
1285 if (text.startsWith(date1 + rightSeparator)) {
1286 textItem.removeText(date1 + rightSeparator);
1287 dateToAdd = date2 + rightSeparator;
1288 prepend = true;
1289 } else if (text.startsWith(date2 + rightSeparator)) {
1290 textItem.removeText(date2 + rightSeparator);
1291 dateToAdd = leftSeparator + date2;
1292 append = true;
1293 } else if (text.endsWith(leftSeparator + date2)) {
1294 textItem.removeEndText(leftSeparator + date2);
1295 append = true;
1296 dateToAdd = leftSeparator + date1;
1297 } else if (text.endsWith(leftSeparator + date1)) {
1298 textItem.removeEndText(leftSeparator + date1);
1299 if (textItem.getLength() > 0) {
1300 dateToAdd = "";
1301 prepend = true;
1302 } else {
1303 // use the default date format
1304 prepend = true;
1305 }
1306 }
1307
1308 if (prepend) {
1309 // add the date to the text item
1310 textItem.prependText(dateToAdd);
1311 if (dateToAdd.length() == textItem.getLength())
1312 DisplayIO.setCursorPosition(textItem
1313 .getEndParagraphPosition());
1314 } else if (append) {
1315 textItem.appendText(dateToAdd);
1316 if (dateToAdd.length() == textItem.getLength())
1317 DisplayIO.setCursorPosition(textItem.getPosition());
1318 } else {
1319 for (int i = 0; i < date1.length(); i++) {
1320 processChar(date1.charAt(i));
1321 }
1322 }
1323
1324 textItem.getParent().setChanged(true);
1325 FrameGraphics.Repaint();
1326 } else {
1327 FrameGraphics
1328 .DisplayMessage("Only text items can have the date prepended to them");
1329 }
1330 // otherwise, create a new text item
1331 } else {
1332 Text newText = createText();
1333 newText.setText(dateToAdd);
1334 DisplayIO.getCurrentFrame().addItem(newText);
1335 DisplayIO.getCurrentFrame().setChanged(true);
1336 FrameGraphics.Repaint();
1337
1338 DisplayIO.setCursorPosition(newText.getEndParagraphPosition());
1339 }
1340
1341 }
1342
1343 /**
1344 * Creates a new Frameset with the name given by the Item
1345 *
1346 * @param name
1347 */
1348 private static void CreateFrameset(Item ip) {
1349 if (ip == null || !(ip instanceof Text)) {
1350 FrameGraphics
1351 .DisplayMessage("There is no selected item to use for the Frameset name");
1352 return;
1353 }
1354
1355 // check permissions
1356 if (ip.Permission < Item.PERMISSION_FULL) {
1357 FrameGraphics
1358 .DisplayMessage("Insufficient permission to create a frameset from this item");
1359 return;
1360 }
1361
1362 Text text = (Text) ip;
1363 try {
1364 // create the new frameset
1365 Frame linkTo = FrameIO.CreateNewFrameset(text.getFirstLine());
1366 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1367 text.setLink(linkTo.getFrameName());
1368 text.getParent().setChanged(true);
1369 FrameUtils.DisplayFrame(linkTo, true);
1370 linkTo.moveMouseToDefaultLocation();
1371 // this needs to be done if the user doesnt move the mouse before
1372 // doing Tdfc while the cursor is set to the text cursor
1373 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1374 } catch (Exception e) {
1375 FrameGraphics.ErrorMessage(e.getMessage());
1376 }
1377 }
1378
1379 /**
1380 * Toggles Audience mode on\off
1381 *
1382 * @param ignored
1383 * This Item is currently ignored
1384 */
1385 private static void ToggleAudience(Item ignored) {
1386 FrameGraphics.ToggleAudienceMode();
1387 }
1388
1389 private static void ToggleXRayMode(Item ignored) {
1390 FrameGraphics.ToggleXRayMode();
1391 }
1392
1393 /**
1394 * Runs the first Item on the first menu
1395 *
1396 * @param ignored
1397 * This Item is currently ignored
1398 */
1399 /*
1400 * private static void RunFirstMenuItem(Item ignored) {
1401 * DisplayIO.activateMenuItem(0, 0); }
1402 */
1403
1404 /**
1405 * Forces a re-parse and repaint of the current Frame.
1406 *
1407 * @param ignored
1408 * This Item is currently ignored
1409 */
1410 private static void Repaint(Item ignored) {
1411 FrameUtils.Parse(DisplayIO.getCurrentFrame());
1412 FrameGraphics.ForceRepaint();
1413 }
1414}
Note: See TracBrowser for help on using the repository browser.