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

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

Implement settings for FrameShareTimeout and StartFrame. StartFrame does not currently support returning to the last visited frame

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