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

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

Switch to using specialised objects for settings so they make more a bit more sense (now each setting is a single object instead of multiple, and setter functions and default values are less hackish)
Also added tooltips (help strings) to settings, will need to add a way of displaying these (maybe add the idea of a tooltip which is a text item which only appears when hovering over another item?)
Converted all settings over to new format, everything seems to be working fine

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