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

Last change on this file since 678 was 678, checked in by jts21, 10 years ago

Add help for click+drag commands, and make the status go onto 2 lines if necessary

File size: 12.4 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Font;
5import java.awt.Graphics;
6import java.awt.Graphics2D;
7import java.awt.GraphicsEnvironment;
8import java.awt.Rectangle;
9import java.awt.RenderingHints;
10import java.awt.geom.Area;
11import java.awt.image.VolatileImage;
12import java.util.LinkedList;
13import java.util.List;
14
15import org.expeditee.actions.Misc;
16import org.expeditee.items.Item;
17import org.expeditee.items.Text;
18
19/**
20 * The bay at the bottom of the expeditee browser which displays messages. TODO
21 * make it thread safe!
22 *
23 */
24public final class MessageBay {
25
26
27 public static final int MESSAGE_BUFFER_HEIGHT = 105;
28
29 private static final int MESSAGE_LINK_Y_OFFSET = 100;
30
31 private static final int MESSAGE_LINK_X = 50;
32
33 public static final Color ERROR_COLOR = Color.red;
34
35 public static final String MESSAGES_FRAMESET_NAME = "Messages";
36
37 // messages shown in the message window
38 private static Text[] _messages = new Text[3];
39 private static Text _status = null;
40
41 // buffer of the message window
42 private static VolatileImage _messageBuffer = null;
43
44 // creator for creating the message frames
45 private static FrameCreator _creator = null;
46
47 // font used for the messages
48 private static Font _messageFont = Font.decode("Serif-Plain-16");
49
50 // the number of messages currently shown (used for scrolling up)
51 private static int _messageCount = 0;
52
53 // if true, error messages are not shown to the user
54 private static boolean _supressMessages = false;
55
56 // The link to the message frameset
57 private static Item _messageLink = new Text(-2, "@"
58 + MESSAGES_FRAMESET_NAME, Color.black, Color.white);
59
60 private static List<Rectangle> _dirtyAreas = new LinkedList<Rectangle>();
61
62 private static String _lastMessage = null;
63
64 private MessageBay() {
65 }
66
67 /**
68 * Syncs messsage bay size according to FrameGraphics max size.
69 *
70 */
71 static void updateSize() {
72
73 _messageBuffer = null;
74
75 for (int i = 0; i < _messages.length; i++) {
76 if (_messages[i] != null) {
77 _messages[i].setOffset(0,
78 -FrameGraphics.getMaxFrameSize().height);
79 // _messages[i].setMaxWidth(FrameGraphics.getMaxFrameSize().width);
80 }
81 }
82
83 _messageLink.setOffset(0, -FrameGraphics.getMaxFrameSize().height);
84 // _messageLink.setMaxWidth(FrameGraphics.getMaxFrameSize().width);
85 // _messageLink.setPosition(FrameGraphics.getMaxFrameSize().width
86 // - MESSAGE_LINK_Y_OFFSET, MESSAGE_LINK_X);
87 updateLink();
88 initBuffer();
89 }
90
91 /**
92 * @param i
93 * @return True if i is an item in the message bay
94 */
95 public static boolean isMessageItem(Item i) {
96 if (_messages != null) {
97 for (Text txt : _messages) {
98 if (txt == i)
99 return true;
100 }
101 }
102
103 return i == _messageLink;
104 }
105
106 public synchronized static void addDirtyArea(Rectangle r) {
107 _dirtyAreas.add(r);
108 }
109
110 static synchronized int getMessageBufferHeight() {
111 if (_messageBuffer != null)
112 return _messageBuffer.getHeight();
113 return 0;
114 }
115
116 public synchronized static Item getMessageLink() {
117 return _messageLink;
118 }
119
120 public synchronized static Text[] getMessages() {
121 return _messages;
122 }
123
124 public synchronized static boolean isDirty() {
125 return !_dirtyAreas.isEmpty();
126 }
127
128 public synchronized static void invalidateFullBay() {
129 if (_messageBuffer != null) {
130 _dirtyAreas.clear();
131 addDirtyArea(new Rectangle(0,
132 FrameGraphics.getMaxFrameSize().height, _messageBuffer
133 .getWidth(), _messageBuffer.getHeight()));
134 }
135 }
136
137 private synchronized static boolean initBuffer() {
138 if (_messageBuffer == null) {
139 if (FrameGraphics.isAudienceMode()
140 || FrameGraphics.getMaxSize().width <= 0)
141 return false;
142
143 GraphicsEnvironment ge = GraphicsEnvironment
144 .getLocalGraphicsEnvironment();
145 _messageBuffer = ge.getDefaultScreenDevice()
146 .getDefaultConfiguration().createCompatibleVolatileImage(
147 FrameGraphics.getMaxSize().width,
148 MESSAGE_BUFFER_HEIGHT);
149 }
150 return true;
151 }
152
153 private static boolean isLinkInitialized = false;
154
155 private static void updateLink() {
156
157 if (!isLinkInitialized && FrameGraphics.getMaxSize().width > 0) {
158 // set up 'Messages' link on the right hand side
159 _messageLink.setPosition(FrameGraphics.getMaxSize().width
160 - MESSAGE_LINK_Y_OFFSET, MESSAGE_LINK_X);
161 _messageLink.setOffset(0, -FrameGraphics.getMaxFrameSize().height);
162 isLinkInitialized = true;
163
164 } else {
165 _messageLink.setPosition(FrameGraphics.getMaxSize().width
166 - MESSAGE_LINK_Y_OFFSET, MESSAGE_LINK_X);
167 }
168 }
169
170 /**
171 * Repaints the message bay. Updates the message bay buffer and draws to
172 * given graphics.
173 *
174 * @param useInvalidation
175 * Set to true of repinting dirty areas only. Otherwise false for
176 * full-repaint.
177 *
178 * @param g
179 *
180 * @param background
181 * The color of the message background
182 */
183 public static synchronized void refresh(boolean useInvalidation,
184 Graphics g, Color background) {
185
186 if (FrameGraphics.getMaxSize().width <= 0)
187 return;
188
189 Area clip = null;
190
191 if (useInvalidation) { // build clip
192
193 if (!_dirtyAreas.isEmpty()) {
194
195 for (Rectangle r : _dirtyAreas) {
196 r.y = (r.y < 0) ? 0 : r.y;
197 r.x = (r.x < 0) ? 0 : r.x;
198 if (clip == null)
199 clip = new Area(r);
200 else
201 clip.add(new Area(r));
202 }
203 } else
204 return; // nothing to render
205 }
206
207 _dirtyAreas.clear();
208
209 // Update the buffer
210 updateBuffer(background, clip);
211
212 // Now repaint to screen
213 if (!FrameGraphics.isAudienceMode()) {
214
215 // Translate clip to messagebox coords
216 // clip.transform(t) // TODO
217 // g.setClip(clip);
218
219 g.drawImage(_messageBuffer, 0,
220 FrameGraphics.getMaxFrameSize().height, null);
221 }
222
223 }
224
225 private static void updateBuffer(Color background, Area clip) {
226 if (!initBuffer())
227 return;
228
229 Graphics2D g = _messageBuffer.createGraphics();
230
231 g.setClip(clip);
232
233 g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
234 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
235 g.setColor(background);
236 g.fillRect(0, 0, FrameGraphics.getMaxSize().width,
237 MESSAGE_BUFFER_HEIGHT);
238 g.setFont(_messageFont);
239 g.setColor(Color.BLACK);
240 g.drawLine(0, 0, FrameGraphics.getMaxSize().width, 0);
241
242 for (Item t : _messages) {
243 if (t == null)
244 continue;
245 if (clip == null || t.isInDrawingArea(clip))
246 FrameGraphics.PaintItem(g, t);
247 }
248 if(_status != null)
249 FrameGraphics.PaintItem(g, _status);
250
251 if (// _messageLink.getLink() != null
252 // &&
253 (clip == null || _messageLink.isInDrawingArea(clip))) {
254 FrameGraphics.PaintItem(g, _messageLink);
255 }
256 g.dispose();
257
258 }
259
260 private static Text displayMessage(String message, String link,
261 List<String> actions, Color color) {
262 return displayMessage(message, link, actions, color, true);
263 }
264
265 public synchronized static Text displayMessage(String message, String link, Color color,
266 boolean displayAlways, String action) {
267 List<String> actions = new LinkedList<String>();
268 if (action != null)
269 actions.add(action);
270 return displayMessage(message, link, actions, color, displayAlways);
271 }
272
273 public synchronized static Text displayMessage(String message, String link,
274 List<String> actions, Color color, boolean displayAlways) {
275
276 System.out.println(message);
277 assert (message != null);
278
279 // Invalidate whole area
280 invalidateFullBay();
281
282 if (_supressMessages)
283 return null;
284
285 if (!displayAlways && message.equals(_lastMessage)) {
286 Misc.beep();
287 return null;
288 }
289 _lastMessage = message;
290
291 if (_creator == null) {
292 _creator = new FrameCreator(MESSAGES_FRAMESET_NAME,
293 FrameIO.MESSAGES_PATH, MESSAGES_FRAMESET_NAME, true, false);
294 }
295
296 // set up 'Messages' link on the right hand side
297 updateLink();
298
299 // if the message slots have not all been used yet
300 if (_messageCount <= _messages.length) {
301 int pos = 15;
302 // find the next empty slot, and create the new message
303 for (int i = 0; i < _messages.length; i++) {
304 if (_messages[i] == null) {
305 _messages[i] = new Text(getMessagePrefix(true) + message);
306 _messages[i].setPosition(20, pos);
307 _messages[i].setOffset(0,
308 -FrameGraphics.getMaxFrameSize().height);
309 // _messages[i].setMaxWidth(FrameGraphics.getMaxFrameSize().width);
310 _messages[i].setColor(color);
311 _messages[i].setLink(link);
312 _messages[i].setActions(actions);
313 _messages[i].setFont(_messageFont);
314 _creator.addItem(_messages[i].copy(), true);
315 _messageLink.setLink(_creator.getCurrent());
316
317 Graphics g = FrameGraphics.createGraphics();
318 if (g != null) {
319 refresh(false, g, Item.DEFAULT_BACKGROUND);
320 }
321 return _messages[i];
322 }
323
324 pos += 25;
325 }
326 }
327
328 // if we have not returned then all message slots are used
329 for (int i = 0; i < _messages.length - 1; i++) {
330 _messages[i].setText(_messages[i + 1].getFirstLine());
331 _messages[i].setColor(_messages[i + 1].getColor());
332 _messages[i].setLink(_messages[i + 1].getLink());
333 _messages[i].setActions(_messages[i + 1].getAction());
334 }
335
336 // show the new message
337 Text last = _messages[_messages.length - 1];
338 last.setColor(color);
339 // Set the text for the new message
340 last.setText(getMessagePrefix(true) + message);
341 last.setLink(link);
342 last.setActions(actions);
343
344 _creator.addItem(last.copy(), true);
345 // update the link to the latest message frame
346 _messageLink.setLink(_creator.getCurrent());
347
348 Graphics g = FrameGraphics.createGraphics();
349 if (g != null) {
350 refresh(false, g, Item.DEFAULT_BACKGROUND);
351 }
352
353 return last;
354 }
355
356 public synchronized static void overwriteMessage(String message) {
357 overwriteMessage(message, null);
358 }
359
360 public synchronized static void overwriteMessage(String message, Color color) {
361 for (int ind = _messages.length - 1; ind >= 0; ind--) {
362 if (_messages[ind] != null) {
363 _messages[ind].setColor(color);
364 _messages[ind].setText(getMessagePrefix(false) + message);
365 refresh(false, FrameGraphics.createGraphics(),
366 Item.DEFAULT_BACKGROUND);
367 return;
368 }
369 }
370
371 // if we have not returned, then there are no messages yet
372 displayMessage(message, Color.darkGray);
373 }
374
375 private static String getMessagePrefix(boolean incrementCounter) {
376 if (incrementCounter)
377 _messageCount++;
378 return "@" + _messageCount + ": ";
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 */
388 public synchronized static Text linkedErrorMessage(String message) {
389 if (_supressMessages)
390 return null;
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 return displayMessage(message, link, null, ERROR_COLOR);
397 }
398
399 public synchronized static Text errorMessage(String message) {
400 if (_supressMessages)
401 return null;
402 Misc.beep();
403 return displayMessage(message, null, null, ERROR_COLOR, false);
404 }
405
406 /**
407 * Displays the given message in the message area of the Frame, any previous
408 * message is cleared from the screen.
409 *
410 * @param message
411 * The message to display to the user in the message area
412 */
413 public synchronized static Text displayMessage(String message) {
414 return displayMessageAlways(message);
415 }
416
417 public synchronized static Text displayMessageOnce(String message) {
418 return displayMessage(message, null, null, Color.BLACK, false);
419 }
420
421 public synchronized static Text displayMessage(String message, Color textColor) {
422 return displayMessage(message, null, null, textColor);
423 // Misc.Beep();
424 }
425
426 public synchronized static Text displayMessage(Text message) {
427 Text t = null;
428 String link = message.getLink();
429 List<String> action = message.getAction();
430 Color color = message.getColor();
431 for (String s : message.getTextList()) {
432 t = displayMessage(s, link, action, color);
433 }
434 return t;
435 // Misc.Beep();
436 }
437
438 public synchronized static Text displayMessageAlways(String message) {
439 return displayMessage(message, null, null, Color.BLACK);
440 // Misc.Beep();
441 }
442
443 public synchronized static Text warningMessage(String message) {
444 return displayMessage(message, null, null, Color.MAGENTA);
445 // Misc.Beep();
446 }
447
448 public synchronized static void supressMessages(boolean val) {
449 _supressMessages = val;
450 }
451
452 public synchronized static void setStatus(String status) {
453 if (_status == null) {
454 _status = new Text(status);
455 _status.setPosition(0, 85);
456 _status.setOffset(0, FrameGraphics.getMaxFrameSize().height);
457 _status.setLink(null); // maybe link to a help frame?
458 _status.setFont(Font.decode(Text.MONOSPACED_FONT));
459 } else {
460 _status.setText(status);
461 }
462 Graphics g = FrameGraphics.createGraphics();
463 if (g != null) {
464 refresh(false, g, Item.DEFAULT_BACKGROUND);
465 }
466 }
467
468}
Note: See TracBrowser for help on using the repository browser.