source: trunk/org/expeditee/gui/FrameKeyboardActions.java@ 4

Last change on this file since 4 was 4, checked in by davidb, 16 years ago

Starting source code to Expeditee

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