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

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

File size: 22.4 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 g.fillRect(getX(), getY(), getWidth(), getHeight());
276
277 // Center the bar
278 int barX = getX() + BAR_HOROZONTIAL_MARGIN;
279 int barY = getY() + (getHeight() >> 1) - (BAR_HEIGHT >> 1);
280
281 int barWidth = getWidth() - (BAR_HOROZONTIAL_MARGIN * 2);
282 if (barWidth <= 0) barWidth = 10;
283
284 // Center the text
285 Font f = (loadState < 0.0f) ? LOAD_INDERTMIN_FONT : LOAD_NORMAL_FONT;
286 String message = (loadState == LOAD_STATE_PENDING || loadState == LOAD_STATE_INCOMPLETED) ?
287 PENDING_MESSAGE : screenMessage;
288
289 g.setFont(f);
290
291 // If need to re-calc the message drawing area... do so
292
293 FontMetrics fm = g.getFontMetrics(f);
294 Rectangle2D rect = fm.getStringBounds(message, g);
295 int textHeight = (int)(rect.getHeight());
296 int textWidth = (int)(rect.getWidth());
297
298 int textX = barX + ((barWidth - textWidth) >> 1);
299 if (textX <= 0) textX = BAR_HOROZONTIAL_MARGIN + 10;
300 int textY = barY + ((BAR_HEIGHT - textHeight) >> 1);
301 if (textY <= 0) textY = barY + 2;
302 textY += textHeight;
303
304 // Ensure that load bar and text doesn't spill over widgets invalidation area
305 Shape clipBackUp = g.getClip();
306 Rectangle tmpClip = (clipBackUp != null) ? clipBackUp.getBounds() :
307 new Rectangle(0, 0,
308 manager.getContentPane().getWidth(),
309 manager.getContentPane().getHeight());
310
311 g.setClip(tmpClip.intersection(SwingConversions.toSwingRectangle(getBounds())));
312
313
314 if (loadState < 0.0f) { // indeterminant
315
316 GradientPaint gp = new GradientPaint(
317 0, barY + (int)(BAR_HEIGHT * 0.8), SwingConversions.toSwingColor(LOAD_BAR_INDETERM_COLOR),
318 0, barY, SwingConversions.toSwingColor(LOAD_BAR_HIGHLIGHT_COLOR));
319 ((Graphics2D)g).setPaint(gp);
320
321 g.fillRect(barX, barY, barWidth, BAR_HEIGHT);
322
323 } else if (isInLoadProgress()) {
324
325 int progBarWidth = (int)(barWidth * loadState);
326
327 GradientPaint gp = new GradientPaint(
328 0, barY + (int)(BAR_HEIGHT * 0.8), SwingConversions.toSwingColor(LOAD_BAR_PROGRESS_COLOR),
329 0, barY, SwingConversions.toSwingColor(LOAD_BAR_HIGHLIGHT_COLOR));
330 ((Graphics2D)g).setPaint(gp);
331
332 g.fillRect(barX, barY, progBarWidth, BAR_HEIGHT);
333
334 }
335
336 g.setColor(SwingConversions.toSwingColor(Colour.DARK_GREY));
337 g.drawRect(barX, barY, barWidth, BAR_HEIGHT);
338
339 if (loadState == LOAD_STATE_FAILED)
340 g.setColor(SwingConversions.toSwingColor(Colour.RED));
341
342 else g.setColor(SwingConversions.toSwingColor(Colour.BLACK));
343
344
345
346 g.drawString(message, textX, textY);
347
348 g.setClip(clipBackUp);
349
350 }
351
352
353
354 /**
355 * Invoked by the load queue manager only.
356 */
357 public final void performLoad() {
358
359 try {
360 runEventAndWait(HDWEvent.Load);
361 } catch (InterruptedException e) {
362 loadState = LOAD_STATE_INCOMPLETED; // safety
363 e.printStackTrace();
364 }
365 }
366
367 /**
368 * Invoked by load manager
369 */
370 public final void cancelLoadRequested() {
371 hasCancelledBeenRequested = true;
372 cancelLoadWidgetData();
373 }
374
375 /**
376 * @return
377 * True if cancel has been requested. This is reset before a new load phase.
378 */
379 protected boolean hasCancelBeenRequested() {
380 return hasCancelledBeenRequested;
381 }
382
383 /**
384 *
385 * @return
386 * True if this widget is in an expired state
387 */
388 protected boolean isExpired() {
389 return isExpired;
390 }
391
392 protected boolean isLoaded() {
393 return this.loadState == LOAD_STATE_COMPLETED;
394 }
395
396 /**
397 * Important: Must be called otherwise the widget states may become unstable.
398 */
399 @Override
400 public void onDelete() {
401 super.onDelete();
402 // Evenetually - unload the data - with the intention of possible recovery, but not saving...
403 queueEvent(HDWEvent.UnloadTMP);
404 }
405
406 /**
407 *
408 * Invoked when it is time to perform all asynchronous (heavey duty) loading for the widget.
409 *
410 * The convention is to load until cancelLoadWidgetData is invoked. Once this is invoked it is
411 * the widgets load logic's choice to whether to acknowledge it. Note that proceeding
412 * loadable entities must wait until this returns - so it is best to heed the as soon as possible.
413 *
414 * Use updateLoadPercentage to update the load bar and setLoadScreenMessage to set the message
415 * for load feedback to users. It will start with the default loading message and progress at 0%.
416 *
417 * @return
418 * The final load state. Must be either:
419 * <ul>
420 * <li>LOAD_STATE_COMPLETED
421 * <li>LOAD_STATE_FAILED
422 * <li>LOAD_STATE_INCOMPLETED
423 * </ul>
424 * If not, then LOAD_STATE_FAILED is assumed - and an exception trace will be printed.
425 */
426 protected abstract float loadWidgetData();
427
428 /**
429 * @see loadWidgetData, hasCancelBeenRequested
430 */
431 protected void cancelLoadWidgetData() {}
432
433 @Override
434 protected void onParentStateChanged(int eventType) {
435
436 switch (eventType) {
437
438 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED:
439 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY:
440 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN:
441 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY:
442
443 // When anchored to the window, then requeue for loading iff load state
444 // is currently pending or incomplete
445 if (hasCancelledBeenRequested ||
446 loadState == LOAD_STATE_INCOMPLETED ||
447 loadState == LOAD_STATE_PENDING) { // if needing to load - then load
448 EntityLoadManager.getInstance().queue(this, getLoadDelayTime());
449 }
450
451 // Ensure that registered for saving at next save point
452 EntitySaveManager.getInstance().register(this);
453
454 // Ensure is cached
455 WidgetCacheManager.cacheWidget(this);
456
457 break;
458
459
460 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED:
461 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY:
462 case ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN:
463
464 // Whenever the widget is not longer in view then cancel current loading proccess
465 // if currently loading. This must be performed later because this event occurs
466 // before the widget has had a chance to bee moved into free space.
467 SwingUtilities.invokeLater(new DoCancelLoad()); // proccess on this thread later.
468
469 // Always unregister from save point.
470 EntitySaveManager.getInstance().unregister(this);
471
472 // If the widget has been removed then unregister from caching
473 // So that if deleted then won't hang around in cache
474 if (eventType == ItemParentStateChangedEvent.EVENT_TYPE_REMOVED) {
475 WidgetCacheManager.uncacheWidget(this);
476 // If removed via overlay, then cached frames may still contain overlay with the widget...
477 }
478 break;
479 }
480 }
481
482 private class DoCancelLoad implements Runnable {
483 public void run() {
484
485 for (Item i : getItems()) { // Only cancel if the widget is not floating. Allow loading while in free space
486 if (FreeItems.getInstance().contains(i))
487 return;
488 }
489
490 // TODO: One problem with loading in free space is that there is no way of cancelling
491 // if the heavy duty widget is deleted in free space while loading.
492 // This is just an inefficiency.
493 EntityLoadManager.getInstance().cancel(HeavyDutyInteractiveWidget.this);
494
495 }
496 }
497
498 /**
499 * @return The time to delay before loading begin when first in view.
500 */
501 public abstract int getLoadDelayTime();
502
503 /**
504 * Invoked by save manager
505 */
506 public final void performSave() {
507 try {
508 runEventAndWait(HDWEvent.Save);
509 } catch (InterruptedException e) {
510 e.printStackTrace();
511 }
512 }
513
514
515 /**
516 * Called by dedicated thread to save data at a save point.
517 * See EntitySaveManager for more inforamtion about save points.
518 *
519 * Heavey duty widgets will only save at the save points if the widget
520 * still belongs to a frame and is anchored.
521 */
522 protected abstract void saveWidgetData();
523
524 /**
525 * Only invoked if has cache expiry and has expired. Only if this is no longer visible.
526 */
527 void expire() {
528 runEventLater(HDWEvent.Unload);
529 }
530
531 /**
532 * Invoked if this widget has a cache expiry and has been expired.
533 * The protocol is that you should unload anything in memory...
534 * This should be quick, e.g. setting references to null / disposing.
535 * Pupose is to free memory.
536 *
537 * Important note:
538 * It is possible for a widget to expire before it had a chance to be saved,
539 * or even when saving. Thus must be careful.
540 * A simple approach would be to check the saving flag
541 *
542 *
543 */
544 protected abstract void unloadWidgetData();
545
546 /**
547 * This is invoked asynronously when the item is deleted. This widget
548 * will be added to an undo list in memory.
549 *
550 * The intention is that the implementation should release its memory as if it were
551 * expired - But it will not be saved. Thus the data should be dumped to a temporary file
552 * so it can be recovered if it is recalled (un-deleted). Note that this is not the same as saving -
553 * the user would not expect the widget to save state if deleted.
554 *
555 * This is never invoke <i>while</i> loading / saving / expiring. The operations
556 * are mutually exclusive. It can be invoked when the widget is not yet loaded.
557 *
558 */
559 protected abstract void tempUnloadWidgetData();
560
561 /**
562 * The cache depth is measured by how many frames the user can traverse through until
563 * the widget should expire.
564 *
565 * This is immutable.
566 *
567 * @return
568 * The cache depth for this interactive widget. less or equal to zero for no cache expiry.
569 */
570 public final int getCacheDepth() {
571 return cacheDepth;
572 }
573
574 private synchronized void runEventAndWait(HDWEvent event) throws InterruptedException {
575 queueEvent(event);
576 eventDispatcher.join();
577 }
578
579 private void runEventLater(HDWEvent event) {
580 queueEvent(event);
581 }
582
583 private void queueEvent(HDWEvent event) {
584
585 synchronized(queuedEvents) {
586
587 if (!queuedEvents.contains(event))
588 queuedEvents.add(event);
589
590
591 if (eventDispatcher == null || !eventDispatcher.isAlive()) {
592 eventDispatcher = new HDWEventDispatcher();
593 eventDispatcher.start();
594 }
595
596 }
597
598 }
599
600
601 /**
602 * Unified state management. Load states are handled by one thread always.
603 * Only has one instance - per widget instance, of this at any given time,
604 *
605 * @author Brook
606 *
607 */
608 private class HDWEventDispatcher extends Thread {
609
610 /**
611 * @param loadState
612 * Can be any of the following values.
613 * <ul>
614 * <li>PENDING_STATE if pending.
615 * <li>COMPLETED_STATE if loaded.
616 * <li>between 0.0f and 1.0f inclusive if loading: represents progress percent complete.
617 * <li>FAILED_STATE if failed.
618 * <li>INCOMPLETED_STATE if load was unable to complete.
619 * <li>negative if loading but indeterminant.
620 * </ul>
621 *
622 */
623 private void setLoadState(float state, boolean expired) {
624 assert (state == LOAD_STATE_FAILED || state != LOAD_STATE_INCOMPLETED ||
625 state != LOAD_STATE_PENDING || state != LOAD_STATE_COMPLETED ||
626 state < 1.0f) ;
627
628 assert (!expired ||
629 (expired && state == LOAD_STATE_PENDING));
630
631
632 isExpired = expired;
633 loadState = state;
634
635 if (loadState == LOAD_STATE_COMPLETED) { // set enabled state - show the swing components
636 SwingUtilities.invokeLater(new Runnable() {
637 public void run() {
638 _swingComponent.setVisible(true);
639 _swingComponent.setEnabled(true);
640 }
641 });
642
643 } else if(expired) { // disable/hide swing GUI when expires, like a reset
644
645 SwingUtilities.invokeLater(new Runnable() {
646 public void run() {
647 _swingComponent.setVisible(false);
648 _swingComponent.setEnabled(false);
649 }
650 });
651 }
652
653 // Re-render loading state - if not expired
654 if (!expired) {
655 DisplayController.invalidateArea(getBounds());
656 DisplayController.requestRefresh(true);
657 }
658 }
659
660 /**
661 * All state transitions performed by one thread.
662 */
663 public void run() {
664
665 while (true) { // keep processing tasks
666
667 HDWEvent event = null;
668
669 synchronized(queuedEvents) {
670 if (queuedEvents.isEmpty()) return;
671 event = queuedEvents.remove();
672 }
673
674 if (event == HDWEvent.Load) {
675 doLoad();
676 } else if (event == HDWEvent.Save) {
677 doSave(); // does not change state
678 } else if (event == HDWEvent.Unload){
679 doUnload();
680 } else {
681 assert(event == HDWEvent.UnloadTMP);
682 doTempUnload();
683 }
684
685
686 }
687 }
688
689 private void doLoad() {
690
691 // Check that not already loaded.
692 // if (loadState == LOAD_STATE_COMPLETED ||
693 // loadState == LOAD_STATE_FAILED) return;
694 if (loadState == LOAD_STATE_COMPLETED) return;
695
696 // Only load if in view
697 if (!isFloating() && getParentFrame() != DisplayController.getCurrentFrame())
698 return;
699
700 // Reset flag.
701 hasCancelledBeenRequested = false;
702
703 // Set the load state as loading... 0%
704 setLoadState(0.0f, false);
705
706 float finalState = LOAD_STATE_FAILED;
707
708 try {
709 finalState = loadWidgetData();
710 } catch (Exception e) {
711 e.printStackTrace();
712 }
713
714 // Safety check for return state
715 try {
716 if (finalState != LOAD_STATE_COMPLETED
717 && finalState != LOAD_STATE_FAILED
718 && finalState != LOAD_STATE_INCOMPLETED) {
719 throw new Exception("ERROR: Bad return state: " + finalState);
720 }
721 } catch (Exception e) {
722 e.printStackTrace();
723 finalState = LOAD_STATE_FAILED;
724 }
725
726 // Set the final state
727 setLoadState(finalState, false);
728
729 }
730
731 private void doSave() {
732
733 // Only save if still belongs to a frame
734 if (!isFloating() && getParentFrame() != null) {
735 saveWidgetData();
736 }
737
738 }
739
740 private void doUnload() {
741
742 // Reset the load state
743 setLoadState(LOAD_STATE_PENDING, true);
744
745 // Get rid of memory
746 unloadWidgetData();
747
748 }
749
750 private void doTempUnload() {
751 // Reset the load state
752 setLoadState(LOAD_STATE_PENDING, true);
753
754 // Get rid of memory
755 tempUnloadWidgetData();
756 }
757
758
759
760 }
761
762 private enum HDWEvent {
763 Save,
764 Load,
765 Unload,
766 UnloadTMP
767 }
768
769
770}
Note: See TracBrowser for help on using the repository browser.