source: trunk/src/org/expeditee/items/widgets/HeavyDutyInteractiveWidget.java@ 1143

Last change on this file since 1143 was 1143, checked in by bln4, 6 years ago

Used Eclipse refactoring to encapsulate Point.X and Point.Y

File size: 22.5 KB
Line 
1/**
2 * HeavyDutyInteractiveWidget.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.items.widgets;
20
21import java.awt.Font;
22import java.awt.FontMetrics;
23import java.awt.GradientPaint;
24import java.awt.Graphics;
25import java.awt.Graphics2D;
26import java.awt.Rectangle;
27import java.awt.Shape;
28import java.awt.geom.Rectangle2D;
29import java.util.LinkedList;
30
31import javax.swing.JComponent;
32import javax.swing.SwingUtilities;
33
34import org.expeditee.core.Colour;
35import org.expeditee.gio.swing.SwingConversions;
36import org.expeditee.gio.swing.SwingGraphicsManager;
37import org.expeditee.gio.swing.SwingMiscManager;
38import org.expeditee.gui.Browser;
39import org.expeditee.gui.DisplayController;
40import org.expeditee.gui.FreeItems;
41import org.expeditee.items.Item;
42import org.expeditee.items.ItemParentStateChangedEvent;
43import org.expeditee.items.Text;
44import org.expeditee.taskmanagement.EntityLoadManager;
45import org.expeditee.taskmanagement.EntitySaveManager;
46import org.expeditee.taskmanagement.LoadableEntity;
47import org.expeditee.taskmanagement.SaveableEntity;
48
49/**
50 * Interactive widgets that may require loading prior to usage, and/or saving.
51 *
52 * HeavyDutyInteractiveWidget provides a conventional asynchronous loading and saving scheme.
53 *
54 * When a loadable interactive widget is created. It is at a pending state - waiting to load
55 * until expeditee allocates a thread to begin loading. The swing component is set as not-visible.
56 *
57 * At the the loading stage, a loadable widget processes its load task and renders a
58 * load message/bar.
59 *
60 * Once loaded, the widget is like any other widget - and its visible state will be set as visible.
61 *
62 * When a HeavyDutyInteractiveWidget becomes visible / is anchored, it will register itself for saving at the next
63 * save point according to SaveEntityManager. Heavy duty widgets will only save at the save points
64 * if the widget still belongs to a frame (i.e. is not removed) and is anchored.
65 *
66 * Heavy duty widgets can also have an expiry on how long they should stay cached for. If they do have an
67 * expiry then it they unload once expired, and will re-load once in view again. Must take care for
68 * unloadable widgets because you must check your unloadable elements to whether they are unloaded or not
69 * every time your widget accesses them.
70 *
71 * When a heavy duty widget is deleted - any data consuming memory can be dumped temporarily if needed.
72 *
73 * @author Brook Novak
74 *
75 */
76public abstract class HeavyDutyInteractiveWidget extends SwingWidget implements LoadableEntity, SaveableEntity {
77
78 /** load finished */
79 public static final float LOAD_STATE_COMPLETED = 2.0f;
80
81 /** waiting to load. Could have been loaded previously but expired. */
82 public static final float LOAD_STATE_PENDING = 3.0f;
83
84 /** load failed */
85 public static final float LOAD_STATE_FAILED = 4.0f;
86
87 /** load was cancelled and still requires loading */
88 public static final float LOAD_STATE_INCOMPLETED = 5.0f;
89
90 // GUI Stuff
91 private static final String DEFAULT_LOAD_MESSAGE = "Loading";
92 private static final String PENDING_MESSAGE = "Pending";
93
94 private static final Colour LOAD_BAR_PROGRESS_COLOR = Colour.FromRGB255(103, 171, 203);
95 private static final Colour LOAD_BAR_INDETERM_COLOR = Colour.ORANGE;
96 private static final Colour LOAD_BAR_HIGHLIGHT_COLOR = Colour.FromRGB255(240, 240, 240);
97 private static final Colour LOAD_SCREEN_COLOR = Colour.LIGHT_GREY;
98 private static final Colour LOAD_SCREEN_COLOR_FREESPACE = FREESPACE_BACKCOLOR;
99
100 private static final Font LOAD_NORMAL_FONT = new Font("Arial", Font.BOLD, 12);
101 private static final Font LOAD_INDERTMIN_FONT = new Font("Arial", Font.ITALIC, 12);
102
103 private static final int BAR_HOROZONTIAL_MARGIN = 20;
104 private static final int BAR_HEIGHT = 40;
105
106 /** Unifies state transitions to a single thread. */
107 private HDWEventDispatcher eventDispatcher = null;
108 private LinkedList<HDWEvent> queuedEvents = new LinkedList<HDWEvent>();
109
110 // Model data
111 private float loadState; // handled by constructors and stateProccessor
112 private String screenMessage = DEFAULT_LOAD_MESSAGE;
113 private boolean hasCancelledBeenRequested;
114 private boolean isExpired = false;
115 private int cacheDepth = -1;
116
117 /**
118 * Constructor.
119 *
120 * @param source
121 *
122 * @param component
123 *
124 * @param minWidth
125 *
126 * @param maxWidth
127 *
128 * @param minHeight
129 *
130 * @param maxHeight
131 *
132 * @param cacheDepth
133 * Less or equal to zero for no cache management - i.e. use expeditees frame caching.
134 * Positive to limit cache; where the widget will be explicity unloaded
135 * ({@link HeavyDutyInteractiveWidget#unloadWidgetData()}) once the user has traversed
136 * cacheDepth frames without seeing this instance.
137 * The general rule is: the more memory your widget may take, the smaller the cache depth.
138 *
139 * @param skipLoad
140 * If true, the load state will be set to completed and the widget will not go through
141 * the loading proccess. Otherwise if true then the widget will go the loading phase.
142 */
143 protected HeavyDutyInteractiveWidget(Text source, JComponent component,
144 int minWidth, int maxWidth, int minHeight, int maxHeight, int cacheDepth, boolean skipLoad)
145 {
146 super(source, component, minWidth, maxWidth, minHeight, maxHeight);
147
148 this.cacheDepth = cacheDepth;
149
150 if (skipLoad) {
151 loadState = LOAD_STATE_COMPLETED;
152 } else {
153 loadState = LOAD_STATE_PENDING;
154 component.setVisible(false); // not allowed to interact with it yet
155 component.setEnabled(false);
156 }
157 }
158
159 /**
160 * Chained constructor.
161 *
162 * @param source
163 * @param component
164 * @param minWidth
165 * @param maxWidth
166 * @param minHeight
167 * @param maxHeight
168 */
169 protected HeavyDutyInteractiveWidget(Text source, JComponent component,
170 int minWidth, int maxWidth, int minHeight, int maxHeight, int cacheDepth) {
171 this(source, component, minWidth, maxWidth, minHeight, maxHeight, cacheDepth, false);
172 }
173
174 /**
175 * Updates the percentage of the load. Should only call when in load phase.
176 *
177 * @param percent
178 * Must be between 0.0 and 1.0 inclusive for percentage. Or negative if indeterminate.
179 *
180 * @throws IllegalArgumentException
181 * If percent is larger than 1.0
182 *
183 * @throws IllegalStateException
184 * Load is not in progress
185 *
186 */
187 protected final void updateLoadPercentage(float percent)
188 {
189 if (percent > 1.0f)
190 throw new IllegalArgumentException("loadState is larger than 1.0");
191
192 else if (!isInLoadProgress() ||
193 eventDispatcher == null ||
194 !eventDispatcher.isAlive())
195 throw new IllegalStateException("Load is not in progress");
196
197 // Assuming that this is called from eventDispatcher.
198 eventDispatcher.setLoadState(percent, false);
199 }
200
201
202 /**
203 * @return The current load state.
204 */
205 protected float getLoadState()
206 {
207 return loadState;
208 }
209
210 /**
211 * @return True if in a loading phase.
212 */
213 protected boolean isInLoadProgress()
214 {
215 return loadState <= 1.0f;
216 }
217
218 /**
219 * Sets the message which is displayed to the users while loading
220 * or when a load has failed.
221 *
222 * @param message
223 * A short human readable decription of the content being loaded
224 * or describing the failure.
225 * If null then the default load message will be assigned
226 */
227 protected final void setLoadScreenMessage(String message) {
228 this.screenMessage = message;
229 if (this.screenMessage == null) {
230 this.screenMessage = DEFAULT_LOAD_MESSAGE;
231 }
232
233 // Re-render loading state
234 DisplayController.invalidateArea(getBounds());
235 DisplayController.requestRefresh(true);
236 }
237
238 @Override
239 public void paintInFreeSpace() {
240 if (loadState == LOAD_STATE_COMPLETED) super.paintInFreeSpace();
241 else paintLoadScreen(LOAD_SCREEN_COLOR_FREESPACE);
242 }
243
244 @Override
245 protected final void paintSwingWidget(Graphics2D g)
246 {
247 if (loadState == LOAD_STATE_COMPLETED) {
248 paintHeavyDutyWidget(g);
249 } else {
250 paintLoadScreen(LOAD_SCREEN_COLOR);
251 }
252
253 }
254
255 /** Can be overridden to provide custom drawing. */
256 protected void paintHeavyDutyWidget(Graphics2D g)
257 {
258 super.paintSwingWidget(g);
259 }
260
261 /**
262 * Renders the load bar / load messages
263 *
264 * @param g
265 */
266 private void paintLoadScreen(Colour backgroundColor) {
267
268 if (Browser._theBrowser == null) return;
269
270 SwingGraphicsManager manager = SwingMiscManager.getIfUsingSwingGraphicsManager();
271 Graphics g = manager.getCurrentSurface();
272
273 // Render shaded window over widget
274 g.setColor(SwingConversions.toSwingColor(backgroundColor));
275 final int x = getX();
276 final int y = getY();
277 final int width = getWidth();
278 final int height = getHeight();
279 g.fillRect(x, y, width, height);
280
281 // Center the bar
282 int barX = x + BAR_HOROZONTIAL_MARGIN;
283 int barY = y + (height >> 1) - (BAR_HEIGHT >> 1);
284
285 int barWidth = width - (BAR_HOROZONTIAL_MARGIN * 2);
286 if (barWidth <= 0) barWidth = 10;
287
288 // Center the text
289 Font f = (loadState < 0.0f) ? LOAD_INDERTMIN_FONT : LOAD_NORMAL_FONT;
290 String message = (loadState == LOAD_STATE_PENDING || loadState == LOAD_STATE_INCOMPLETED) ?
291 PENDING_MESSAGE : screenMessage;
292
293 g.setFont(f);
294
295 // If need to re-calc the message drawing area... do so
296
297 FontMetrics fm = g.getFontMetrics(f);
298 Rectangle2D rect = fm.getStringBounds(message, g);
299 int textHeight = (int)(rect.getHeight());
300 int textWidth = (int)(rect.getWidth());
301
302 int textX = barX + ((barWidth - textWidth) >> 1);
303 if (textX <= 0) textX = BAR_HOROZONTIAL_MARGIN + 10;
304 int textY = barY + ((BAR_HEIGHT - textHeight) >> 1);
305 if (textY <= 0) textY = barY + 2;
306 textY += textHeight;
307
308 // Ensure that load bar and text doesn't spill over widgets invalidation area
309 Shape clipBackUp = g.getClip();
310 Rectangle tmpClip = (clipBackUp != null) ? clipBackUp.getBounds() :
311 new Rectangle(0, 0,
312 manager.getContentPane().getWidth(),
313 manager.getContentPane().getHeight());
314
315 g.setClip(tmpClip.intersection(SwingConversions.toSwingRectangle(getBounds())));
316
317
318 if (loadState < 0.0f) { // indeterminant
319
320 GradientPaint gp = new GradientPaint(
321 0, barY + (int)(BAR_HEIGHT * 0.8), SwingConversions.toSwingColor(LOAD_BAR_INDETERM_COLOR),
322 0, barY, SwingConversions.toSwingColor(LOAD_BAR_HIGHLIGHT_COLOR));
323 ((Graphics2D)g).setPaint(gp);
324
325 g.fillRect(barX, barY, barWidth, BAR_HEIGHT);
326
327 } else if (isInLoadProgress()) {
328
329 int progBarWidth = (int)(barWidth * loadState);
330
331 GradientPaint gp = new GradientPaint(
332 0, barY + (int)(BAR_HEIGHT * 0.8), SwingConversions.toSwingColor(LOAD_BAR_PROGRESS_COLOR),
333 0, barY, SwingConversions.toSwingColor(LOAD_BAR_HIGHLIGHT_COLOR));
334 ((Graphics2D)g).setPaint(gp);
335
336 g.fillRect(barX, barY, progBarWidth, BAR_HEIGHT);
337
338 }
339
340 g.setColor(SwingConversions.toSwingColor(Colour.DARK_GREY));
341 g.drawRect(barX, barY, barWidth, BAR_HEIGHT);
342
343 if (loadState == LOAD_STATE_FAILED)
344 g.setColor(SwingConversions.toSwingColor(Colour.RED));
345
346 else g.setColor(SwingConversions.toSwingColor(Colour.BLACK));
347
348
349
350 g.drawString(message, textX, textY);
351
352 g.setClip(clipBackUp);
353
354 }
355
356
357
358 /**
359 * Invoked by the load queue manager only.
360 */
361 public final void performLoad() {
362
363 try {
364 runEventAndWait(HDWEvent.Load);
365 } catch (InterruptedException e) {
366 loadState = LOAD_STATE_INCOMPLETED; // safety
367 e.printStackTrace();
368 }
369 }
370
371 /**
372 * Invoked by load manager
373 */
374 public final void cancelLoadRequested() {
375 hasCancelledBeenRequested = true;
376 cancelLoadWidgetData();
377 }
378
379 /**
380 * @return
381 * True if cancel has been requested. This is reset before a new load phase.
382 */
383 protected boolean hasCancelBeenRequested() {
384 return hasCancelledBeenRequested;
385 }
386
387 /**
388 *
389 * @return
390 * True if this widget is in an expired state
391 */
392 protected boolean isExpired() {
393 return isExpired;
394 }
395
396 protected boolean isLoaded() {
397 return this.loadState == LOAD_STATE_COMPLETED;
398 }
399
400 /**
401 * Important: Must be called otherwise the widget states may become unstable.
402 */
403 @Override
404 public void onDelete() {
405 super.onDelete();
406 // Evenetually - unload the data - with the intention of possible recovery, but not saving...
407 queueEvent(HDWEvent.UnloadTMP);
408 }
409
410 /**
411 *
412 * Invoked when it is time to perform all asynchronous (heavey duty) loading for the widget.
413 *
414 * The convention is to load until cancelLoadWidgetData is invoked. Once this is invoked it is
415 * the widgets load logic's choice to whether to acknowledge it. Note that proceeding
416 * loadable entities must wait until this returns - so it is best to heed the as soon as possible.
417 *
418 * Use updateLoadPercentage to update the load bar and setLoadScreenMessage to set the message
419 * for load feedback to users. It will start with the default loading message and progress at 0%.
420 *
421 * @return
422 * The final load state. Must be either:
423 * <ul>
424 * <li>LOAD_STATE_COMPLETED
425 * <li>LOAD_STATE_FAILED
426 * <li>LOAD_STATE_INCOMPLETED
427 * </ul>
428 * If not, then LOAD_STATE_FAILED is assumed - and an exception trace will be printed.
429 */
430 protected abstract float loadWidgetData();
431
432 /**
433 * @see loadWidgetData, hasCancelBeenRequested
434 */
435 protected void cancelLoadWidgetData() {}
436
437 @Override
438 protected void onParentStateChanged(int eventType) {
439
440 switch (eventType) {
441
442 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED:
443 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY:
444 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN:
445 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY:
446
447 // When anchored to the window, then requeue for loading iff load state
448 // is currently pending or incomplete
449 if (hasCancelledBeenRequested ||
450 loadState == LOAD_STATE_INCOMPLETED ||
451 loadState == LOAD_STATE_PENDING) { // if needing to load - then load
452 EntityLoadManager.getInstance().queue(this, getLoadDelayTime());
453 }
454
455 // Ensure that registered for saving at next save point
456 EntitySaveManager.getInstance().register(this);
457
458 // Ensure is cached
459 WidgetCacheManager.cacheWidget(this);
460
461 break;
462
463
464 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED:
465 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY:
466 case ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN:
467
468 // Whenever the widget is not longer in view then cancel current loading proccess
469 // if currently loading. This must be performed later because this event occurs
470 // before the widget has had a chance to bee moved into free space.
471 SwingUtilities.invokeLater(new DoCancelLoad()); // proccess on this thread later.
472
473 // Always unregister from save point.
474 EntitySaveManager.getInstance().unregister(this);
475
476 // If the widget has been removed then unregister from caching
477 // So that if deleted then won't hang around in cache
478 if (eventType == ItemParentStateChangedEvent.EVENT_TYPE_REMOVED) {
479 WidgetCacheManager.uncacheWidget(this);
480 // If removed via overlay, then cached frames may still contain overlay with the widget...
481 }
482 break;
483 }
484 }
485
486 private class DoCancelLoad implements Runnable {
487 public void run() {
488
489 for (Item i : getItems()) { // Only cancel if the widget is not floating. Allow loading while in free space
490 if (FreeItems.getInstance().contains(i))
491 return;
492 }
493
494 // TODO: One problem with loading in free space is that there is no way of cancelling
495 // if the heavy duty widget is deleted in free space while loading.
496 // This is just an inefficiency.
497 EntityLoadManager.getInstance().cancel(HeavyDutyInteractiveWidget.this);
498
499 }
500 }
501
502 /**
503 * @return The time to delay before loading begin when first in view.
504 */
505 public abstract int getLoadDelayTime();
506
507 /**
508 * Invoked by save manager
509 */
510 public final void performSave() {
511 try {
512 runEventAndWait(HDWEvent.Save);
513 } catch (InterruptedException e) {
514 e.printStackTrace();
515 }
516 }
517
518
519 /**
520 * Called by dedicated thread to save data at a save point.
521 * See EntitySaveManager for more inforamtion about save points.
522 *
523 * Heavey duty widgets will only save at the save points if the widget
524 * still belongs to a frame and is anchored.
525 */
526 protected abstract void saveWidgetData();
527
528 /**
529 * Only invoked if has cache expiry and has expired. Only if this is no longer visible.
530 */
531 void expire() {
532 runEventLater(HDWEvent.Unload);
533 }
534
535 /**
536 * Invoked if this widget has a cache expiry and has been expired.
537 * The protocol is that you should unload anything in memory...
538 * This should be quick, e.g. setting references to null / disposing.
539 * Pupose is to free memory.
540 *
541 * Important note:
542 * It is possible for a widget to expire before it had a chance to be saved,
543 * or even when saving. Thus must be careful.
544 * A simple approach would be to check the saving flag
545 *
546 *
547 */
548 protected abstract void unloadWidgetData();
549
550 /**
551 * This is invoked asynronously when the item is deleted. This widget
552 * will be added to an undo list in memory.
553 *
554 * The intention is that the implementation should release its memory as if it were
555 * expired - But it will not be saved. Thus the data should be dumped to a temporary file
556 * so it can be recovered if it is recalled (un-deleted). Note that this is not the same as saving -
557 * the user would not expect the widget to save state if deleted.
558 *
559 * This is never invoke <i>while</i> loading / saving / expiring. The operations
560 * are mutually exclusive. It can be invoked when the widget is not yet loaded.
561 *
562 */
563 protected abstract void tempUnloadWidgetData();
564
565 /**
566 * The cache depth is measured by how many frames the user can traverse through until
567 * the widget should expire.
568 *
569 * This is immutable.
570 *
571 * @return
572 * The cache depth for this interactive widget. less or equal to zero for no cache expiry.
573 */
574 public final int getCacheDepth() {
575 return cacheDepth;
576 }
577
578 private synchronized void runEventAndWait(HDWEvent event) throws InterruptedException {
579 queueEvent(event);
580 eventDispatcher.join();
581 }
582
583 private void runEventLater(HDWEvent event) {
584 queueEvent(event);
585 }
586
587 private void queueEvent(HDWEvent event) {
588
589 synchronized(queuedEvents) {
590
591 if (!queuedEvents.contains(event))
592 queuedEvents.add(event);
593
594
595 if (eventDispatcher == null || !eventDispatcher.isAlive()) {
596 eventDispatcher = new HDWEventDispatcher();
597 eventDispatcher.start();
598 }
599
600 }
601
602 }
603
604
605 /**
606 * Unified state management. Load states are handled by one thread always.
607 * Only has one instance - per widget instance, of this at any given time,
608 *
609 * @author Brook
610 *
611 */
612 private class HDWEventDispatcher extends Thread {
613
614 /**
615 * @param loadState
616 * Can be any of the following values.
617 * <ul>
618 * <li>PENDING_STATE if pending.
619 * <li>COMPLETED_STATE if loaded.
620 * <li>between 0.0f and 1.0f inclusive if loading: represents progress percent complete.
621 * <li>FAILED_STATE if failed.
622 * <li>INCOMPLETED_STATE if load was unable to complete.
623 * <li>negative if loading but indeterminant.
624 * </ul>
625 *
626 */
627 private void setLoadState(float state, boolean expired) {
628 assert (state == LOAD_STATE_FAILED || state != LOAD_STATE_INCOMPLETED ||
629 state != LOAD_STATE_PENDING || state != LOAD_STATE_COMPLETED ||
630 state < 1.0f) ;
631
632 assert (!expired ||
633 (expired && state == LOAD_STATE_PENDING));
634
635
636 isExpired = expired;
637 loadState = state;
638
639 if (loadState == LOAD_STATE_COMPLETED) { // set enabled state - show the swing components
640 SwingUtilities.invokeLater(new Runnable() {
641 public void run() {
642 _swingComponent.setVisible(true);
643 _swingComponent.setEnabled(true);
644 }
645 });
646
647 } else if(expired) { // disable/hide swing GUI when expires, like a reset
648
649 SwingUtilities.invokeLater(new Runnable() {
650 public void run() {
651 _swingComponent.setVisible(false);
652 _swingComponent.setEnabled(false);
653 }
654 });
655 }
656
657 // Re-render loading state - if not expired
658 if (!expired) {
659 DisplayController.invalidateArea(getBounds());
660 DisplayController.requestRefresh(true);
661 }
662 }
663
664 /**
665 * All state transitions performed by one thread.
666 */
667 public void run() {
668
669 while (true) { // keep processing tasks
670
671 HDWEvent event = null;
672
673 synchronized(queuedEvents) {
674 if (queuedEvents.isEmpty()) return;
675 event = queuedEvents.remove();
676 }
677
678 if (event == HDWEvent.Load) {
679 doLoad();
680 } else if (event == HDWEvent.Save) {
681 doSave(); // does not change state
682 } else if (event == HDWEvent.Unload){
683 doUnload();
684 } else {
685 assert(event == HDWEvent.UnloadTMP);
686 doTempUnload();
687 }
688
689
690 }
691 }
692
693 private void doLoad() {
694
695 // Check that not already loaded.
696 // if (loadState == LOAD_STATE_COMPLETED ||
697 // loadState == LOAD_STATE_FAILED) return;
698 if (loadState == LOAD_STATE_COMPLETED) return;
699
700 // Only load if in view
701 if (!isFloating() && getParentFrame() != DisplayController.getCurrentFrame())
702 return;
703
704 // Reset flag.
705 hasCancelledBeenRequested = false;
706
707 // Set the load state as loading... 0%
708 setLoadState(0.0f, false);
709
710 float finalState = LOAD_STATE_FAILED;
711
712 try {
713 finalState = loadWidgetData();
714 } catch (Exception e) {
715 e.printStackTrace();
716 }
717
718 // Safety check for return state
719 try {
720 if (finalState != LOAD_STATE_COMPLETED
721 && finalState != LOAD_STATE_FAILED
722 && finalState != LOAD_STATE_INCOMPLETED) {
723 throw new Exception("ERROR: Bad return state: " + finalState);
724 }
725 } catch (Exception e) {
726 e.printStackTrace();
727 finalState = LOAD_STATE_FAILED;
728 }
729
730 // Set the final state
731 setLoadState(finalState, false);
732
733 }
734
735 private void doSave() {
736
737 // Only save if still belongs to a frame
738 if (!isFloating() && getParentFrame() != null) {
739 saveWidgetData();
740 }
741
742 }
743
744 private void doUnload() {
745
746 // Reset the load state
747 setLoadState(LOAD_STATE_PENDING, true);
748
749 // Get rid of memory
750 unloadWidgetData();
751
752 }
753
754 private void doTempUnload() {
755 // Reset the load state
756 setLoadState(LOAD_STATE_PENDING, true);
757
758 // Get rid of memory
759 tempUnloadWidgetData();
760 }
761
762
763
764 }
765
766 private enum HDWEvent {
767 Save,
768 Load,
769 Unload,
770 UnloadTMP
771 }
772
773
774}
Note: See TracBrowser for help on using the repository browser.