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

Last change on this file was 1544, checked in by bnemhaus, 3 years ago

With the fixes to antialiasing (details in coming commits), the MessageBay and MailBay no longer turn antialiasing unconditionally on.

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