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

Last change on this file since 1032 was 1032, checked in by davidb, 8 years ago

Change of logic so a given starting frame on the command-lline is treated as the home frame

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