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

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

Attempt at fixing problem with window height not being known at settings frame generation

File size: 16.5 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((start = FrameIO.LoadFrame(_startFrame)) != null) {
293 // Make sure HomeFrame gets set
294 if (UserSettings.HomeFrame.get() == null)
295 UserSettings.HomeFrame.set(profile.getName());
296 // Make sure the user can get back to the profile frame easily
297 DisplayIO.addToBack(profile);
298 // Go to the start frame
299 DisplayIO.setCurrentFrame(start, true);
300 } else {
301 // If an invalid start frame was specified, show a warning
302 if(_startFrame != null) {
303 warningMessages.add("Unknown frame: " + _startFrame);
304 }
305 // Go to the profile frame
306 FrameUtils.loadFirstFrame(profile);
307 }
308 DisplayIO.UpdateTitle();
309
310 /*
311 * I think this can be moved back up to the top of the Go method
312 * now... It used to crash the program trying to print error
313 * messages up the top
314 */
315 for (String message : warningMessages)
316 MessageBay.warningMessage(message);
317
318 this.getContentPane().addKeyListener(FrameKeyboardActions.getInstance());
319 this.addKeyListener(FrameKeyboardActions.getInstance());
320
321 _mouseEventRouter.addExpediteeMouseListener(FrameMouseActions.getInstance());
322 _mouseEventRouter.addExpediteeMouseMotionListener(FrameMouseActions.getInstance());
323 _mouseEventRouter.addExpediteeMouseWheelListener(FrameMouseActions.getInstance());
324
325 // Dont refresh for the profile frame otherwise error messages are shown twice
326 if (!DisplayIO.getCurrentFrame().equals(profile)) {
327 FrameKeyboardActions.Refresh();
328 // If it's the profile frame just reparse it in order to display images/circles/widgets correctly
329 } else {
330 FrameUtils.Parse(profile);
331 }
332 // setVisible(true);
333 } catch (Exception e) {
334 e.printStackTrace();
335 Logger.Log(e);
336 }
337 }
338
339 /**
340 * @param userName
341 * @return
342 */
343 private Frame loadProfile(String userName) {
344 Frame profile = FrameIO.LoadProfile(userName);
345 if (profile == null) {
346 try {
347 profile = FrameIO.CreateNewProfile(userName);
348 } catch (Exception e) {
349 // TODO tell the user that there was a problem creating the
350 // profile frame and close nicely
351 e.printStackTrace();
352 assert (false);
353 }
354 }
355 return profile;
356 }
357
358 public Graphics2D g;
359
360 private void setupGraphics() {
361 if (g != null)
362 g.dispose();
363 g = (Graphics2D) this.getContentPane().getGraphics();
364 assert (g != null);
365 g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
366 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
367 g.setFont(g.getFont().deriveFont(40f));
368 FrameGraphics.setDisplayGraphics(g);
369 }
370
371 // private int count = 0;
372 @Override
373 public void paint(Graphics g) {
374 // All this does is make sure the screen is repainted when the browser
375 // is moved so that some of the window is off the edge of the display
376 // then moved back into view
377 super.paint(g);
378 FrameGraphics.ForceRepaint();
379 // System.out.println("Paint " + count++);
380 }
381
382 /**
383 * @inheritDoc
384 */
385 public void componentResized(ComponentEvent e) {
386 setSizes(this.getSize());
387 setupGraphics();
388 FrameIO.RefreshCasheImages();
389 FrameGraphics.ForceRepaint();
390 }
391
392 /**
393 * @inheritDoc
394 */
395 public void componentMoved(ComponentEvent e) {
396 // FrameGraphics.setMaxSize(this.getSize());
397 }
398
399 /**
400 * @inheritDoc
401 */
402 public void componentShown(ComponentEvent e) {
403 }
404
405 /**
406 * @inheritDoc
407 */
408 public void componentHidden(ComponentEvent e) {
409 }
410
411 public void windowClosing(WindowEvent e) {
412 }
413
414 public void windowClosed(WindowEvent e) {
415 exit();
416 }
417
418 public void windowOpened(WindowEvent e) {
419 }
420
421 public void windowIconified(WindowEvent e) {
422 }
423
424 public void windowDeiconified(WindowEvent e) {
425 }
426
427 public void windowActivated(WindowEvent e) {
428 // System.out.println("Activated");
429 }
430
431 public void windowDeactivated(WindowEvent e) {
432 ItemSelection.cut(FreeItems.getInstance());
433 }
434
435 public void windowStateChanged(WindowEvent e) {
436 }
437
438 public int getDrawingAreaX() {
439 // return scrollPane.getLocationOnScreen().x;
440 return this.getLocationOnScreen().x;
441 }
442
443 public int getDrawingAreaY() {
444 // return scrollPane.getLocationOnScreen().y;
445 return this.getLocationOnScreen().y;
446 }
447
448 public void saveCompleted(SaveStateChangedEvent event) {
449 // if (isExisting()) {
450
451 // } else {
452 MessageBay.displayMessage("Save finished!", Color.BLUE);
453 // }
454 }
455
456 public void saveStarted(SaveStateChangedEvent event) {
457 // if (isExisting()) {
458
459 // } else {
460 String name = event.getEntity().getSaveName();
461 if (name == null)
462 name = "data";
463 MessageBay.displayMessage("Saving " + name + "...", Color.BLUE);
464 // }
465 }
466
467 /**
468 * Closes the browser and ends the application. Performs saving operations -
469 * halting until saves have completed. Feedback is given to the user while
470 * the application is exiting. Must call on the swing thread.
471 */
472 public void exit() {
473
474 // Set exiting flag
475 _isExiting = true;
476
477 MessageBay.displayMessage("System exiting...");
478
479 /**
480 * TODO: Prompt the user etc.
481 */
482
483 // TODO: Should we should a popup with a progress bar for user feedback?
484 // this would be nice and easy to do.
485 // Exit on a dedicated thread so that feedback can be obtained
486 new Exiter().start(); // this will exit the application
487 }
488
489 /**
490 * The system must exit on a different thread other than the swing thread so
491 * that the save threads can fire save-feedback to the swing thread and thus
492 * provide user feedback on asynchronous save operations.
493 *
494 * @author Brook Novak
495 *
496 */
497 private class Exiter extends Thread {
498
499 @Override
500 public void run() {
501
502 // The final save point for saveable entities
503 EntitySaveManager.getInstance().saveAll();
504 try {
505 EntitySaveManager.getInstance().waitUntilAllSavingFinished();
506 } catch (InterruptedException e) {
507 e.printStackTrace();
508 }
509
510 // The final phase must save on the swing thread since dealing with
511 // the expeditee data model
512 SwingUtilities.invokeLater(new Runnable() {
513 public void run() {
514
515 // Stop any agents or simple programs
516 Simple.stop();
517 Actions.stopAgent();
518 // Wait for them to stop
519 try {
520 MessageBay
521 .displayMessage("Stopping Simple programs..."); // TODO:
522 /**
523 * Only stop if need to...
524 */
525 while (Simple.isProgramRunning()) {
526 Thread.sleep(100);
527 /* Brook: What purpose does this serve? */
528 }
529 MessageBay.displayMessage("Stopping Agents...");
530 /* TODO: Only stop if need to... */
531 while (Actions.isAgentRunning()) {
532 Thread.sleep(100); // Brook: What purpose does this
533 // serve?
534 }
535 } catch (Exception e) {
536
537 }
538
539 MessageBay.displayMessage("Saving current frame...");
540 FrameIO.SaveFrame(DisplayIO.getCurrentFrame());
541
542 MessageBay.displayMessage("Saving stats...");
543 StatsLogger.WriteStatsFile();
544
545 if (MailSession.getInstance() != null) {
546 if (MailSession.getInstance().finalise()) {
547 // TODO display this message before the finalising
548 // is done but only if the mail needs closing
549 MessageBay.displayMessage("Closed ExpMail...");
550 }
551 }
552
553 if (FrameShare.getInstance() != null) {
554 MessageBay.displayMessage("Stopping FrameServer...");
555 FrameShare.getInstance().finalise();
556 }
557
558 MessageBay.displayMessage("System exited");
559
560 // Finally remove the messages frameset
561 FrameIO.moveFrameset("messages", FrameIO.MESSAGES_PATH);
562
563 /*
564 * Create a new messages folder so that it doesn't throw
565 * Exceptions when two Expeditee's open at once and the
566 * second tries to save its messages
567 */
568 File file = new File(FrameIO.MESSAGES_PATH + "messages");
569 file.mkdirs();
570
571 System.exit(0);
572 }
573 });
574 }
575 }
576
577 /**
578 * Used to set up the the browser for use in testing.
579 *
580 * @return
581 */
582 public static Browser initializeForTesting() {
583 if (Browser._theBrowser == null) {
584 FrameShare.disableNetworking = true;
585 MailSession._autoConnect = false;
586
587 Browser.main(null);
588 try {
589 while (!isInitComplete()) {
590 Thread.sleep(10);
591 }
592 } catch (Exception e) {
593 }
594 }
595 return _theBrowser;
596 }
597
598}
Note: See TracBrowser for help on using the repository browser.