Issue #3516 - Get Color Maps for levels off the dispatch thread.

Change-Id: Icc89f4860eef0b0b404e1e5b66bb0178eedd73dc

Former-commit-id: 246bc4770f [formerly c885873b25 [formerly a67a429eb2d266e66a99b6c18916193cd0d20d9d]]
Former-commit-id: c885873b25
Former-commit-id: fffec40c30
This commit is contained in:
Roger Ferrel 2014-09-03 09:17:34 -05:00
parent 189380f33c
commit b35417acb6
8 changed files with 598 additions and 152 deletions

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------- -------- ----------- --------------------------
* Sep 18, 2013 2421 bsteffen Initial creation
*
* </pre>
*
* @author bsteffen
* @version 1.0
*/
public class ColorMapTreeFactory {
private static ColorMapTree baseTree;
private static Object baseTreeLock = new Object();
private static final Map<LocalizationLevel, ColorMapTree> treesByLevel = new HashMap<LocalizationLevel, ColorMapTree>();
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;
}
}
}

View file

@ -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.
*
* </pre>
*
@ -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(

View file

@ -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,

View file

@ -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.
*
* </pre>
*
@ -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();
}
}
}
}

View file

@ -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.
*
* <pre>
*
* 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.
*
* </pre>
*
* @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<LocalizationLevel, ColorMapTree> treesByLevel = new HashMap<LocalizationLevel, ColorMapTree>();
/**
* Listeners needing treesByLevel information
*/
private List<ILevelMapsCallback> tlcListeners = new ArrayList<ILevelMapsCallback>();
/**
* 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<IRefreshColorMapTreeListener> refreshListeners = Collections
.synchronizedList(new ArrayList<IRefreshColorMapTreeListener>());
/**
* 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);
}
}

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 28, 2014 3516 rferrel Initial creation
*
* </pre>
*
* @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);
}

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Sep 3, 2014 3516 rferrel Initial creation
*
* </pre>
*
* @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();
}

View file

@ -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.
*
* </pre>
*
@ -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<ColorMapTree> subTrees = tree.getSubTrees();
Collections.sort(subTrees, new Comparator<ColorMapTree>() {
@ -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<ColorMapTree> getLevelTrees() {
if (menu == cmapPopupMenu) {
List<ColorMapTree> trees = new ArrayList<ColorMapTree>();
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));
}