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

Last change on this file since 1329 was 1329, checked in by bln4, 5 years ago

When running old regime without authentication, do not attempt to extract personal resources.

File size: 17.6 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.io.File;
22import java.io.IOException;
23import java.net.Authenticator;
24import java.security.KeyStoreException;
25import java.security.NoSuchAlgorithmException;
26import java.security.cert.CertificateException;
27import java.sql.SQLException;
28import java.util.ArrayList;
29import java.util.Collection;
30
31import org.expeditee.actions.Actions;
32import org.expeditee.actions.Simple;
33import org.expeditee.agents.mail.MailSession;
34import org.expeditee.auth.AuthenticatorBrowser;
35import org.expeditee.core.BlockingRunnable;
36import org.expeditee.core.Colour;
37import org.expeditee.core.Dimension;
38import org.expeditee.core.Point;
39import org.expeditee.gio.EcosystemManager;
40import org.expeditee.gio.EcosystemManager.Ecosystem;
41import org.expeditee.gio.GraphicsManager;
42import org.expeditee.gio.InputManager;
43import org.expeditee.gio.InputManager.WindowEventListener;
44import org.expeditee.gio.InputManager.WindowEventType;
45import org.expeditee.gio.gesture.StandardGestureActions;
46import org.expeditee.io.ProxyAuth;
47import org.expeditee.items.Item;
48import org.expeditee.items.ItemUtils;
49import org.expeditee.items.Text;
50import org.expeditee.items.widgets.WidgetCacheManager;
51import org.expeditee.network.FrameShare;
52import org.expeditee.settings.Settings;
53import org.expeditee.settings.UserSettings;
54import org.expeditee.stats.Logger;
55import org.expeditee.stats.StatsLogger;
56import org.expeditee.taskmanagement.EntitySaveManager;
57import org.expeditee.taskmanagement.SaveStateChangedEvent;
58import org.expeditee.taskmanagement.SaveStateChangedEventListener;
59
60/**
61 * The Main GUI class, comprises what people will see on the screen.<br>
62 * Note: Each Object (Item) is responsible for drawing itself on the screen.<br>
63 * Note2: The Frame is registered as a MouseListener and KeyListener, and
64 * processes any Events.
65 *
66 * TODO List:
67 *
68 * Back to standard:
69 * - JavaFX Text-hitting (requires JFX1.9)
70 * - Overlays and Vectors review
71 * - Pop-ups (unused in base Expeditee, only in Apollo)
72 * - Apollo input (test)
73 * - The rest of Apollo
74 * - Make sure clipping/invalidation takes twin-frames into account
75 * - Reinstate Simple input commands
76 * - Test LinkedTrack.paintInFreeSpace()
77 * - Test EmulatedTextItem.onMouseReleased(MouseEvent) (removed emulatedSource mouse button check)
78 * - Constrained placement of items
79 *
80 * Extra:
81 * - Self-describing input
82 * - Thread safety (most stuff on GIO event thread but Agents can start new threads)
83 * - Touch input
84 * - Better reflection??? (currently changes to the code break reflection as it relies on names)
85 * - JavaFX lag (always rendering to images doesn't gel well with JavaFX)
86 * - Swing widgets in JFX
87 * - JavaFX Widgets (exception on drawing?)
88 * - Reduce reliance on into-image rendering to improve JavaFX performance (utilise enforced-clip)
89 * - Block Swing ecosystem setup until window is available, or...
90 * - Reconfigure window-resized code so things are properly resized (PREFERRED).
91 * - Swing alpha-compositing of colours (currently alpha is ignored e.g. drop-shadow in transition).
92 * - Overly-thick extrusion lines (seems to depend on number of connected lines...)
93 * - Make FreeItems control pickup etc.
94 * - Remove MouseEventRouter
95 * - Highlighting (should be controlled by the items themselves)
96 * - Make Widgets into fully-fledged items (maybe???)
97 * - Merge Widget and HeavyDutyWidget
98 * - Redefine TextLayouts (relative/absolute layouts)
99 * - Settings exceptions (Password widget in JFX) (currently fixed with hack)
100 * - MessageBay (what did I mean by this specifically?)
101 * - Order-dependency of start-up code
102 * - MessageBay not refreshing at start-up
103 * - Invalidation hierarchy (item => frame => display controller area => window)
104 * - Add gesture data type checking
105 * - Paintable interface
106 * - EcosystemSpecific interface (utilise to enable/disable features based on ecosystem)
107 * - Remove/modify Mutable class (in Apollo.util, change to InOutReference)
108 * - Convert BlockingRunnable to interface
109 * - Modify Metronome to utilise Timeouts
110 * - Need AWT in FastAlphaEffect???
111 *
112 * General:
113 * - Tidy FrameGraphics
114 * - Comment
115 * - UserSettings.DEFAULT_PROFILE_NAME not actually a user setting
116 *
117 * Done:
118 * - Timers
119 * - Timer input animations
120 * - Finish DisplayController/FrameGraphics separation
121 * - Anchor constraints (create class)
122 * - Make MessageBay take lead from DisplayController
123 * - Make message bay display again (to do with above)
124 * - Tooltips (layout broken in Swing as window size not correct when tooltips laid out)
125 * - Frame transitions
126 * - Frame transitions in twin-frames mode drawing over each other
127 * - JFX DnD manager
128 * - Reinstate Simple
129 * - Reinstate exclude source
130 * - Reinstate commented code
131 * - Twin-frames division 0 at startup
132 * - Twin frames off-by-one frame size (draws a line of erroneous pixels)
133 * - MessageBay delays showing messages on start-up
134 * - JFX Antialiasing done on theScene??? Doesn't apply for our situation
135 * - Incorporate Clip class into code
136 * - Enforced clip
137 *
138 * @author cts16
139 * @author jdm18
140 */
141public class Browser implements SaveStateChangedEventListener {
142
143 public static final Ecosystem ECOSYSTEM_TYPE = Ecosystem.Swing;
144
145 public static Browser _theBrowser = null;
146
147 public static ProxyAuth proxyAuth = new ProxyAuth();
148
149 public static boolean _hasExited = false;
150
151 /** A flag which is set once the application is exiting. */
152 protected boolean _isExiting = false;
153
154 protected static boolean _initComplete = false;
155
156 private static String _startFrame = null;
157
158
159 /**
160 * Constructs a new Browser object, then launches it
161 *
162 * @param args
163 */
164 public static void main(String[] args) {
165 if (AuthenticatorBrowser.isAuthenticationRequired()) {
166 String starting_user_name = System.getProperty("user.name");
167 System.setProperty("startinguser.name", starting_user_name);
168 System.setProperty("user.name", AuthenticatorBrowser.USER_NOBODY);
169 }
170
171 // Parse the starting frame command-line argument
172 if(args.length > 0) {
173 setStartFrame(args[0]);
174 if(!Character.isDigit(getStartFrame().charAt(getStartFrame().length() - 1))) {
175 setStartFrame(getStartFrame() + "1");
176 }
177 } else {
178 setStartFrame("home1");
179 }
180
181 // Window icon must be set before initialisation
182 GraphicsManager.setWindowIcon(DisplayController.ICON_IMAGE);
183
184 // Setup the GIO ecosystem so it is ready when we need it
185 EcosystemManager.createEcosystem(ECOSYSTEM_TYPE);
186
187 try {
188 EcosystemManager.getMiscManager().runOnGIOThread(new BlockingRunnable() {
189 @Override
190 public void execute() {
191 init();
192 }
193 });
194 } catch (Throwable e) {
195 e.printStackTrace(System.err);
196 System.exit(1);
197 }
198
199 DisplayController.requestRefresh(false, () -> { MessageBay.showDelayedMessages(false); return true; });
200 }
201
202 public static void init() {
203 if (AuthenticatorBrowser.isAuthenticationRequired()) {
204 try {
205 _theBrowser = AuthenticatorBrowser.getInstance();
206 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | ClassNotFoundException | SQLException e) {
207 e.printStackTrace();
208 }
209 } else {
210 _theBrowser = new Browser();
211 }
212
213 EcosystemManager.getGraphicsManager().requestFocus();
214
215 // Java's way of getting access to internet. Authenticating the user with their proxy username and password.
216 Authenticator.setDefault(proxyAuth);
217
218 _initComplete = true;
219 }
220
221 /**
222 * @return
223 *
224 * True if the application is about to exit. False if not. Note that this is
225 * only set once the window is in its closed state (not closing) or if the
226 * application has explicitly being requested to exit.
227 *
228 * @see Browser#exit()
229 *
230 */
231 public boolean isExiting() {
232 return _isExiting;
233 }
234
235 public static boolean isInitComplete() {
236 return _initComplete;
237 }
238
239 protected Browser(String mode) {
240 System.out.println("Running Expeditee in " + mode + " mode.");
241 }
242
243 protected Browser() {
244 // center the frame on the screen
245 GraphicsManager g = EcosystemManager.getGraphicsManager();
246 Dimension screen = g.getScreenSize();
247 double xpos = (screen.width - UserSettings.InitialWidth.get()) / 2.0;
248 double ypos = (screen.height - UserSettings.InitialHeight.get()) / 2.0;
249 g.setWindowLocation(new Point((int) xpos, (int) ypos));
250
251 DisplayController.Init();
252
253 DisplayController.addDisplayObserver(WidgetCacheManager.getInstance());
254 if (ECOSYSTEM_TYPE == Ecosystem.Swing) {
255 DisplayController.addDisplayObserver(PopupManager.getInstance());
256 }
257
258 setInputManagerWindowRoutines();
259
260 // Reset windows to user specified size
261 Dimension initialWindowSize = new Dimension(UserSettings.InitialWidth.get(), UserSettings.InitialHeight.get());
262 g.setWindowSize(initialWindowSize);
263
264 // Load documentation and start pages
265 FrameUtils.extractResources(false);
266
267 // Load fonts before loading any frames so the items on the frames will be able
268 // to access their fonts
269 Text.InitFonts();
270
271 Settings.Init();
272 Frame userProfile = loadProfiles();
273
274 if (!AuthenticatorBrowser.isAuthenticationRequired() && UserSettings.PublicAndPrivateResources) {
275 String userName = UserSettings.ProfileName.get();
276 FrameIO.setupPersonalResources(userName);
277 }
278 // Listen for save status to display during and after runtime
279 EntitySaveManager.getInstance().addSaveStateChangedEventListener(this);
280
281 try {
282 MessageBay.warningMessages(Actions.Init());
283
284 // Go to the start frame if specified, otherwise go to the profile frame
285 Frame start = null;
286 if (getStartFrame() == null) {
287 setStartFrame(UserSettings.StartFrame.get());
288 if (getStartFrame() != null && !Character.isDigit(getStartFrame().charAt(getStartFrame().length() - 1))) {
289 setStartFrame(getStartFrame() + "1");
290 }
291 }
292
293 if ((start = FrameIO.LoadFrame(getStartFrame())) != null) {
294 // Make sure HomeFrame gets set
295 UserSettings.HomeFrame.set(start.getName());
296
297 // Go to the start frame
298 DisplayController.setCurrentFrame(start, true);
299 } else {
300 // If an invalid start frame was specified, show a warning
301 if (getStartFrame() != null) {
302 MessageBay.warningMessage("Unknown frame: " + getStartFrame());
303 }
304
305 // Go to the profile frame
306 FrameUtils.loadFirstFrame(userProfile);
307 }
308
309 DisplayController.updateTitle();
310
311 // Don't refresh for the profile frame otherwise error messages are shown twice
312 if (!DisplayController.getCurrentFrame().equals(userProfile)) {
313 StandardGestureActions.Refresh();
314 // If it's the profile frame just reparse it in order to display
315 // images/circles/widgets correctly
316 } else {
317 FrameUtils.Parse(userProfile);
318 }
319 } catch (Exception e) {
320 e.printStackTrace();
321 Logger.Log(e);
322 }
323 }
324
325 @Override
326 public void saveCompleted(SaveStateChangedEvent event)
327 {
328 MessageBay.displayMessage("Save finished!", Colour.BLUE);
329 }
330
331 @Override
332 public void saveStarted(SaveStateChangedEvent event)
333 {
334 String name = event.getEntity().getSaveName();
335 if (name == null) {
336 name = "data";
337 }
338 MessageBay.displayMessage("Saving " + name + "...", Colour.BLUE);
339 }
340
341 /**
342 * Closes the browser and ends the application. Performs saving operations -
343 * halting until saves have completed. Feedback is given to the user while
344 * the application is exiting. Must call on the swing thread.
345 */
346 public void exit() {
347
348 // Set exiting flag
349 _isExiting = true;
350
351 MessageBay.displayMessage("System exiting...");
352
353 /**
354 * TODO: Prompt the user etc.
355 */
356
357 // TODO: Should we should a popup with a progress bar for user feedback?
358 // this would be nice and easy to do.
359 // Exit on a dedicated thread so that feedback can be obtained
360 new Exiter().start(); // this will exit the application
361 }
362
363 /**
364 * The system must exit on a different thread other than the swing thread so
365 * that the save threads can fire save-feedback to the swing thread and thus
366 * provide user feedback on asynchronous save operations.
367 *
368 * @author Brook Novak
369 *
370 */
371 private class Exiter extends Thread {
372
373 @Override
374 public void run() {
375
376 // The final save point for saveable entities
377 EntitySaveManager.getInstance().saveAll();
378 try {
379 EntitySaveManager.getInstance().waitUntilAllSavingFinished();
380 } catch (InterruptedException e) {
381 e.printStackTrace();
382 }
383
384 // Stop any agents or simple programs
385 Simple.stop();
386 Actions.stopAgent();
387 // Wait for them to stop
388 try {
389 // Only stop if need to...
390 // Brook: What purpose does this serve?
391 MessageBay.displayMessage("Stopping Simple programs...");
392 while (Simple.isProgramRunning()) {
393 Thread.sleep(100);
394 }
395
396 MessageBay.displayMessage("Stopping Agents...");
397 /* TODO: Only stop if need to... */
398 while (Actions.isAgentRunning()) {
399 Thread.sleep(100); // Brook: What purpose does this serve?
400 }
401 } catch (Exception e) {
402
403 }
404
405 MessageBay.displayMessage("Saving current frame...");
406 FrameIO.SaveFrame(DisplayController.getCurrentFrame());
407
408 MessageBay.displayMessage("Saving stats...");
409 StatsLogger.WriteStatsFile();
410
411 if (MailSession.getInstance() != null) {
412 if (MailSession.getInstance().finalise()) {
413 // TODO display this message before the finalising
414 // is done but only if the mail needs closing
415 MessageBay.displayMessage("Closed ExpMail...");
416 }
417 }
418
419 if (FrameShare.getInstance() != null) {
420 MessageBay.displayMessage("Stopping FrameServer...");
421 FrameShare.getInstance().finalise();
422 }
423
424 MessageBay.displayMessage("System exited");
425
426 // Finally remove the messages frameset
427 FrameIO.moveFrameset("messages", FrameIO.MESSAGES_PATH, false);
428
429 /*
430 * Create a new messages folder so that it doesn't throw
431 * Exceptions when two Expeditee's open at once and the
432 * second tries to save its messages
433 */
434 File file = new File(FrameIO.MESSAGES_PATH + "messages");
435 file.mkdirs();
436
437 Browser._hasExited = true;
438
439 System.exit(0);
440 }
441 }
442
443 /**
444 * Used to set up the the browser for use in testing.
445 *
446 * @return
447 */
448 public static Browser initializeForTesting()
449 {
450 if (Browser._theBrowser == null) {
451 FrameShare.disableNetworking = true;
452 MailSession._autoConnect = false;
453
454 Browser.main(new String[]{});
455 try {
456 while (!isInitComplete()) {
457 Thread.sleep(10);
458 }
459 } catch (Exception e) {
460 }
461 }
462 return _theBrowser;
463 }
464
465 private static void setInputManagerWindowRoutines() {
466 InputManager manager = EcosystemManager.getInputManager();
467
468 // Refresh the layout when the window resizes
469 manager.addWindowEventListener(new WindowEventListener() {
470 @Override
471 public void onWindowEvent(WindowEventType type)
472 {
473 if (type != WindowEventType.WINDOW_RESIZED) {
474 return;
475 }
476 DisplayController.refreshWindowSize();
477 FrameIO.RefreshCacheImages();
478 for (Frame frame : DisplayController.getFrames()) {
479 if (frame != null) {
480 ItemUtils.Justify(frame);
481 frame.refreshSize();
482 }
483 }
484 DisplayController.requestRefresh(false);
485 }
486 });
487
488 manager.addWindowEventListener(new WindowEventListener() {
489 @Override
490 public void onWindowEvent(WindowEventType type)
491 {
492 if (type != WindowEventType.MOUSE_EXITED_WINDOW) {
493 return;
494 }
495 StandardGestureActions.mouseExitedWindow();
496 }
497 });
498
499 manager.addWindowEventListener(new WindowEventListener() {
500 @Override
501 public void onWindowEvent(WindowEventType type)
502 {
503 if (type != WindowEventType.MOUSE_ENTERED_WINDOW) {
504 return;
505 }
506 StandardGestureActions.mouseEnteredWindow();
507 }
508 });
509
510 manager.addWindowEventListener(new WindowEventListener() {
511 @Override
512 public void onWindowEvent(WindowEventType type)
513 {
514 if (type != WindowEventType.WINDOW_CLOSED) {
515 return;
516 }
517 if (Browser._theBrowser != null) {
518 Browser._theBrowser.exit();
519 }
520 }
521 });
522 }
523
524 /**
525 * @return The user's profile frame.
526 */
527 public static Frame loadProfiles()
528 {
529 String defaultProfileName = UserSettings.DEFAULT_PROFILE_NAME;
530 String userName = UserSettings.ProfileName.get();
531
532 Frame userProfile = loadProfile(userName);
533 Frame defaultProfile = loadProfile(defaultProfileName);
534
535 MessageBay.warningMessages(FrameUtils.ParseProfile(defaultProfile));
536
537 // Save the cursor if the defaultProfile had a custom cursor
538 Collection<Item> cursor = null;
539 if(FreeItems.hasCursor()) {
540 cursor = new ArrayList<Item>();
541 cursor.addAll(FreeItems.getCursor());
542 }
543
544 MessageBay.warningMessages(FrameUtils.ParseProfile(userProfile));
545
546 if (cursor != null && !FreeItems.hasCursor()) {
547 FreeItems.setCursor(cursor);
548 }
549
550 return userProfile;
551 }
552
553 protected static Frame loadProfile(String userName) {
554 Frame profile = FrameIO.LoadProfile(userName);
555
556 if (profile == null) {
557 try {
558 profile = FrameIO.CreateNewProfile(userName, null, null);
559 } catch (Exception e) {
560 // TODO tell the user that there was a problem creating the
561 // profile frame and close nicely
562 e.printStackTrace();
563 assert (false);
564 }
565 }
566 return profile;
567 }
568
569 public static String getStartFrame() {
570 return _startFrame;
571 }
572
573 public static void setStartFrame(String _startFrame) {
574 Browser._startFrame = _startFrame;
575 }
576}
Note: See TracBrowser for help on using the repository browser.