source: trunk/src/org/expeditee/gui/MessageBay.java@ 1480

Last change on this file since 1480 was 1480, checked in by bnemhaus, 4 years ago

Window title now notifies you when you are in surrogate mode.
Window title also now notifies you when you are running demo mode.

File size: 18.5 KB
Line 
1/**
2 * MessageBay.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.gui;
20
21import java.util.LinkedList;
22import java.util.List;
23
24import org.expeditee.Util;
25import org.expeditee.actions.Misc;
26import org.expeditee.auth.AuthenticatorBrowser;
27import org.expeditee.core.Clip;
28import org.expeditee.core.Colour;
29import org.expeditee.core.Dimension;
30import org.expeditee.core.Font;
31import org.expeditee.core.Image;
32import org.expeditee.encryption.items.surrogates.Label;
33import org.expeditee.gio.EcosystemManager;
34import org.expeditee.gio.GraphicsManager;
35import org.expeditee.items.Item;
36import org.expeditee.items.Text;
37import org.expeditee.settings.UserSettings;
38
39/**
40 * The bay at the bottom of the expeditee browser which displays messages. TODO:
41 * Make it thread safe!
42 */
43public final class MessageBay {
44
45 /** The distance from the top of the message bay to the Message frame link. */
46 private static final int MESSAGE_LINK_Y_OFFSET = 100;
47
48 /** TODO: Comment. cts16 */
49 private static final int MESSAGE_LINK_X = 50;
50
51 /** TODO: Comment. cts16 */
52 public static final Colour ERROR_COLOR = Colour.RED;
53
54 /** TODO: Comment. cts16 */
55 public static final String MESSAGES_FRAMESET_NAME = "Messages";
56
57 /** The list of messages shown in the message bay. */
58 private static List<Item> _messages = new LinkedList<Item>();
59
60 /**
61 * Messages which were delayed because they couldn't be shown at time of
62 * creation.
63 */
64 private static List<DelayedMessage> _delayedMessages = new LinkedList<DelayedMessage>();
65
66 /** TODO: Comment. cts16 */
67 private static Text _status = null;
68
69 private static Text _authorisedUser = null;
70
71 private static Text _surrogateMode = null;
72
73 /** Buffer image of the message window. */
74 private static Image _messageBuffer = null;
75
76 /** Creator for creating the message frames. */
77 private static FrameCreator _creator = null;
78
79 /** The user we are providing messages for **/
80 private static String _forUser = null;
81
82 /** Font used for the messages. */
83 private static Font _messageFont = new Font("Serif-Plain-16");
84
85 /** The number of messages currently shown (used for scrolling up). */
86 private static int _messageCount = 0;
87
88 /** If true, error messages are not shown to the user. */
89 private static boolean _suppressMessages = false;
90
91 /** The link to the message frameset. */
92 private static Item _messageLink = new Text(-2, "@" + MESSAGES_FRAMESET_NAME, Colour.BLACK, Colour.WHITE);
93
94 /** TODO: Comment. cts16 */
95 private static String _lastMessage = null;
96
97 /** TODO: Comment. cts16 */
98 private static boolean isLinkInitialized = false;
99
100 /** Static-only class. */
101 private MessageBay() {
102 }
103
104 /** Whether the message bay is ready to display messages. */
105 public static boolean isReady() {
106 return Browser.isInitComplete();
107 }
108
109 /** Syncs message bay size according to FrameGraphics max size. */
110 private static void updateSize() {
111 for (Item i : _messages) {
112 if (i != null) {
113 i.setOffset(0, -DisplayController.getMessageBayPaintArea().getMinY());
114 }
115 }
116
117 _messageLink.setOffset(0, -DisplayController.getMessageBayPaintArea().getMinY());
118
119 updateLink();
120 }
121
122 /** Whether the given item is an item in the message bay. */
123 public static boolean isMessageItem(Item i) {
124 return _messages.contains(i) || i == _messageLink || i == getStatus();
125 }
126
127 /** TODO: Comment. cts16 */
128 public synchronized static Item getMessageLink() {
129 return _messageLink;
130 }
131
132 /** TODO: Comment. cts16 */
133 public synchronized static List<Item> getMessages() {
134 return _messages;
135 }
136
137 /** Causes the entire message bay area to be invalidated. */
138 public synchronized static void invalidateFullBay() {
139 DisplayController.invalidateArea(DisplayController.getMessageBayPaintArea());
140 }
141
142 /** TODO: Comment. cts16 */
143 private static void updateLink() {
144 if (!isLinkInitialized && DisplayController.getFramePaintArea() != null
145 && DisplayController.getFramePaintAreaWidth() > 0) {
146 // set up 'Messages' link on the right hand side
147 _messageLink.setPosition(DisplayController.getMessageBayPaintArea().getWidth() - MESSAGE_LINK_Y_OFFSET,
148 MESSAGE_LINK_X);
149 _messageLink.setOffset(0, -DisplayController.getMessageBayPaintArea().getMinY());
150 isLinkInitialized = true;
151 } else {
152 _messageLink.setPosition(DisplayController.getMessageBayPaintArea().getWidth() - MESSAGE_LINK_Y_OFFSET,
153 MESSAGE_LINK_X);
154 }
155 }
156
157 /** TODO: Comment. cts16 */
158 public static Image getImage(Clip clip, Dimension size) {
159 // Can't get an image with an invalid size
160 if (size == null || size.width <= 0 || size.height <= 0) {
161 return null;
162 }
163
164 // Update the buffer
165 updateBuffer(Item.DEFAULT_BACKGROUND, clip, size);
166
167 // Return the image buffer
168 return _messageBuffer;
169 }
170
171 public static void clear() {
172 MessageBay._messageCount = 0;
173 MessageBay._messages.clear();
174 }
175
176 /** Updates the image buffer to reflect the current state of the message bay. */
177 private synchronized static void updateBuffer(Colour background, Clip clip, Dimension size) {
178 // If the buffer doesn't exist or is the wrong size, recreate it
179 if (_messageBuffer == null || !_messageBuffer.getSize().equals(size)) {
180 _messageBuffer = Image.createImage(size, true);
181 clip = null; // Need to recreate the entire image;
182 updateSize();
183 }
184
185 GraphicsManager g = EcosystemManager.getGraphicsManager();
186 g.pushDrawingSurface(_messageBuffer);
187
188 if (clip != null) {
189 g.pushClip(clip);
190 }
191 g.setAntialiasing(true);
192
193 g.clear(background);
194
195 g.setFont(_messageFont);
196
197 for (Item message : _messages) {
198 if (message != null) {
199 if (clip == null || clip.isNotClipped() || message.isInDrawingArea(clip.getBounds())) {
200 FrameGraphics.PaintItem(message);
201 }
202 }
203 }
204
205 if (getStatus() != null) {
206 FrameGraphics.PaintItem(getStatus());
207 }
208
209 if (AuthenticatorBrowser.isAuthenticated() && _authorisedUser != null) {
210 FrameGraphics.PaintItem(_authorisedUser);
211 }
212
213 if (Label.isInSurrogateMode() && _surrogateMode != null) {
214 FrameGraphics.PaintItem(_surrogateMode);
215 }
216
217 if (clip == null || clip.isNotClipped() || _messageLink.isInDrawingArea(clip.getBounds())) {
218 FrameGraphics.PaintItem(_messageLink);
219 }
220
221 g.popDrawingSurface();
222 }
223
224 /** TODO: Comment. cts16 */
225 private static Text displayMessage(String message, String link, List<String> actions, Colour color) {
226 return displayMessage(message, link, actions, color, true);
227 }
228
229 /** TODO: Comment. cts16 */
230 public synchronized static Text displayMessage(String message, String link, Colour color, boolean displayAlways,
231 String action) {
232 List<String> actions = new LinkedList<String>();
233 if (action != null) {
234 actions.add(action);
235 }
236 return displayMessage(message, link, actions, color, displayAlways);
237 }
238
239 public static void updateFramesetLocation() {
240 if (_forUser != UserSettings.UserName.get()) {
241 if (AuthenticatorBrowser.isAuthenticated()) {
242 _creator = new FrameCreator(MESSAGES_FRAMESET_NAME, FrameIO.MESSAGES_PATH, MESSAGES_FRAMESET_NAME, FrameCreator.ExistingFramesetOptions.OverrideExistingFrames,
243 false, AuthenticatorBrowser.PROFILEENCRYPTIONLABEL);
244 } else {
245 _creator = new FrameCreator(MESSAGES_FRAMESET_NAME, FrameIO.MESSAGES_PATH, MESSAGES_FRAMESET_NAME, FrameCreator.ExistingFramesetOptions.OverrideExistingFrames,
246 false, null);
247 }
248 _forUser = UserSettings.UserName.get();
249 }
250 }
251
252 /** TODO: Comment. cts16 */
253 private static Text newMessage(String message, String link, List<String> actions, Colour color) {
254 Text t = new Text(getMessagePrefix(true) + message);
255 t.setPosition(20, 15 + _messages.size() * 25);
256 t.setOffset(0, -DisplayController.getFramePaintAreaHeight());
257 t.setColor(color);
258 t.setLink(link);
259 t.setActions(actions);
260 t.setFont(_messageFont.clone());
261 _creator.addItem(t.copy(), true);
262 if (link == null) {
263 t.setLink(_creator.getCurrent());
264 }
265 return t;
266 }
267
268 /** TODO: Comment. cts16 */
269 private synchronized static Text displayMessage(String message, String link, List<String> actions, Colour color,
270 boolean displayAlways, boolean redraw) {
271 assert (message != null);
272
273 if (!isReady()) {
274 delayMessage(message, link, actions, color, displayAlways, redraw);
275 return null;
276 }
277
278 System.out.println(message);
279
280 // Invalidate whole area
281 invalidateFullBay();
282
283 if (_suppressMessages) {
284 return null;
285 }
286
287 if (!displayAlways && message.equals(_lastMessage)) {
288 Item lastMessage = _messages.get(_messages.size() - 1);
289 String text = lastMessage.getText();
290 if (text.endsWith("}")) {
291 int startOfRepeat = text.lastIndexOf("{");
292 String repeatString = text.substring(startOfRepeat);
293 repeatString = repeatString.substring(1, repeatString.length() - 1);
294 try {
295 int repeatCount = Integer.parseInt(repeatString) + 1;
296 text = text.substring(0, startOfRepeat).trim() + " {" + repeatCount + "}";
297 } catch (NumberFormatException e) {
298 e.printStackTrace();
299 }
300 } else {
301 text = text.trim() + " {2}";
302 }
303 lastMessage.setText(text);
304 DisplayController.DisableMailMode();
305 return null;
306 }
307
308 _lastMessage = message;
309
310 if (_creator == null) {
311 if (AuthenticatorBrowser.isAuthenticated()) {
312 _creator = new FrameCreator(MESSAGES_FRAMESET_NAME, FrameIO.MESSAGES_PATH, MESSAGES_FRAMESET_NAME, FrameCreator.ExistingFramesetOptions.OverrideExistingFrames,
313 false, AuthenticatorBrowser.PROFILEENCRYPTIONLABEL);
314 } else {
315 _creator = new FrameCreator(MESSAGES_FRAMESET_NAME, FrameIO.MESSAGES_PATH, MESSAGES_FRAMESET_NAME, FrameCreator.ExistingFramesetOptions.OverrideExistingFrames,
316 false, null);
317 }
318 _forUser = UserSettings.UserName.get();
319 }
320
321 // set up 'Messages' link on the right hand side
322 updateLink();
323
324 if (_messages.size() >= 3) {
325 _messages.remove(0);
326 for (Item i : _messages) {
327 i.setY(i.getY() - 25);
328 }
329 }
330
331 Text t = newMessage(message, link, actions, color);
332
333 _messages.add(t);
334
335 // update the link to the latest message frame
336 _messageLink.setLink(_creator.getCurrent());
337
338 if (redraw) {
339 DisplayController.DisableMailMode();
340 DisplayController.requestRefresh(true);
341 }
342
343 return t;
344 }
345
346 /** TODO: Comment. cts16 */
347 public synchronized static Text displayMessage(String message, String link, List<String> actions, Colour color,
348 boolean displayAlways) {
349 return displayMessage(message, link, actions, color, displayAlways, true);
350 }
351
352 /** TODO: Comment. cts16 */
353 public synchronized static void overwriteMessage(String message) {
354 overwriteMessage(message, null);
355 }
356
357 /** TODO: Comment. cts16 */
358 public synchronized static void overwriteMessage(String message, Colour color) {
359 _messages.remove(_messages.size() - 1);
360 Text t = newMessage(message, null, null, color);
361 _messages.add(t);
362 DisplayController.requestRefresh(true);
363 }
364
365 /** TODO: Comment. cts16 */
366 private static String getMessagePrefix(int counter) {
367 return "@" + counter + ": ";
368 }
369
370 /** TODO: Comment. cts16 */
371 private static String getMessagePrefix(boolean incrementCounter) {
372 if (incrementCounter) {
373 _messageCount++;
374 }
375
376 return getMessagePrefix(_messageCount);
377 }
378
379 /**
380 * Checks if the error message ends with a frame name after the
381 * frameNameSeparator symbol
382 *
383 * @param message
384 * the message to be displayed
385 */
386 public synchronized static Text linkedErrorMessage(String message) {
387 if (_suppressMessages) {
388 return null;
389 }
390 Misc.beep();
391 String[] tokens = message.split(Text.FRAME_NAME_SEPARATOR);
392 String link = null;
393 if (tokens.length > 1) {
394 link = tokens[tokens.length - 1];
395 }
396 return displayMessage(message, link, null, ERROR_COLOR);
397 }
398
399 /** TODO: Comment. cts16 */
400 public synchronized static Text errorMessage(String message) {
401 if (_suppressMessages) {
402 return null;
403 }
404 Misc.beep();
405 return displayMessage(message, null, null, ERROR_COLOR, false);
406 }
407
408 /**
409 * Displays the given message in the message area of the Frame, any previous
410 * message is cleared from the screen.
411 *
412 * @param message
413 * The message to display to the user in the message area
414 */
415 public synchronized static Text displayMessage(String message) {
416 return displayMessageAlways(message);
417 }
418
419 /** TODO: Comment. cts16 */
420 public synchronized static Text displayMessageOnce(String message) {
421 return displayMessage(message, null, null, Colour.BLACK, false);
422 }
423
424 /** TODO: Comment. cts16 */
425 public synchronized static Text displayMessage(String message, Colour textColor) {
426 return displayMessage(message, null, null, textColor);
427 }
428
429 /** TODO: Comment. cts16 */
430 public synchronized static Text displayMessage(Text message) {
431 Text t = null;
432 String link = message.getLink();
433 List<String> action = message.getAction();
434 Colour color = message.getColor();
435 for (String s : message.getTextList()) {
436 t = displayMessage(s, link, action, color);
437 }
438 return t;
439 }
440
441 /** TODO: Comment. cts16 */
442 public synchronized static Text displayMessageAlways(String message) {
443 return displayMessage(message, null, null, Colour.BLACK);
444 // Misc.Beep();
445 }
446
447 /** TODO: Comment. cts16 */
448 public synchronized static Text warningMessage(String message) {
449 return displayMessage(message, null, null, Colour.MAGENTA);
450 }
451
452 /** TODO: Comment. cts16 */
453 public synchronized static List<Text> warningMessages(List<String> messages) {
454 if (messages == null) {
455 return null;
456 }
457 List<Text> ret = new LinkedList<Text>();
458 for (String message : messages) {
459 ret.add(warningMessage(message));
460 }
461 return ret;
462 }
463
464 /** TODO: Comment. cts16 */
465 public synchronized static void suppressMessages(boolean val) {
466 _suppressMessages = val;
467 }
468
469 /** TODO: Comment. cts16 */
470 public synchronized static void setStatus(String status) {
471 if (_status == null) {
472 _status = new Text(status);
473 _status.setPosition(0, 85);
474 _status.setOffset(0, -DisplayController.getMessageBayPaintArea().getMinY());
475 _status.setLink(null); // maybe link to a help frame?
476 _status.setFont(new Font(Text.MONOSPACED_FONT));
477 } else {
478 _status.setText(status);
479 }
480
481 _authorisedUser = new Text("Username: " + System.getProperty("user.name"));
482 _authorisedUser.setFont(new Font(Text.MONOSPACED_FONT));
483 _authorisedUser.setY(95);
484 _authorisedUser.setAnchorRight(1);
485
486 if (Label.isInSurrogateMode()) {
487 String surrogateModeString = Label.surrogateModeString();
488
489 _surrogateMode = new Text(surrogateModeString);
490 _surrogateMode.setFont(new Font(Text.MONOSPACED_FONT));
491 _surrogateMode.setY(75);
492 _surrogateMode.setAnchorRight(1);
493 } else {
494 if (_surrogateMode != null) {
495 _surrogateMode.setVisible(false);
496 }
497 }
498
499 // invalidateFullBay();
500 DisplayController.requestRefresh(true);
501 }
502
503 /** TODO: Comment. cts16 */
504 public static final class Progress {
505 /** The colour progress bars should be displayed in. */
506 private static final Colour BAR_COLOUR = Colour.GREEN.darker();
507
508 /**
509 * The character used to assemble the uncompleted portions of the progress bar.
510 */
511 private static final char UNCOMPLETED_CHARACTER = '\u2591'; // ░
512 /**
513 * The character used to assemble the completed portions of the progress bar.
514 */
515 private static final char COMPLETED_CHARACTER = '\u2592'; // ▒
516
517 /** What the progress bar should look like when at 100% completion. */
518 private static final String COMPLETED_BAR = Util.nCopiesOf(20, COMPLETED_CHARACTER);
519 /** What the progress bar should look like when at 0% completion. */
520 private static final String UNCOMPLETED_BAR = Util.nCopiesOf(20, UNCOMPLETED_CHARACTER);
521
522 private String _message;
523 private Text _text;
524
525 protected Progress(String text) {
526 this._text = displayMessage(text, null, null, BAR_COLOUR, true, false);
527 this._message = this._text.getText();
528 this._text.setText(this._message + " [" + UNCOMPLETED_BAR + "] 0%");
529 DisplayController.requestRefresh(true);
530 }
531
532 public void UpdateMessage(final String text, final int newProgress) throws Exception {
533 this._message = text;
534 set(newProgress);
535 }
536
537 public String GetMessage() {
538 return _message;
539 }
540
541 /**
542 *
543 * @param progress
544 * progress value from 0 to 100
545 * @return true if the progress was updated, false if the progress was off the
546 * screen
547 * @throws Exception
548 * if progress out of bounds
549 */
550 public boolean set(int progress) throws Exception {
551 if (progress < 0 || progress > 100) {
552 throw new Exception("Progress value out of bounds");
553 }
554 int p = progress / 5;
555 if (isMessageItem(this._text)) {
556 this._text.setText(this._message + " [" + COMPLETED_BAR.substring(0, p) + UNCOMPLETED_BAR.substring(p)
557 + "] " + progress + "%");
558 DisplayController.requestRefresh(true);
559 return true;
560 }
561 return false;
562 }
563 }
564
565 /** TODO: Comment. cts16 */
566 public synchronized static Progress displayProgress(String message) {
567 return new Progress(message);
568 }
569
570 /** Remembers the arguments to a displayMessage call for later use. */
571 private static class DelayedMessage {
572
573 private String _message;
574 private String _link;
575 private List<String> _actions;
576 private Colour _colour;
577 private boolean _displayAlways;
578 private boolean _redraw;
579
580 public DelayedMessage(String message, String link, List<String> actions, Colour color, boolean displayAlways,
581 boolean redraw) {
582 _message = message;
583 _link = link;
584 if (actions == null) {
585 _actions = null;
586 } else {
587 _actions = new LinkedList<String>();
588 _actions.addAll(actions);
589 }
590 _colour = color == null ? null : color.clone();
591 _displayAlways = displayAlways;
592 _redraw = redraw;
593 }
594
595 public void display() {
596 displayMessage(_message, _link, _actions, _colour, _displayAlways, _redraw);
597 }
598
599 }
600
601 private static void delayMessage(String message, String link, List<String> actions, Colour color,
602 boolean displayAlways, boolean redraw) {
603 _delayedMessages.add(new DelayedMessage(message, link, actions, color, displayAlways, redraw));
604 }
605
606 public static void showDelayedMessages(final boolean requestRefresh) {
607 if (isReady()) {
608 for (DelayedMessage message : _delayedMessages) {
609 message.display();
610 }
611 _delayedMessages.clear();
612 invalidateFullBay();
613 if(requestRefresh) {
614 DisplayController.requestRefresh(true);
615 }
616 }
617 }
618
619 public static Text getStatus() {
620 return _status;
621 }
622}
Note: See TracBrowser for help on using the repository browser.