From b35417acb62a9037de64772b2852b4840ade5452 Mon Sep 17 00:00:00 2001 From: Roger Ferrel Date: Wed, 3 Sep 2014 09:17:34 -0500 Subject: [PATCH] Issue #3516 - Get Color Maps for levels off the dispatch thread. Change-Id: Icc89f4860eef0b0b404e1e5b66bb0178eedd73dc Former-commit-id: 246bc4770f4ce023e8728f92dc96b86c26bb2acc [formerly c885873b25c51606eed351c0de3066b12df35683 [formerly a67a429eb2d266e66a99b6c18916193cd0d20d9d]] Former-commit-id: c885873b25c51606eed351c0de3066b12df35683 Former-commit-id: fffec40c30a2f0227907e0e80e8f5ec85f12a6b0 --- .../core/drawables/ColorMapTreeFactory.java | 95 ----- .../gfe/actions/ChangeColorTableAction.java | 3 + cave/com.raytheon.viz.ui/META-INF/MANIFEST.MF | 1 + .../viz/ui/colormap}/ColorMapTree.java | 35 +- .../viz/ui/colormap/ColorMapTreeFactory.java | 327 ++++++++++++++++++ .../viz/ui/colormap/ILevelMapsCallback.java | 52 +++ .../IRefreshColorMapTreeListener.java | 46 +++ .../raytheon/viz/ui/dialogs/ColormapComp.java | 191 +++++++--- 8 files changed, 598 insertions(+), 152 deletions(-) delete mode 100644 cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ColorMapTreeFactory.java rename cave/{com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables => com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap}/ColorMapTree.java (88%) create mode 100644 cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ColorMapTreeFactory.java create mode 100644 cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ILevelMapsCallback.java create mode 100644 cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/IRefreshColorMapTreeListener.java diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ColorMapTreeFactory.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ColorMapTreeFactory.java deleted file mode 100644 index bfca3db606..0000000000 --- a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ColorMapTreeFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * This software was developed and / or modified by Raytheon Company, - * pursuant to Contract DG133W-05-CQ-1067 with the US Government. - * - * U.S. EXPORT CONTROLLED TECHNICAL DATA - * This software product contains export-restricted data whose - * export/transfer/disclosure is restricted by U.S. law. Dissemination - * to non-U.S. persons whether in the United States or abroad requires - * an export license or other authorization. - * - * Contractor Name: Raytheon Company - * Contractor Address: 6825 Pine Street, Suite 340 - * Mail Stop B8 - * Omaha, NE 68106 - * 402.291.0100 - * - * See the AWIPS II Master Rights File ("Master Rights File.pdf") for - * further licensing information. - **/ -package com.raytheon.uf.viz.core.drawables; - -import java.util.HashMap; -import java.util.Map; - -import com.raytheon.uf.common.localization.IPathManager; -import com.raytheon.uf.common.localization.LocalizationContext; -import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel; -import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; -import com.raytheon.uf.common.localization.PathManagerFactory; - -/** - * Factory which can provide cached versions of {@link ColorMapTree} objects. - * - *
- * 
- * SOFTWARE HISTORY
- * 
- * Date          Ticket#  Engineer    Description
- * ------------- -------- ----------- --------------------------
- * Sep 18, 2013  2421     bsteffen    Initial creation
- * 
- * 
- * - * @author bsteffen - * @version 1.0 - */ - -public class ColorMapTreeFactory { - - private static ColorMapTree baseTree; - - private static Object baseTreeLock = new Object(); - - private static final Map treesByLevel = new HashMap(); - - private static Object treesByLevelLock = new Object(); - - /** - * Get a tree for the BASE localization context. This tree will be different - * from the tree returned by getTreeForLevel(LocalizationLevel.BASE) because - * it will not be for the BASE level but instead is for the BASE context. - * - */ - public static ColorMapTree getBaseTree() { - synchronized (baseTreeLock) { - if(baseTree == null){ - IPathManager pm = PathManagerFactory.getPathManager(); - LocalizationContext baseContext = pm.getContext( - LocalizationType.COMMON_STATIC, LocalizationLevel.BASE); - baseTree = new ColorMapTree(pm, baseContext, - ColorMapLoader.DIR_NAME); - } - return baseTree; - } - - } - - /** - * Return a {@link ColorMapTree}Tree for the provided level. The tree will - * have the same name as the level and will have a subtree for each context - * that exists at that level. - */ - public static ColorMapTree getTreeForLevel(LocalizationLevel level) { - synchronized (treesByLevelLock) { - ColorMapTree tree = treesByLevel.get(level); - if (tree == null) { - IPathManager pm = PathManagerFactory.getPathManager(); - tree = new ColorMapTree(pm, level, ColorMapLoader.DIR_NAME); - treesByLevel.put(level, tree); - } - return tree; - } - } - -} \ No newline at end of file diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/actions/ChangeColorTableAction.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/actions/ChangeColorTableAction.java index 7d9c2bcc24..136c96fa18 100644 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/actions/ChangeColorTableAction.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/actions/ChangeColorTableAction.java @@ -24,6 +24,7 @@ import org.eclipse.jface.action.IMenuCreator; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; import com.raytheon.uf.common.dataplugin.gfe.db.objects.GFERecord.GridType; @@ -43,6 +44,7 @@ import com.raytheon.viz.ui.dialogs.ColormapComp; * ------------ ---------- ----------- -------------------------- * Apr 8, 2009 njensen Initial creation * Aug 11, 2010 wldougher + * Aug 28, 2014 3516 rferrel Add separator after color map component. * * * @@ -120,6 +122,7 @@ public class ChangeColorTableAction extends AbstractRightClickAction implements // Build a ColormapComp to get the menu from ColormapComp comp = new ColormapComp(parent, parms, cap); menu = comp.getMenu(); + new MenuItem(menu, SWT.SEPARATOR); } ActionContributionItem aci = new ActionContributionItem( diff --git a/cave/com.raytheon.viz.ui/META-INF/MANIFEST.MF b/cave/com.raytheon.viz.ui/META-INF/MANIFEST.MF index 3a8d021d9a..6a387dc6fd 100644 --- a/cave/com.raytheon.viz.ui/META-INF/MANIFEST.MF +++ b/cave/com.raytheon.viz.ui/META-INF/MANIFEST.MF @@ -24,6 +24,7 @@ Export-Package: com.raytheon.viz.ui, com.raytheon.viz.ui.actions, com.raytheon.viz.ui.cmenu, com.raytheon.viz.ui.color, + com.raytheon.viz.ui.colormap, com.raytheon.viz.ui.dialogs, com.raytheon.viz.ui.dialogs.colordialog, com.raytheon.viz.ui.editor, diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ColorMapTree.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ColorMapTree.java similarity index 88% rename from cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ColorMapTree.java rename to cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ColorMapTree.java index bfb2895859..f703343a51 100644 --- a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ColorMapTree.java +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ColorMapTree.java @@ -17,7 +17,7 @@ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ -package com.raytheon.uf.viz.core.drawables; +package com.raytheon.viz.ui.colormap; import java.lang.ref.Reference; import java.lang.ref.WeakReference; @@ -33,6 +33,7 @@ import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; import com.raytheon.uf.common.localization.LocalizationFile; import com.raytheon.uf.common.localization.LocalizationNotificationObserver; +import com.raytheon.uf.viz.core.drawables.ColorMapLoader; /** * ColorMapTree represents the directory structure of colormaps directory. The @@ -46,6 +47,8 @@ import com.raytheon.uf.common.localization.LocalizationNotificationObserver; * Date Ticket# Engineer Description * ------------- -------- ----------- -------------------------- * Sep 18, 2013 2421 bsteffen Initial creation + * Sep 11, 2014 3516 rferrel file updates now inform the factory. + * getName() no longer returns a null. * * * @@ -110,7 +113,11 @@ public class ColorMapTree { } else { int start = path.lastIndexOf(IPathManager.SEPARATOR); if (start <= 0) { - return context.getContextName(); + String name = context.getContextName(); + if (name == null) { + name = context.getLocalizationLevel().name(); + } + return name; } return path.substring(start + 1); } @@ -181,15 +188,22 @@ public class ColorMapTree { } /** - * Optimize the internal structure so future {@link #isEmpty()} calls are - * fast. isEmpty() is a slow operations on trees with many empty subtrees, - * so this can be called in the background to enable faster calls to isEmpty - * when it is needed. In cases where isEmpty does not need extra data or is - * already optimized this call should complete very quickly. + * Recursively optimize the internal structure so future {@link #isEmpty()} + * calls are fast. isEmpty() is a slow operations on trees with many empty + * subtrees, so this can be called in the background to enable faster calls + * to isEmpty when it is needed. In cases where isEmpty does not need extra + * data or is already optimized this call should complete very quickly. + * Intended for use on non-UI thread. */ public void optimizeIsEmpty() { - /* isEmpty caches data in the subtrees so nothing else is needed. */ isEmpty(); + + /* + * isEmpty() may not check all sub trees. Force check of all sub trees. + */ + for (ColorMapTree subTree : getSubTrees()) { + subTree.optimizeIsEmpty(); + } } /** @@ -258,10 +272,9 @@ public class ColorMapTree { .removeGlobalFileChangeObserver(this); } else { tree.handleUpdate(message); + tree.optimizeIsEmpty(); + ColorMapTreeFactory.getInstance().refresh(); } - } - } - } diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ColorMapTreeFactory.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ColorMapTreeFactory.java new file mode 100644 index 0000000000..6e69f88034 --- /dev/null +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ColorMapTreeFactory.java @@ -0,0 +1,327 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.viz.ui.colormap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; + +import com.raytheon.uf.common.localization.IPathManager; +import com.raytheon.uf.common.localization.LocalizationContext; +import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel; +import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; +import com.raytheon.uf.common.localization.PathManagerFactory; +import com.raytheon.uf.viz.core.VizApp; +import com.raytheon.uf.viz.core.drawables.ColorMapLoader; + +/** + * Factory which can provide cached versions of {@link ColorMapTree} objects. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date          Ticket#  Engineer    Description
+ * ------------- -------- ----------- --------------------------
+ * Sep 18, 2013  2421     bsteffen    Initial creation
+ * Aug 28, 2014  3516     rferrel     Getting treesByLevel no longer
+ *                                     on the UI thread.
+ *                                    Converted to singleton.
+ *                                    Added localized file observer.
+ * 
+ * 
+ * + * @author bsteffen + * @version 1.0 + */ + +public class ColorMapTreeFactory { + + /** + * The only allowed instance of this class. + */ + private final static ColorMapTreeFactory instance = new ColorMapTreeFactory(); + + /** + * + * @return instance + */ + public static ColorMapTreeFactory getInstance() { + return instance; + } + + /** + * BASE localization tree must be handled differently from the other + * localization levels. Its items are placed directly on the top menu while + * the other levels are in their own pop up menu. + */ + private ColorMapTree baseTree; + + /** + * Lock when working on baseTree. + */ + private Object baseTreeLock = new Object(); + + /** + * Trees for non-BASE localization levels. + */ + private final Map treesByLevel = new HashMap(); + + /** + * Listeners needing treesByLevel information + */ + private List tlcListeners = new ArrayList(); + + /** + * Non-BASE localization levels. + */ + private final LocalizationLevel[] treesLevelLocalization; + + /** + * Thread safe list of listeners to notify when localized colormaps + * directories have a file change. Since the list is modified more often + * then it is traversed CopyOnWriterArrayList is not recommended. + * + */ + private final List refreshListeners = Collections + .synchronizedList(new ArrayList()); + + /** + * Singleton constructor. + */ + private ColorMapTreeFactory() { + IPathManager pm = PathManagerFactory.getPathManager(); + LocalizationLevel[] allLevels = pm.getAvailableLevels(); + // Remove BASE + treesLevelLocalization = Arrays.copyOfRange(allLevels, 1, + allLevels.length); + + // Start creating trees off the UI-thread + Job job = new Job("Find ColorMapTree base") { + + @Override + protected IStatus run(IProgressMonitor monitor) { + ColorMapTree base = getBaseTree(); + base.optimizeIsEmpty(); + return Status.OK_STATUS; + } + + }; + job.setSystem(true); + job.schedule(); + initTreeByLevel(); + } + + /** + * Get a tree for the BASE localization context. This tree is treated + * differently from the other localization context trees. The tree items are + * placed directly on the top menu while the other level trees are put in + * their own pop up menu. + */ + public ColorMapTree getBaseTree() { + synchronized (baseTreeLock) { + if (baseTree == null) { + IPathManager pm = PathManagerFactory.getPathManager(); + LocalizationContext baseContext = pm.getContext( + LocalizationType.COMMON_STATIC, LocalizationLevel.BASE); + + /* + * Useful for testing delay in getting base tree. + */ + // try { + // baseTreeLock.wait(8000L); + // } catch (InterruptedException e) { + // e.printStackTrace(); + // } + + baseTree = new ColorMapTree(pm, baseContext, + ColorMapLoader.DIR_NAME); + } + return baseTree; + } + } + + /** + * This immediately performs the call back for each non-BASE localization + * level sending a null tree when a level's tree has not yet been created. + * This allows the call back to generate a place holder for the level. When + * needed the call back routine will be called as any missing trees are + * generated. + * + * @param listener + */ + public void updateLevelMapsCallack(ILevelMapsCallback listener) { + synchronized (treesByLevel) { + + /* + * Immediately perform the action for all levels. When treeByLevel + * entry is null the tree for the level has not yet been created; + * invoke the listener with a null tree. This allows a listener + * creating menu items to generate a place holder entry such as + * "USER ???". + */ + for (LocalizationLevel level : getTreesLevelLocalization()) { + ColorMapTree tree = treesByLevel.get(level); + listener.treeCreated(level, tree); + } + + /* + * Still missing trees. Must call the callback again as each missing + * tree is created. + */ + if (isMissingLevelTrees()) { + tlcListeners.add(listener); + } + } + } + + /** + * + * @return true if still generating trees + */ + private boolean isMissingLevelTrees() { + return tlcListeners != null; + } + + /** + * Return a {@link ColorMapTree}Tree for the provided level. The tree will + * have the same name as the level and will have a subtree for each context + * that exists at that level. + */ + public ColorMapTree getTreeForLevel(LocalizationLevel level) { + synchronized (treesByLevel) { + ColorMapTree tree = treesByLevel.get(level); + if (tree == null) { + IPathManager pm = PathManagerFactory.getPathManager(); + tree = new ColorMapTree(pm, level, ColorMapLoader.DIR_NAME); + treesByLevel.put(level, tree); + } + return tree; + } + } + + /** + * + * @return treesLevelLocalization + */ + public LocalizationLevel[] getTreesLevelLocalization() { + return treesLevelLocalization; + } + + /** + * This creates the trees for treesByLevel off of the UI thread. It informs + * all listeners when a tree is created. + */ + private void initTreeByLevel() { + synchronized (treesByLevel) { + treesByLevel.clear(); + + Job job = new Job("Finding Colormaps") { + + @Override + protected IStatus run(IProgressMonitor monitor) { + IPathManager pm = PathManagerFactory.getPathManager(); + LocalizationLevel[] levels = getTreesLevelLocalization(); + for (final LocalizationLevel level : levels) { + ColorMapTree tree = new ColorMapTree(pm, level, + ColorMapLoader.DIR_NAME); + tree.optimizeIsEmpty(); + + synchronized (treesByLevel) { + /* + * Use for debugging. Simulates a long delay in + * getting tree level's color map. Allows testing to + * see if menu items for the tree levels are + * properly handled. + */ + // try { + // treesByLevel.wait(3000L); + // } catch (InterruptedException e) { + // e.printStackTrace(); + // } + + treesByLevel.put(level, tree); + for (ILevelMapsCallback listener : tlcListeners) { + listener.treeCreated(level, tree); + } + } + } + + synchronized (treesByLevel) { + /* + * Free up tlcListeners space and indicate all level + * trees are now generated. + */ + tlcListeners.clear(); + tlcListeners = null; + } + return Status.OK_STATUS; + } + }; + job.schedule(); + } + } + + /** + * Method used to inform listeners of any changes to any ColorMapTree. + * + * @param level + */ + public void refresh() { + + VizApp.runAsync(new Runnable() { + + @Override + public void run() { + IRefreshColorMapTreeListener[] rListeners = refreshListeners + .toArray(new IRefreshColorMapTreeListener[0]); + for (IRefreshColorMapTreeListener listener : rListeners) { + listener.refreshColorMapTree(); + } + } + }); + } + + /** + * Thread safe removal of listener. + * + * @param listener + */ + public void removeRefreshItemsListener(IRefreshColorMapTreeListener listener) { + refreshListeners.remove(listener); + } + + /** + * Thread safe adding listener. + * + * @param listener + */ + public void addRefreshItemsListener(IRefreshColorMapTreeListener listener) { + refreshListeners.add(listener); + } +} diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ILevelMapsCallback.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ILevelMapsCallback.java new file mode 100644 index 0000000000..ea85e7f1f2 --- /dev/null +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/ILevelMapsCallback.java @@ -0,0 +1,52 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.viz.ui.colormap; + +import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel; + +/** + * Use by {@link ColorMapTreeFactory} to perform an action for each non-Base + * localization level. May be invoked from a non-UI thread. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 28, 2014 3516       rferrel     Initial creation
+ * 
+ * 
+ * + * @author rferrel + * @version 1.0 + */ + +public interface ILevelMapsCallback { + /** + * Method invoked for each non-BASE localization level. When tree is null it + * is being generated and this method will be called again once the tree is + * created. + * + * @param level + * @param tree + */ + public void treeCreated(LocalizationLevel level, ColorMapTree tree); +} diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/IRefreshColorMapTreeListener.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/IRefreshColorMapTreeListener.java new file mode 100644 index 0000000000..d0d5bc1224 --- /dev/null +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/colormap/IRefreshColorMapTreeListener.java @@ -0,0 +1,46 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.viz.ui.colormap; + +/** + * Used by ColorMapTreeFactory to notify listeners when there changes to a + * colormaps localization files. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Sep 3, 2014  3516       rferrel     Initial creation
+ * 
+ * 
+ * + * @author rferrel + * @version 1.0 + */ + +public interface IRefreshColorMapTreeListener { + /** + * Method called on the UI thread to inform listener of changes in non-BASE + * levels files in the colormaps directory. + */ + public void refreshColorMapTree(); +} diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/dialogs/ColormapComp.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/dialogs/ColormapComp.java index 134c4514fc..6e71c5878d 100644 --- a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/dialogs/ColormapComp.java +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/dialogs/ColormapComp.java @@ -26,11 +26,9 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.events.MenuListener; import org.eclipse.swt.events.SelectionAdapter; @@ -44,19 +42,20 @@ import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; -import com.raytheon.uf.common.localization.IPathManager; import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel; import com.raytheon.uf.common.localization.LocalizationFile; import com.raytheon.uf.common.localization.PathManager; -import com.raytheon.uf.common.localization.PathManagerFactory; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; +import com.raytheon.uf.viz.core.VizApp; import com.raytheon.uf.viz.core.drawables.ColorMapLoader; -import com.raytheon.uf.viz.core.drawables.ColorMapTree; -import com.raytheon.uf.viz.core.drawables.ColorMapTreeFactory; import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.rsc.capabilities.ColorMapCapability; +import com.raytheon.viz.ui.colormap.ColorMapTree; +import com.raytheon.viz.ui.colormap.ColorMapTreeFactory; +import com.raytheon.viz.ui.colormap.ILevelMapsCallback; +import com.raytheon.viz.ui.colormap.IRefreshColorMapTreeListener; /** * Cascading control for colormaps @@ -68,6 +67,8 @@ import com.raytheon.uf.viz.core.rsc.capabilities.ColorMapCapability; * ------------- -------- ----------- -------------------------- * Jul 26, 2010 mschenke Initial creation * Sep 18, 2013 2421 bsteffen Use ColorMapTree for asyncronous loading. + * Aug 28, 2014 3616 rferrel Display ColorMapTree status while creating off + * the UI thread; and added refresh item. * * * @@ -136,7 +137,7 @@ public class ColormapComp { this.cap = cap; } - public void refreshItems() { + public void initItems() { if (cmapPopupMenu != null) { cmapPopupMenu.dispose(); } @@ -151,8 +152,12 @@ public class ColormapComp { } cmapPopupMenu.setVisible(false); - cmapPopupMenu.addMenuListener(new MenuPopulator(cmapPopupMenu, - ColorMapTreeFactory.getBaseTree())); + cmapPopupMenu.addMenuListener(new MenuPopulator(cmapPopupMenu, null)); + } + + public void initializeComponents(Shell shell) { + this.shell = shell; + initializeComponents(); } /** @@ -165,6 +170,31 @@ public class ColormapComp { gd.widthHint = 250; cmapButton.setLayoutData(gd); + /** + * Add listeners and when needed set up removal. + */ + final IRefreshColorMapTreeListener riListener = new IRefreshColorMapTreeListener() { + + @Override + public void refreshColorMapTree() { + if (!cmapButton.isDisposed()) { + initItems(); + } + } + }; + + shell.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + ColorMapTreeFactory.getInstance() + .removeRefreshItemsListener(riListener); + } + }); + + ColorMapTreeFactory.getInstance().addRefreshItemsListener( + riListener); + cmapButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { @@ -175,7 +205,7 @@ public class ColormapComp { } }); } - refreshItems(); + initItems(); } public Menu getMenu() { @@ -212,17 +242,15 @@ public class ColormapComp { * is also an eclipse Job that will automatically try to prefetch some * information from the tree for faster population. */ - private class MenuPopulator extends Job implements MenuListener { + private class MenuPopulator implements MenuListener { private final Menu menu; - private final ColorMapTree tree; + private ColorMapTree tree; public MenuPopulator(Menu menu, ColorMapTree tree) { - super("Loading Color Maps"); this.menu = menu; this.tree = tree; - schedule(); } @Override @@ -230,29 +258,24 @@ public class ColormapComp { /* Do Nothing */ } - /** - * Get all the subTrees that will be displayed and call - * optimizeIsEmpty(). isEmpty() is usually the slowest operation in - * menuShown because it can go recursive. optimizeIsEmpty ensures that - * future calls(from menuShown) are as fast as possible. - */ - @Override - protected IStatus run(IProgressMonitor monitor) { - for (ColorMapTree subTree : tree.getSubTrees()) { - subTree.optimizeIsEmpty(); - } - for (ColorMapTree subTree : getLevelTrees()) { - subTree.optimizeIsEmpty(); - } - return Status.OK_STATUS; - } - - /** + /* * Fill the menu with items from the tree. Always create menus at the - * top even if there are other items + * top even if there are empty trees. */ @Override public void menuShown(MenuEvent e) { + + /* + * When null assume base tree is needed. This allows any dialog + * display to come up without the delay of creating the base tree. + */ + if (tree == null) { + shell.setCursor(shell.getDisplay().getSystemCursor( + SWT.CURSOR_WAIT)); + tree = ColorMapTreeFactory.getInstance().getBaseTree(); + shell.setCursor(null); + } + int index = 0; List subTrees = tree.getSubTrees(); Collections.sort(subTrees, new Comparator() { @@ -282,14 +305,93 @@ public class ColormapComp { addFile(file, index++); } - for (ColorMapTree subTree : getLevelTrees()) { - if (!subTree.isEmpty()) { - addSubTree(subTree, index++); + if (menu == cmapPopupMenu) { + new MenuItem(menu, SWT.SEPARATOR, index++); + final int startLevelIndex = index; + + /* + * Place holders for levels in order to display properly in the + * color bar popup menu. + */ + for (LocalizationLevel level : ColorMapTreeFactory + .getInstance().getTreesLevelLocalization()) { + MenuItem mi = new MenuItem(menu, SWT.NONE, index++); + mi.setText(level.name()); + } + + ColorMapTreeFactory.getInstance().updateLevelMapsCallack( + new ILevelMapsCallback() { + @Override + public void treeCreated( + final LocalizationLevel level, + final ColorMapTree tree) { + /* + * VizApp.runAsync does not update the menu item + * correctly when parent is a menu instead of a + * button. + */ + VizApp.runSync(new Runnable() { + + @Override + public void run() { + updateMenu(startLevelIndex, level, tree); + } + }); + } + }); + } else { + for (ColorMapTree subTree : getLevelTrees()) { + if (!subTree.isEmpty()) { + addSubTree(subTree, index++); + } } } menu.removeMenuListener(this); } + /** + * Update/Add menu item for a level entry with the associated tree. + * Assume running on the UI thread. + * + * @param startLevelIndex + * @param level + * @param tree + * - When null generate unknown place holder + */ + private void updateMenu(int startLevelIndex, LocalizationLevel level, + ColorMapTree tree) { + if (menu.isDisposed()) { + return; + } + int index = startLevelIndex; + MenuItem mi = null; + String name = level.name(); + while (index < menu.getItemCount()) { + mi = menu.getItem(index); + if (mi.getText().startsWith(name)) { + break; + } + ++index; + } + if (index == menu.getItemCount()) { + mi = new MenuItem(menu, SWT.NONE); + } + + if (tree == null) { + mi.setText(name + " ???"); + } else if (tree.isEmpty()) { + mi.setText(name + " ---"); + Menu miMenu = mi.getMenu(); + if (miMenu != null) { + miMenu.dispose(); + mi.setMenu(null); + } + } else { + mi.dispose(); + addSubTree(tree, index); + } + } + /** * The root menu should show not only the base tree, but also an * additional menu item for each localization level(SITE, USER ...), @@ -299,15 +401,11 @@ public class ColormapComp { private List getLevelTrees() { if (menu == cmapPopupMenu) { List trees = new ArrayList(); - IPathManager pm = PathManagerFactory.getPathManager(); - LocalizationLevel[] levels = pm.getAvailableLevels(); + ColorMapTreeFactory cmtf = ColorMapTreeFactory.getInstance(); + LocalizationLevel[] levels = cmtf.getTreesLevelLocalization(); for (LocalizationLevel level : levels) { - if (level != LocalizationLevel.BASE) { - ColorMapTree tree = ColorMapTreeFactory - .getTreeForLevel(level); - trees.add(tree); - - } + ColorMapTree tree = cmtf.getTreeForLevel(level); + trees.add(tree); } return trees; } else { @@ -319,6 +417,7 @@ public class ColormapComp { MenuItem item = new MenuItem(menu, SWT.CASCADE, index); item.setText(tree.getName()); Menu subMenu = new Menu(shell, SWT.DROP_DOWN); + subMenu.setData(tree.getName()); item.setMenu(subMenu); subMenu.addMenuListener(new MenuPopulator(subMenu, tree)); }