source: trunk/src/org/expeditee/gui/Browser.java@ 419

Last change on this file since 419 was 419, checked in by ra33, 16 years ago

Refactored DateTime format code... so all formats are determined in a single spot.

Fixed bug preventing users from clicking a frame title of a protected frame to move to the next and previous frame.

File size: 13.9 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Dimension;
5import java.awt.Graphics;
6import java.awt.Graphics2D;
7import java.awt.MouseInfo;
8import java.awt.Point;
9import java.awt.RenderingHints;
10import java.awt.Toolkit;
11import java.awt.event.ComponentEvent;
12import java.awt.event.ComponentListener;
13import java.awt.event.WindowEvent;
14import java.awt.event.WindowListener;
15import java.awt.event.WindowStateListener;
16import java.io.File;
17import java.util.Collection;
18import java.util.LinkedList;
19
20import javax.swing.JFrame;
21import javax.swing.RepaintManager;
22import javax.swing.SwingUtilities;
23
24import org.expeditee.AbsoluteLayout;
25import org.expeditee.actions.Actions;
26import org.expeditee.actions.Simple;
27import org.expeditee.agents.mail.MailSession;
28import org.expeditee.importer.FrameDNDTransferHandler;
29import org.expeditee.items.widgets.WidgetCacheManager;
30import org.expeditee.network.FrameShare;
31import org.expeditee.stats.Logger;
32import org.expeditee.stats.StatsLogger;
33import org.expeditee.taskmanagement.EntitySaveManager;
34import org.expeditee.taskmanagement.SaveStateChangedEvent;
35import org.expeditee.taskmanagement.SaveStateChangedEventListener;
36
37/**
38 * The Main GUI class, comprises what people will see on the screen.<br>
39 * Note: Each Object (Item) is responsible for drawing itself on the screen.<br>
40 * Note2: The Frame is registered as a MouseListener and KeyListener, and
41 * processes any Events.
42 *
43 * @author jdm18
44 *
45 */
46public class Browser extends JFrame implements ComponentListener,
47 WindowListener, WindowStateListener, SaveStateChangedEventListener {
48
49 /**
50 * Default version - just to stop eclipse from complaining about it.
51 */
52 private static final long serialVersionUID = 1L;
53
54 // private static final JScrollPane scrollPane = new JScrollPane();
55
56 public static Browser _theBrowser = null;
57
58 private MouseEventRouter _mouseEventRouter;
59
60 // A flag which is set once the application is exiting.
61 private boolean _isExiting = false;
62
63 private boolean _version6 = false;
64
65 public boolean isVersion6() {
66 return _version6;
67 }
68
69 private static boolean _initComplete = false;
70
71 /**
72 * Constructs a new Browser object, then launches it
73 *
74 * @param args
75 */
76 public static void main(String[] args) {
77
78 // Prepare all expeditee and swing data on the AWT event thread.
79 SwingUtilities.invokeLater(new Runnable() {
80 public void run() {
81 // MessageBay.supressMessages(true);
82
83 // MessageBay.supressMessages(false);
84
85 _theBrowser = new Browser();
86 _theBrowser.requestFocus();
87 FrameMouseActions.MouseX = MouseInfo.getPointerInfo()
88 .getLocation().x
89 - _theBrowser.getOrigin().x;
90 FrameMouseActions.MouseY = MouseInfo.getPointerInfo()
91 .getLocation().y
92 - _theBrowser.getOrigin().y;
93 _initComplete = true;
94 }
95 });
96
97 }
98
99 public Point getOrigin() {
100 return getContentPane().getLocationOnScreen();
101 }
102
103 /**
104 * @return The mouse event router used fot this browser. Never null after
105 * browser constructed.
106 */
107 public MouseEventRouter getMouseEventRouter() {
108 return _mouseEventRouter;
109 }
110
111 /**
112 * @return
113 *
114 * True if the application is about to exit. False if not. Not that this is
115 * only set once the window is in its closed state (not closing) or if the
116 * application has explicity being requested to exit.
117 *
118 * @see Browser#exit()
119 *
120 */
121 public boolean isExisting() {
122 return _isExiting;
123 }
124
125 private static boolean isInitComplete() {
126 return _initComplete;
127 }
128
129 public void setSizes(Dimension size) {
130 setSize(size);
131 setPreferredSize(size);
132 Dimension paneSize = getContentPane().getSize();
133 FrameGraphics.setMaxSize(paneSize);
134 }
135
136 private Browser() {
137 // Use the default values initially so we can load the profile frame
138 setSizes(new Dimension(UserSettings.InitialWidth,
139 UserSettings.InitialHeight));
140 // center the frame on the screen
141 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
142 double xpos = screen.getWidth() / 2;
143 double ypos = screen.getHeight() / 2;
144 setLocation((int) (xpos - (UserSettings.InitialWidth / 2)),
145 (int) (ypos - (UserSettings.InitialHeight / 2)));
146
147 addWindowListener(this);
148 addWindowStateListener(this);
149
150 DisplayIO.addDisplayIOObserver(WidgetCacheManager.getInstance());
151 DisplayIO.addDisplayIOObserver(PopupManager.getInstance());
152
153 UserSettings.ProfileName = FrameIO.ConvertToValidFramesetName(System
154 .getProperty("user.name"));
155 String userName = UserSettings.ProfileName;
156 UserSettings.UserName = UserSettings.ProfileName;
157
158 Frame profile = loadProfile(userName);
159
160 // Need to display errors once things have been init otherwise
161 // exceptions occur if there are more than four messages neededing to be
162 // displayed.
163
164 Frame defaultProfile = loadProfile(UserSettings.DEFAULT_PROFILE_NAME);
165
166 Collection<String> warningMessages = new LinkedList<String>();
167 warningMessages.addAll(FrameUtils.ParseProfile(defaultProfile));
168 warningMessages.addAll(FrameUtils.ParseProfile(profile));
169 // set the layout to absolute layout for widgets
170 this.getContentPane().setLayout(new AbsoluteLayout());
171
172 _mouseEventRouter = new MouseEventRouter(getJMenuBar(),
173 getContentPane());
174
175 // enable the glasspane-for capturing all mouse events
176 this.setGlassPane(_mouseEventRouter);
177
178 this.getGlassPane().setVisible(true);
179 this.getContentPane().setBackground(Color.white);
180 this.getContentPane().setFocusTraversalKeysEnabled(false);
181
182 addComponentListener(this);
183 pack();
184
185 // Reset windows to user specified size
186 // Must be done after initialising the content pane above!
187 setSizes(new Dimension(UserSettings.InitialWidth,
188 UserSettings.InitialHeight));
189
190 /*
191 * See Java bug ID 4016934. They say that window closed events are
192 * called once the JFrame is disposed.
193 */
194 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
195
196 // Expeditee handles its own repainting of AWT/Swing components
197 RepaintManager.setCurrentManager(ExpediteeRepaintManager.getInstance());
198
199 // Listen for save status to display during and after runtime
200 EntitySaveManager.getInstance().addSaveStateChangedEventListener(this);
201
202 String version = System.getProperty("java.version");
203
204 if (version.startsWith("1.6")) {
205 // Set the drag and drop handler
206 _version6 = true;
207 setTransferHandler(FrameDNDTransferHandler.getInstance());
208 } else {
209 System.out.println("Upgrade to Java 1.6 to enable drag and drop");
210 }
211
212 try {
213 warningMessages.addAll(Actions.Init());
214
215 DisplayIO.Init(this);
216 // Set visible must be just after DisplayIO.Init for the message box
217 // to
218 // be the right size
219 setVisible(true);
220
221 setupGraphics();
222
223 // required to accept TAB key
224 setFocusTraversalKeysEnabled(false);
225
226 // Must be loaded after setupGraphics if images are on the frame
227 // Turn off XRay mode and load the first frame
228 FrameGraphics.setMode(FrameGraphics.MODE_NORMAL, false);
229 FrameUtils.loadFirstFrame(profile);
230 DisplayIO.UpdateTitle();
231
232 /*
233 * I think this can be moved back up to the top of the Go method
234 * now... It used to crash the program trying to print error
235 * messages up the top
236 */
237 for (String message : warningMessages)
238 MessageBay.warningMessage(message);
239
240 this.getContentPane().addKeyListener(
241 FrameKeyboardActions.getInstance());
242 this.addKeyListener(FrameKeyboardActions.getInstance());
243
244 _mouseEventRouter.addExpediteeMouseListener(FrameMouseActions
245 .getInstance());
246 _mouseEventRouter.addExpediteeMouseMotionListener(FrameMouseActions
247 .getInstance());
248 _mouseEventRouter.addExpediteeMouseWheelListener(FrameMouseActions
249 .getInstance());
250
251 /*
252 * Dont refresh for the profile frame otherwise error messages are
253 * shown twice
254 */
255 if (!DisplayIO.getCurrentFrame().equals(profile))
256 FrameKeyboardActions.Refresh();
257 // setVisible(true);
258 } catch (Exception e) {
259 e.printStackTrace();
260 Logger.Log(e);
261 }
262 }
263
264 /**
265 * @param userName
266 * @return
267 */
268 private Frame loadProfile(String userName) {
269 Frame profile = FrameIO.LoadProfile(userName);
270 if (profile == null) {
271 try {
272 profile = FrameIO.CreateNewProfile(userName);
273 } catch (Exception e) {
274 // TODO tell the user that there was a problem creating the
275 // profile frame and close nicely
276 e.printStackTrace();
277 assert (false);
278 }
279 }
280 return profile;
281 }
282
283 public Graphics2D g;
284
285 private void setupGraphics() {
286 if (g != null)
287 g.dispose();
288 g = (Graphics2D) this.getContentPane().getGraphics();
289 assert (g != null);
290 g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
291 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
292 g.setFont(g.getFont().deriveFont(40f));
293 FrameGraphics.setDisplayGraphics(g);
294 }
295
296 // private int count = 0;
297 @Override
298 public void paint(Graphics g) {
299 // All this does is make sure the screen is repainted when the browser
300 // is moved so that some of the window is off the edge of the display
301 // then moved back into view
302 super.paint(g);
303 FrameGraphics.ForceRepaint();
304 // System.out.println("Paint " + count++);
305 }
306
307 /**
308 * @inheritDoc
309 */
310 public void componentResized(ComponentEvent e) {
311 setSizes(this.getSize());
312 setupGraphics();
313 FrameIO.RefreshCasheImages();
314 FrameGraphics.ForceRepaint();
315 }
316
317 /**
318 * @inheritDoc
319 */
320 public void componentMoved(ComponentEvent e) {
321 // FrameGraphics.setMaxSize(this.getSize());
322 }
323
324 /**
325 * @inheritDoc
326 */
327 public void componentShown(ComponentEvent e) {
328 }
329
330 /**
331 * @inheritDoc
332 */
333 public void componentHidden(ComponentEvent e) {
334 }
335
336 public void windowClosing(WindowEvent e) {
337 }
338
339 public void windowClosed(WindowEvent e) {
340 exit();
341 }
342
343 public void windowOpened(WindowEvent e) {
344 }
345
346 public void windowIconified(WindowEvent e) {
347 }
348
349 public void windowDeiconified(WindowEvent e) {
350 }
351
352 public void windowActivated(WindowEvent e) {
353 // System.out.println("Activated");
354 }
355
356 public void windowDeactivated(WindowEvent e) {
357 }
358
359 public void windowStateChanged(WindowEvent e) {
360 }
361
362 public int getDrawingAreaX() {
363 // return scrollPane.getLocationOnScreen().x;
364 return this.getLocationOnScreen().x;
365 }
366
367 public int getDrawingAreaY() {
368 // return scrollPane.getLocationOnScreen().y;
369 return this.getLocationOnScreen().y;
370 }
371
372 public void saveCompleted(SaveStateChangedEvent event) {
373 // if (isExisting()) {
374
375 // } else {
376 MessageBay.displayMessage("Save finished!", Color.BLUE);
377 // }
378 }
379
380 public void saveStarted(SaveStateChangedEvent event) {
381 // if (isExisting()) {
382
383 // } else {
384 String name = event.getEntity().getSaveName();
385 if (name == null)
386 name = "data";
387 MessageBay.displayMessage("Saving " + name + "...", Color.BLUE);
388 // }
389 }
390
391 /**
392 * Closes the browser and ends the application. Performs saving operations -
393 * halting until saves have completed. Feedback is given to the user while
394 * the application is exiting. Must call on the swing thread.
395 */
396 public void exit() {
397
398 // Set exiting flag
399 _isExiting = true;
400
401 MessageBay.displayMessage("System exiting...");
402
403 /**
404 * TODO: Prompt the user etc.
405 */
406
407 // TODO: Should we should a popup with a progress bar for user feedback?
408 // this would be nice and easy to do.
409 // Exit on a dedicated thread so that feedback can be obtained
410 new Exiter().start(); // this will exit the application
411 }
412
413 /**
414 * The system must exit on a different thread other than the swing thread so
415 * that the save threads can fire save-feedback to the swing thread and thus
416 * provide user feedback on asynchronous save operations.
417 *
418 * @author Brook Novak
419 *
420 */
421 private class Exiter extends Thread {
422
423 @Override
424 public void run() {
425
426 // The final save point for saveable entities
427 EntitySaveManager.getInstance().saveAll();
428 try {
429 EntitySaveManager.getInstance().waitUntilAllSavingFinished();
430 } catch (InterruptedException e) {
431 e.printStackTrace();
432 }
433
434 // The final phase must save on the swing thread since dealing with
435 // the expeditee data model
436 SwingUtilities.invokeLater(new Runnable() {
437 public void run() {
438
439 // Stop any agents or simple programs
440 Simple.stop();
441 Actions.stopAgent();
442 // Wait for them to stop
443 try {
444 MessageBay
445 .displayMessage("Stopping Simple programs..."); // TODO:
446 /**
447 * Only stop if need to...
448 */
449 while (Simple.isProgramRunning()) {
450 Thread.sleep(100);
451 /* Brook: What purpose does this serve? */
452 }
453 MessageBay.displayMessage("Stopping Agents...");
454 /* TODO: Only stop if need to... */
455 while (Actions.isAgentRunning()) {
456 Thread.sleep(100); // Brook: What purpose does this
457 // serve?
458 }
459 } catch (Exception e) {
460
461 }
462
463 MessageBay.displayMessage("Saving current frame...");
464 FrameIO.SaveFrame(DisplayIO.getCurrentFrame());
465
466 MessageBay.displayMessage("Saving stats...");
467 StatsLogger.WriteStatsFile();
468
469 if (MailSession.getInstance() != null) {
470 if (MailSession.getInstance().finalise()) {
471 // TODO display this message before the finalising
472 // is done but only if the mail needs closing
473 MessageBay.displayMessage("Closed ExpMail...");
474 }
475 }
476
477 if (FrameShare.getInstance() != null) {
478 MessageBay.displayMessage("Stopping FrameServer...");
479 FrameShare.getInstance().finalise();
480 }
481
482 MessageBay.displayMessage("System exited");
483
484 // Finally remove the messages frameset
485 FrameIO.moveFrameset("messages", FrameIO.MESSAGES_PATH);
486
487 /*
488 * Create a new messages folder so that it doesn't throw
489 * Exceptions when two Expeditee's open at once and the
490 * second tries to save its messages
491 */
492 File file = new File(FrameIO.MESSAGES_PATH + "messages");
493 file.mkdirs();
494
495 System.exit(0);
496 }
497 });
498 }
499 }
500
501 /**
502 * Used to set up the the browser for use in testing.
503 *
504 * @return
505 */
506 public static Browser initializeForTesting() {
507 if (Browser._theBrowser == null) {
508 FrameShare.disableNetworking = true;
509 MailSession._autoConnect = false;
510
511 Browser.main(null);
512 try {
513 while (!isInitComplete()) {
514 Thread.sleep(10);
515 }
516 } catch (Exception e) {
517 }
518 }
519 return _theBrowser;
520 }
521
522}
Note: See TracBrowser for help on using the repository browser.