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

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

Updates to the look of some of the more prominant expeditee pages. More to come.
Also first draft of Encryption Cockpit.

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