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

Last change on this file since 929 was 929, checked in by bln4, 10 years ago
File size: 14.1 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.awt.Color;
22import java.awt.Font;
23import java.awt.Graphics;
24import java.awt.Graphics2D;
25import java.awt.GraphicsEnvironment;
26import java.awt.Rectangle;
27import java.awt.RenderingHints;
28import java.awt.geom.Area;
29import java.awt.image.VolatileImage;
30import java.util.LinkedList;
31import java.util.List;
32
33import org.expeditee.actions.Misc;
34import org.expeditee.items.Item;
35import org.expeditee.items.Text;
36
37/**
38 * The bay at the bottom of the expeditee browser which displays messages. TODO
39 * make it thread safe!
40 *
41 */
42public final class MessageBay {
43
44
45 public static final int MESSAGE_BUFFER_HEIGHT = 105;
46
47 private static final int MESSAGE_LINK_Y_OFFSET = 100;
48
49 private static final int MESSAGE_LINK_X = 50;
50
51 public static final Color ERROR_COLOR = Color.red;
52
53 public static final String MESSAGES_FRAMESET_NAME = "Messages";
54
55 // messages shown in the message window
56 private static List<Item> _messages = new LinkedList<Item>();
57 private static Text _status = null;
58
59 // buffer of the message window
60 private static VolatileImage _messageBuffer = null;
61
62 // creator for creating the message frames
63 private static FrameCreator _creator = null;
64
65 // font used for the messages
66 private static Font _messageFont = Font.decode("Serif-Plain-16");
67
68 // the number of messages currently shown (used for scrolling up)
69 private static int _messageCount = 0;
70
71 // if true, error messages are not shown to the user
72 private static boolean _suppressMessages = false;
73
74 // The link to the message frameset
75 private static Item _messageLink = new Text(-2, "@"
76 + MESSAGES_FRAMESET_NAME, Color.black, Color.white);
77
78 private static List<Rectangle> _dirtyAreas = new LinkedList<Rectangle>();
79
80 private static String _lastMessage = null;
81
82 private MessageBay() {
83 }
84
85 /**
86 * Syncs messsage bay size according to FrameGraphics max size.
87 *
88 */
89 static void updateSize() {
90
91 _messageBuffer = null;
92
93 for(Item i : _messages) {
94 if(i != null) {
95 i.setOffset(0, -FrameGraphics.getMaxFrameSize().height);
96 // i.setMaxWidth(FrameGraphics.getMaxFrameSize().width);
97 }
98 }
99
100 _messageLink.setOffset(0, -FrameGraphics.getMaxFrameSize().height);
101 // _messageLink.setMaxWidth(FrameGraphics.getMaxFrameSize().width);
102 // _messageLink.setPosition(FrameGraphics.getMaxFrameSize().width
103 // - MESSAGE_LINK_Y_OFFSET, MESSAGE_LINK_X);
104 updateLink();
105 initBuffer();
106 }
107
108 /**
109 * @param i
110 * @return True if i is an item in the message bay
111 */
112 public static boolean isMessageItem(Item i) {
113
114 return _messages.contains(i) || i == _messageLink;
115 }
116
117 public synchronized static void addDirtyArea(Rectangle r) {
118 _dirtyAreas.add(r);
119 }
120
121 static synchronized int getMessageBufferHeight() {
122 if (_messageBuffer != null)
123 return _messageBuffer.getHeight();
124 return 0;
125 }
126
127 public synchronized static Item getMessageLink() {
128 return _messageLink;
129 }
130
131 public synchronized static List<Item> getMessages() {
132 return _messages;
133 }
134
135 public synchronized static boolean isDirty() {
136 return !_dirtyAreas.isEmpty();
137 }
138
139 public synchronized static void invalidateFullBay() {
140 if (_messageBuffer != null) {
141 _dirtyAreas.clear();
142 addDirtyArea(new Rectangle(0,
143 FrameGraphics.getMaxFrameSize().height, _messageBuffer
144 .getWidth(), _messageBuffer.getHeight()));
145 }
146 }
147
148 private synchronized static boolean initBuffer() {
149 if (_messageBuffer == null) {
150 if (FrameGraphics.isAudienceMode()
151 || FrameGraphics.getMaxSize().width <= 0)
152 return false;
153
154 GraphicsEnvironment ge = GraphicsEnvironment
155 .getLocalGraphicsEnvironment();
156 _messageBuffer = ge.getDefaultScreenDevice()
157 .getDefaultConfiguration().createCompatibleVolatileImage(
158 FrameGraphics.getMaxSize().width,
159 MESSAGE_BUFFER_HEIGHT);
160 }
161 return true;
162 }
163
164 private static boolean isLinkInitialized = false;
165
166 private static void updateLink() {
167
168 if (!isLinkInitialized && FrameGraphics.getMaxSize().width > 0) {
169 // set up 'Messages' link on the right hand side
170 _messageLink.setPosition(FrameGraphics.getMaxSize().width
171 - MESSAGE_LINK_Y_OFFSET, MESSAGE_LINK_X);
172 _messageLink.setOffset(0, -FrameGraphics.getMaxFrameSize().height);
173 isLinkInitialized = true;
174
175 } else {
176 _messageLink.setPosition(FrameGraphics.getMaxSize().width
177 - MESSAGE_LINK_Y_OFFSET, MESSAGE_LINK_X);
178 }
179 }
180
181 /**
182 * Repaints the message bay. Updates the message bay buffer and draws to
183 * given graphics.
184 *
185 * @param useInvalidation
186 * Set to true of repinting dirty areas only. Otherwise false for
187 * full-repaint.
188 *
189 * @param g
190 *
191 * @param background
192 * The color of the message background
193 */
194 public static synchronized void refresh(boolean useInvalidation,
195 Graphics g, Color background) {
196
197 if(g == null)
198 return;
199
200 if (FrameGraphics.getMaxSize().width <= 0)
201 return;
202
203 Area clip = null;
204
205 if (useInvalidation) { // build clip
206
207 if (!_dirtyAreas.isEmpty()) {
208
209 for (Rectangle r : _dirtyAreas) {
210 r.y = (r.y < 0) ? 0 : r.y;
211 r.x = (r.x < 0) ? 0 : r.x;
212 if (clip == null)
213 clip = new Area(r);
214 else
215 clip.add(new Area(r));
216 }
217 } else
218 return; // nothing to render
219 }
220
221 _dirtyAreas.clear();
222
223 // Update the buffer
224 updateBuffer(background, clip);
225
226 // Now repaint to screen
227 if (!FrameGraphics.isAudienceMode()) {
228
229 // Translate clip to messagebox coords
230 // clip.transform(t) // TODO
231 // g.setClip(clip);
232
233 g.drawImage(_messageBuffer, 0, FrameGraphics.getMaxFrameSize().height, null);
234 }
235
236 }
237
238 private static void updateBuffer(Color background, Area clip) {
239 if (!initBuffer())
240 return;
241
242 Graphics2D g = _messageBuffer.createGraphics();
243
244 g.setClip(clip);
245
246 g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
247 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
248 g.setColor(background);
249 g.fillRect(0, 0, FrameGraphics.getMaxSize().width,
250 MESSAGE_BUFFER_HEIGHT);
251 g.setFont(_messageFont);
252 g.setColor(Color.BLACK);
253 g.drawLine(0, 0, FrameGraphics.getMaxSize().width, 0);
254
255 for (Item t : _messages) {
256 if (t == null)
257 continue;
258 if (clip == null || t.isInDrawingArea(clip))
259 FrameGraphics.PaintItem(g, t);
260 }
261 if(_status != null)
262 FrameGraphics.PaintItem(g, _status);
263
264 if (// _messageLink.getLink() != null
265 // &&
266 (clip == null || _messageLink.isInDrawingArea(clip))) {
267 FrameGraphics.PaintItem(g, _messageLink);
268 }
269 g.dispose();
270
271 }
272
273 private static Text displayMessage(String message, String link,
274 List<String> actions, Color color) {
275 return displayMessage(message, link, actions, color, true);
276 }
277
278 public synchronized static Text displayMessage(String message, String link, Color color,
279 boolean displayAlways, String action) {
280 List<String> actions = new LinkedList<String>();
281 if (action != null)
282 actions.add(action);
283 return displayMessage(message, link, actions, color, displayAlways);
284 }
285
286 private static Text newMessage(String message, String link, List<String> actions, Color color) {
287 Text t = new Text(getMessagePrefix(true) + message);
288 t.setPosition(20, 15 + _messages.size() * 25);
289 t.setOffset(0, -FrameGraphics.getMaxFrameSize().height);
290 t.setColor(color);
291 t.setLink(link);
292 t.setActions(actions);
293 t.setFont(_messageFont);
294 _creator.addItem(t.copy(), true);
295 if(link == null) t.setLink(_creator.getCurrent());
296 return t;
297 }
298
299 private synchronized static Text displayMessage(String message, String link,
300 List<String> actions, Color color, boolean displayAlways, boolean redraw) {
301 System.out.println(message);
302 assert (message != null);
303
304 // Invalidate whole area
305 invalidateFullBay();
306
307 if (_suppressMessages)
308 return null;
309
310 if (!displayAlways && message.equals(_lastMessage)) {
311 Misc.beep();
312 return null;
313 }
314 _lastMessage = message;
315
316 if (_creator == null) {
317 _creator = new FrameCreator(MESSAGES_FRAMESET_NAME,
318 FrameIO.MESSAGES_PATH, MESSAGES_FRAMESET_NAME, true, false);
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 Graphics g = FrameGraphics.createGraphics();
340 if (g != null) {
341 refresh(false, g, Item.DEFAULT_BACKGROUND);
342 }
343 }
344
345 return t;
346 }
347
348 public synchronized static Text displayMessage(String message, String link,
349 List<String> actions, Color color, boolean displayAlways) {
350 return displayMessage(message, link, actions, color, displayAlways, true);
351 }
352
353 public synchronized static void overwriteMessage(String message) {
354 overwriteMessage(message, null);
355 }
356
357 public synchronized static void overwriteMessage(String message, Color color) {
358 _messages.remove(_messages.size() - 1);
359 Text t = newMessage(message, null, null, color);
360 _messages.add(t);
361 Graphics g = FrameGraphics.createGraphics();
362 if (g != null) {
363 refresh(false, g, Item.DEFAULT_BACKGROUND);
364 }
365 }
366
367 private static String getMessagePrefix(int counter) {
368 return "@" + counter + ": ";
369 }
370
371 private static String getMessagePrefix(boolean incrementCounter) {
372 if (incrementCounter)
373 _messageCount++;
374 return getMessagePrefix(_messageCount);
375 }
376
377 /**
378 * Checks if the error message ends with a frame name after the
379 * frameNameSeparator symbol
380 *
381 * @param message
382 * the message to be displayed
383 */
384 public synchronized static Text linkedErrorMessage(String message) {
385 if (_suppressMessages)
386 return null;
387 Misc.beep();
388 String[] tokens = message.split(Text.FRAME_NAME_SEPARATOR);
389 String link = null;
390 if (tokens.length > 1)
391 link = tokens[tokens.length - 1];
392 return displayMessage(message, link, null, ERROR_COLOR);
393 }
394
395 public synchronized static Text errorMessage(String message) {
396 if (_suppressMessages)
397 return null;
398 Misc.beep();
399 return displayMessage(message, null, null, ERROR_COLOR, false);
400 }
401
402 /**
403 * Displays the given message in the message area of the Frame, any previous
404 * message is cleared from the screen.
405 *
406 * @param message
407 * The message to display to the user in the message area
408 */
409 public synchronized static Text displayMessage(String message) {
410 return displayMessageAlways(message);
411 }
412
413 public synchronized static Text displayMessageOnce(String message) {
414 return displayMessage(message, null, null, Color.BLACK, false);
415 }
416
417 public synchronized static Text displayMessage(String message, Color textColor) {
418 return displayMessage(message, null, null, textColor);
419 // Misc.Beep();
420 }
421
422 public synchronized static Text displayMessage(Text message) {
423 Text t = null;
424 String link = message.getLink();
425 List<String> action = message.getAction();
426 Color color = message.getColor();
427 for (String s : message.getTextList()) {
428 t = displayMessage(s, link, action, color);
429 }
430 return t;
431 // Misc.Beep();
432 }
433
434 public synchronized static Text displayMessageAlways(String message) {
435 return displayMessage(message, null, null, Color.BLACK);
436 // Misc.Beep();
437 }
438
439 public synchronized static Text warningMessage(String message) {
440 return displayMessage(message, null, null, Color.MAGENTA);
441 // Misc.Beep();
442 }
443
444 public synchronized static void suppressMessages(boolean val) {
445 _suppressMessages = val;
446 }
447
448 public synchronized static void setStatus(String status) {
449 if (_status == null) {
450 _status = new Text(status);
451 _status.setPosition(0, 85);
452 _status.setOffset(0, FrameGraphics.getMaxFrameSize().height);
453 _status.setLink(null); // maybe link to a help frame?
454 _status.setFont(Font.decode(Text.MONOSPACED_FONT));
455 } else {
456 _status.setText(status);
457 }
458 Graphics g = FrameGraphics.createGraphics();
459 if (g != null) {
460 refresh(false, g, Item.DEFAULT_BACKGROUND);
461 }
462 }
463
464 public static final class Progress {
465
466 private static final String filled = "\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592";
467 private static final String unfilled = "\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591";
468
469 String message;
470 Text text;
471
472 protected Progress(String text) {
473 this.text = displayMessage(text, null, null, Color.GREEN.darker(), true, false);
474 //this.text.setFont(Font.decode(Text.MONOSPACED_FONT + "-16"));
475 this.message = this.text.getText();
476 this.text.setText(this.message + " [" + unfilled + "] 0%");
477 refresh(false, FrameGraphics.createGraphics(), Item.DEFAULT_BACKGROUND);
478 }
479
480 public void UpdateMessage(final String text, final int newProgress) throws Exception {
481 this.message = text;
482 set(newProgress);
483 }
484
485 public String GetMessage() {
486 return message;
487 }
488
489 /**
490 *
491 * @param progress progress value from 0 to 100
492 * @return true if the progress was updated, false if the progress was off the screen
493 * @throws Exception if progress out of bounds
494 */
495 public boolean set(int progress) throws Exception {
496 if(progress < 0 || progress > 100) throw new Exception("Progress value out of bounds");
497 int p = progress / 5;
498 if(isMessageItem(this.text)) {
499 this.text.setText(this.message + " [" + filled.substring(0, p) + unfilled.substring(p) + "] " + progress + "%");
500 refresh(false, FrameGraphics.createGraphics(), Item.DEFAULT_BACKGROUND);
501 return true;
502 }
503 return false;
504 }
505 }
506
507 public synchronized static Progress displayProgress(String message) {
508 return new Progress(message);
509 }
510
511}
Note: See TracBrowser for help on using the repository browser.