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

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

Remove non alpha numeric chars from frameset names

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