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
RevLine 
[919]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
[122]19package org.expeditee.gui;
20
21import java.util.LinkedList;
22import java.util.List;
23
[1102]24import org.expeditee.Util;
[122]25import org.expeditee.actions.Misc;
[1298]26import org.expeditee.auth.AuthenticatorBrowser;
[1102]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;
[1464]32import org.expeditee.encryption.items.surrogates.Label;
[1102]33import org.expeditee.gio.EcosystemManager;
34import org.expeditee.gio.GraphicsManager;
[122]35import org.expeditee.items.Item;
36import org.expeditee.items.Text;
[1291]37import org.expeditee.settings.UserSettings;
[122]38
39/**
[1183]40 * The bay at the bottom of the expeditee browser which displays messages. TODO:
41 * Make it thread safe!
[122]42 */
43public final class MessageBay {
[130]44
[1102]45 /** The distance from the top of the message bay to the Message frame link. */
[122]46 private static final int MESSAGE_LINK_Y_OFFSET = 100;
47
[1102]48 /** TODO: Comment. cts16 */
[122]49 private static final int MESSAGE_LINK_X = 50;
[130]50
[1102]51 /** TODO: Comment. cts16 */
52 public static final Colour ERROR_COLOR = Colour.RED;
[130]53
[1102]54 /** TODO: Comment. cts16 */
[122]55 public static final String MESSAGES_FRAMESET_NAME = "Messages";
56
[1102]57 /** The list of messages shown in the message bay. */
[689]58 private static List<Item> _messages = new LinkedList<Item>();
[1183]59
60 /**
61 * Messages which were delayed because they couldn't be shown at time of
62 * creation.
63 */
[1102]64 private static List<DelayedMessage> _delayedMessages = new LinkedList<DelayedMessage>();
65
66 /** TODO: Comment. cts16 */
[673]67 private static Text _status = null;
[1298]68
69 private static Text _authorisedUser = null;
[1464]70
71 private static Text _surrogateMode = null;
[122]72
[1102]73 /** Buffer image of the message window. */
74 private static Image _messageBuffer = null;
[130]75
[1102]76 /** Creator for creating the message frames. */
[122]77 private static FrameCreator _creator = null;
[1291]78
79 /** The user we are providing messages for **/
80 private static String _forUser = null;
[122]81
[1102]82 /** Font used for the messages. */
83 private static Font _messageFont = new Font("Serif-Plain-16");
[122]84
[1102]85 /** The number of messages currently shown (used for scrolling up). */
[122]86 private static int _messageCount = 0;
[130]87
[1102]88 /** If true, error messages are not shown to the user. */
[689]89 private static boolean _suppressMessages = false;
[122]90
[1102]91 /** The link to the message frameset. */
92 private static Item _messageLink = new Text(-2, "@" + MESSAGES_FRAMESET_NAME, Colour.BLACK, Colour.WHITE);
[122]93
[1102]94 /** TODO: Comment. cts16 */
[122]95 private static String _lastMessage = null;
96
[1102]97 /** TODO: Comment. cts16 */
98 private static boolean isLinkInitialized = false;
99
100 /** Static-only class. */
[1183]101 private MessageBay() {
[130]102 }
[1183]103
[1102]104 /** Whether the message bay is ready to display messages. */
[1183]105 public static boolean isReady() {
[1102]106 return Browser.isInitComplete();
107 }
[130]108
[1102]109 /** Syncs message bay size according to FrameGraphics max size. */
[1183]110 private static void updateSize() {
111 for (Item i : _messages) {
112 if (i != null) {
[1102]113 i.setOffset(0, -DisplayController.getMessageBayPaintArea().getMinY());
[122]114 }
115 }
116
[1102]117 _messageLink.setOffset(0, -DisplayController.getMessageBayPaintArea().getMinY());
118
[125]119 updateLink();
[122]120 }
[130]121
[1102]122 /** Whether the given item is an item in the message bay. */
[1183]123 public static boolean isMessageItem(Item i) {
[1221]124 return _messages.contains(i) || i == _messageLink || i == getStatus();
[122]125 }
[130]126
[1102]127 /** TODO: Comment. cts16 */
[1183]128 public synchronized static Item getMessageLink() {
[122]129 return _messageLink;
130 }
[130]131
[1102]132 /** TODO: Comment. cts16 */
[1183]133 public synchronized static List<Item> getMessages() {
[122]134 return _messages;
135 }
[130]136
[1102]137 /** Causes the entire message bay area to be invalidated. */
[1183]138 public synchronized static void invalidateFullBay() {
[1102]139 DisplayController.invalidateArea(DisplayController.getMessageBayPaintArea());
[125]140 }
[130]141
[1102]142 /** TODO: Comment. cts16 */
[1183]143 private static void updateLink() {
144 if (!isLinkInitialized && DisplayController.getFramePaintArea() != null
[1258]145 && DisplayController.getFramePaintAreaWidth() > 0) {
[125]146 // set up 'Messages' link on the right hand side
[1183]147 _messageLink.setPosition(DisplayController.getMessageBayPaintArea().getWidth() - MESSAGE_LINK_Y_OFFSET,
148 MESSAGE_LINK_X);
[1102]149 _messageLink.setOffset(0, -DisplayController.getMessageBayPaintArea().getMinY());
[125]150 isLinkInitialized = true;
151 } else {
[1183]152 _messageLink.setPosition(DisplayController.getMessageBayPaintArea().getWidth() - MESSAGE_LINK_Y_OFFSET,
153 MESSAGE_LINK_X);
[125]154 }
155 }
156
[1102]157 /** TODO: Comment. cts16 */
[1183]158 public static Image getImage(Clip clip, Dimension size) {
[1102]159 // Can't get an image with an invalid size
[1183]160 if (size == null || size.width <= 0 || size.height <= 0) {
161 return null;
162 }
[130]163
[1102]164 // Update the buffer
165 updateBuffer(Item.DEFAULT_BACKGROUND, clip, size);
[1183]166
[1102]167 // Return the image buffer
168 return _messageBuffer;
169 }
[1314]170
171 public static void clear() {
172 MessageBay._messageCount = 0;
173 MessageBay._messages.clear();
174 }
[130]175
[1102]176 /** Updates the image buffer to reflect the current state of the message bay. */
[1183]177 private synchronized static void updateBuffer(Colour background, Clip clip, Dimension size) {
[1102]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();
[122]183 }
184
[1102]185 GraphicsManager g = EcosystemManager.getGraphicsManager();
186 g.pushDrawingSurface(_messageBuffer);
[122]187
[1183]188 if (clip != null) {
189 g.pushClip(clip);
190 }
[1102]191 g.setAntialiasing(true);
[1183]192
[1102]193 g.clear(background);
[1183]194
[122]195 g.setFont(_messageFont);
[1183]196
197 for (Item message : _messages) {
[1102]198 if (message != null) {
199 if (clip == null || clip.isNotClipped() || message.isInDrawingArea(clip.getBounds())) {
200 FrameGraphics.PaintItem(message);
201 }
202 }
[122]203 }
[130]204
[1221]205 if (getStatus() != null) {
206 FrameGraphics.PaintItem(getStatus());
[1183]207 }
[1298]208
209 if (AuthenticatorBrowser.isAuthenticated() && _authorisedUser != null) {
210 FrameGraphics.PaintItem(_authorisedUser);
211 }
[1464]212
213 if (Label.isInSurrogateMode() && _surrogateMode != null) {
214 FrameGraphics.PaintItem(_surrogateMode);
215 }
[1183]216
[1102]217 if (clip == null || clip.isNotClipped() || _messageLink.isInDrawingArea(clip.getBounds())) {
218 FrameGraphics.PaintItem(_messageLink);
[125]219 }
[1183]220
[1102]221 g.popDrawingSurface();
[122]222 }
[130]223
[1102]224 /** TODO: Comment. cts16 */
[1183]225 private static Text displayMessage(String message, String link, List<String> actions, Colour color) {
[284]226 return displayMessage(message, link, actions, color, true);
[122]227 }
[336]228
[1102]229 /** TODO: Comment. cts16 */
[1183]230 public synchronized static Text displayMessage(String message, String link, Colour color, boolean displayAlways,
[1434]231 String action) {
[284]232 List<String> actions = new LinkedList<String>();
[1183]233 if (action != null) {
234 actions.add(action);
235 }
[284]236 return displayMessage(message, link, actions, color, displayAlways);
237 }
[1303]238
239 public static void updateFramesetLocation() {
240 if (_forUser != UserSettings.UserName.get()) {
[1304]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 }
[1303]248 _forUser = UserSettings.UserName.get();
249 }
250 }
[1102]251
252 /** TODO: Comment. cts16 */
[1183]253 private static Text newMessage(String message, String link, List<String> actions, Colour color) {
[689]254 Text t = new Text(getMessagePrefix(true) + message);
255 t.setPosition(20, 15 + _messages.size() * 25);
[1258]256 t.setOffset(0, -DisplayController.getFramePaintAreaHeight());
[689]257 t.setColor(color);
258 t.setLink(link);
259 t.setActions(actions);
[1125]260 t.setFont(_messageFont.clone());
[689]261 _creator.addItem(t.copy(), true);
[1183]262 if (link == null) {
263 t.setLink(_creator.getCurrent());
264 }
[689]265 return t;
266 }
[1303]267
[1102]268 /** TODO: Comment. cts16 */
[1183]269 private synchronized static Text displayMessage(String message, String link, List<String> actions, Colour color,
270 boolean displayAlways, boolean redraw) {
[1102]271 assert (message != null);
[1183]272
[1102]273 if (!isReady()) {
274 delayMessage(message, link, actions, color, displayAlways, redraw);
275 return null;
276 }
[1183]277
[122]278 System.out.println(message);
[130]279
[125]280 // Invalidate whole area
281 invalidateFullBay();
[130]282
[1183]283 if (_suppressMessages) {
284 return null;
285 }
[122]286
287 if (!displayAlways && message.equals(_lastMessage)) {
[1415]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);
[1221]304 DisplayController.DisableMailMode();
[284]305 return null;
[122]306 }
[1183]307
[122]308 _lastMessage = message;
309
[1303]310 if (_creator == null) {
[1304]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 }
[1291]318 _forUser = UserSettings.UserName.get();
[122]319 }
[130]320
[125]321 // set up 'Messages' link on the right hand side
322 updateLink();
[122]323
[1183]324 if (_messages.size() >= 3) {
[689]325 _messages.remove(0);
[1183]326 for (Item i : _messages) {
[689]327 i.setY(i.getY() - 25);
[122]328 }
329 }
[1183]330
[689]331 Text t = newMessage(message, link, actions, color);
[1183]332
[689]333 _messages.add(t);
[1183]334
[122]335 // update the link to the latest message frame
336 _messageLink.setLink(_creator.getCurrent());
[130]337
[1183]338 if (redraw) {
[1221]339 DisplayController.DisableMailMode();
[1102]340 DisplayController.requestRefresh(true);
[649]341 }
[336]342
[689]343 return t;
[122]344 }
[130]345
[1102]346 /** TODO: Comment. cts16 */
[1183]347 public synchronized static Text displayMessage(String message, String link, List<String> actions, Colour color,
348 boolean displayAlways) {
[689]349 return displayMessage(message, link, actions, color, displayAlways, true);
350 }
351
[1102]352 /** TODO: Comment. cts16 */
[1183]353 public synchronized static void overwriteMessage(String message) {
[247]354 overwriteMessage(message, null);
355 }
[336]356
[1102]357 /** TODO: Comment. cts16 */
[1183]358 public synchronized static void overwriteMessage(String message, Colour color) {
[806]359 _messages.remove(_messages.size() - 1);
[689]360 Text t = newMessage(message, null, null, color);
361 _messages.add(t);
[1102]362 DisplayController.requestRefresh(true);
[122]363 }
[1102]364
365 /** TODO: Comment. cts16 */
[1183]366 private static String getMessagePrefix(int counter) {
[689]367 return "@" + counter + ": ";
368 }
[122]369
[1102]370 /** TODO: Comment. cts16 */
[1183]371 private static String getMessagePrefix(boolean incrementCounter) {
372 if (incrementCounter) {
373 _messageCount++;
374 }
375
[689]376 return getMessagePrefix(_messageCount);
[122]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 */
[1183]386 public synchronized static Text linkedErrorMessage(String message) {
387 if (_suppressMessages) {
388 return null;
389 }
[181]390 Misc.beep();
[122]391 String[] tokens = message.split(Text.FRAME_NAME_SEPARATOR);
392 String link = null;
[1183]393 if (tokens.length > 1) {
394 link = tokens[tokens.length - 1];
395 }
[284]396 return displayMessage(message, link, null, ERROR_COLOR);
[122]397 }
398
[1102]399 /** TODO: Comment. cts16 */
[1183]400 public synchronized static Text errorMessage(String message) {
401 if (_suppressMessages) {
402 return null;
403 }
[181]404 Misc.beep();
[284]405 return displayMessage(message, null, null, ERROR_COLOR, false);
[122]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 */
[1183]415 public synchronized static Text displayMessage(String message) {
[284]416 return displayMessageAlways(message);
[122]417 }
418
[1102]419 /** TODO: Comment. cts16 */
[1183]420 public synchronized static Text displayMessageOnce(String message) {
[1102]421 return displayMessage(message, null, null, Colour.BLACK, false);
[122]422 }
423
[1102]424 /** TODO: Comment. cts16 */
[1183]425 public synchronized static Text displayMessage(String message, Colour textColor) {
[284]426 return displayMessage(message, null, null, textColor);
[122]427 }
428
[1102]429 /** TODO: Comment. cts16 */
[1183]430 public synchronized static Text displayMessage(Text message) {
[336]431 Text t = null;
432 String link = message.getLink();
433 List<String> action = message.getAction();
[1102]434 Colour color = message.getColor();
[336]435 for (String s : message.getTextList()) {
436 t = displayMessage(s, link, action, color);
437 }
438 return t;
[122]439 }
440
[1102]441 /** TODO: Comment. cts16 */
[1183]442 public synchronized static Text displayMessageAlways(String message) {
[1102]443 return displayMessage(message, null, null, Colour.BLACK);
[122]444 // Misc.Beep();
445 }
446
[1102]447 /** TODO: Comment. cts16 */
[1183]448 public synchronized static Text warningMessage(String message) {
[1102]449 return displayMessage(message, null, null, Colour.MAGENTA);
[122]450 }
[130]451
[1102]452 /** TODO: Comment. cts16 */
[1183]453 public synchronized static List<Text> warningMessages(List<String> messages) {
454 if (messages == null) {
455 return null;
456 }
[1102]457 List<Text> ret = new LinkedList<Text>();
[1183]458 for (String message : messages) {
459 ret.add(warningMessage(message));
460 }
[1102]461 return ret;
462 }
463
464 /** TODO: Comment. cts16 */
[1183]465 public synchronized static void suppressMessages(boolean val) {
[689]466 _suppressMessages = val;
[122]467 }
[1102]468
469 /** TODO: Comment. cts16 */
[1183]470 public synchronized static void setStatus(String status) {
[673]471 if (_status == null) {
472 _status = new Text(status);
[678]473 _status.setPosition(0, 85);
[1102]474 _status.setOffset(0, -DisplayController.getMessageBayPaintArea().getMinY());
[673]475 _status.setLink(null); // maybe link to a help frame?
[1102]476 _status.setFont(new Font(Text.MONOSPACED_FONT));
[673]477 } else {
478 _status.setText(status);
479 }
[1298]480
[1464]481 _authorisedUser = new Text("Username: " + System.getProperty("user.name"));
[1298]482 _authorisedUser.setFont(new Font(Text.MONOSPACED_FONT));
[1300]483 _authorisedUser.setY(95);
484 _authorisedUser.setAnchorRight(1);
[1464]485
486 if (Label.isInSurrogateMode()) {
[1480]487 String surrogateModeString = Label.surrogateModeString();
488
489 _surrogateMode = new Text(surrogateModeString);
[1464]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 }
[1102]498
[1183]499 // invalidateFullBay();
[1102]500 DisplayController.requestRefresh(true);
[673]501 }
[1102]502
503 /** TODO: Comment. cts16 */
[1183]504 public static final class Progress {
[1102]505 /** The colour progress bars should be displayed in. */
506 private static final Colour BAR_COLOUR = Colour.GREEN.darker();
[1183]507
508 /**
509 * The character used to assemble the uncompleted portions of the progress bar.
510 */
[1102]511 private static final char UNCOMPLETED_CHARACTER = '\u2591'; // ░
[1183]512 /**
513 * The character used to assemble the completed portions of the progress bar.
514 */
[1102]515 private static final char COMPLETED_CHARACTER = '\u2592'; // ▒
[1183]516
[1102]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);
[1183]521
[1102]522 private String _message;
523 private Text _text;
[1183]524
525 protected Progress(String text) {
[1102]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);
[689]530 }
[1183]531
532 public void UpdateMessage(final String text, final int newProgress) throws Exception {
[1102]533 this._message = text;
[929]534 set(newProgress);
535 }
[1183]536
537 public String GetMessage() {
[1102]538 return _message;
[929]539 }
[1183]540
[689]541 /**
542 *
[1183]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
[689]549 */
[1183]550 public boolean set(int progress) throws Exception {
551 if (progress < 0 || progress > 100) {
552 throw new Exception("Progress value out of bounds");
553 }
[689]554 int p = progress / 5;
[1183]555 if (isMessageItem(this._text)) {
556 this._text.setText(this._message + " [" + COMPLETED_BAR.substring(0, p) + UNCOMPLETED_BAR.substring(p)
557 + "] " + progress + "%");
[1102]558 DisplayController.requestRefresh(true);
[689]559 return true;
560 }
[1183]561 return false;
562 }
[689]563 }
[1102]564
565 /** TODO: Comment. cts16 */
[1183]566 public synchronized static Progress displayProgress(String message) {
[689]567 return new Progress(message);
568 }
[1183]569
570 /** Remembers the arguments to a displayMessage call for later use. */
[1102]571 private static class DelayedMessage {
[1183]572
[1102]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;
[1183]579
580 public DelayedMessage(String message, String link, List<String> actions, Colour color, boolean displayAlways,
581 boolean redraw) {
[1102]582 _message = message;
583 _link = link;
584 if (actions == null) {
585 _actions = null;
586 } else {
[1183]587 _actions = new LinkedList<String>();
[1102]588 _actions.addAll(actions);
589 }
590 _colour = color == null ? null : color.clone();
591 _displayAlways = displayAlways;
592 _redraw = redraw;
593 }
[1183]594
595 public void display() {
[1102]596 displayMessage(_message, _link, _actions, _colour, _displayAlways, _redraw);
597 }
[1183]598
[1102]599 }
[1183]600
601 private static void delayMessage(String message, String link, List<String> actions, Colour color,
602 boolean displayAlways, boolean redraw) {
[1102]603 _delayedMessages.add(new DelayedMessage(message, link, actions, color, displayAlways, redraw));
604 }
[1183]605
606 public static void showDelayedMessages(final boolean requestRefresh) {
[1102]607 if (isReady()) {
[1183]608 for (DelayedMessage message : _delayedMessages) {
609 message.display();
610 }
[1102]611 _delayedMessages.clear();
612 invalidateFullBay();
[1183]613 if(requestRefresh) {
614 DisplayController.requestRefresh(true);
615 }
[1102]616 }
617 }
[130]618
[1221]619 public static Text getStatus() {
620 return _status;
621 }
[122]622}
Note: See TracBrowser for help on using the repository browser.