From 6ddb455763991da7e0e291982d22825403ff8c05 Mon Sep 17 00:00:00 2001 From: Max Schenkelberg Date: Thu, 16 May 2013 10:54:16 -0500 Subject: [PATCH] Issue #1638 Fixed localization feature dependencies. Updated plugin data object resource to be thread safe. Moved common legend operations to DefaultLegendResource. Amend: Corrected ticket number Change-Id: Ice336b6b4fb286dd4905f43b6f16259f23dea0c6 Former-commit-id: 33f7156a45ee1ca8df7a54587e7ee8b831ca40bf [formerly 62efca5041ba965bbfc8dbace9d987c91f44276e] [formerly 67e0d17aed5b7d566efe6aa0537a4d76c1f275ae] [formerly 8b4d47c69066504d2c13cd0af4716e4514bcc6d6 [formerly 67e0d17aed5b7d566efe6aa0537a4d76c1f275ae [formerly dad0108065ac5f208f59ce6bc6493000278e2c21]]] Former-commit-id: 8b4d47c69066504d2c13cd0af4716e4514bcc6d6 Former-commit-id: 9c30edc05a7e1827e11b53976977decdf367c0eb [formerly 0038138fdc570697b7c8cfe977a778fcea2cab65] Former-commit-id: a3ad1c7c4524318f70aafc1883e00bff67b24de5 --- .../rsc/legend/AbstractLegendResource.java | 74 ++++- .../rsc/legend/DefaultLegendResource.java | 99 ++++++- .../rsc/AbstractPluginDataObjectResource.java | 262 ++++++++++++------ .../feature.xml | 4 +- 4 files changed, 344 insertions(+), 95 deletions(-) diff --git a/cave/com.raytheon.uf.viz.core.rsc/src/com/raytheon/uf/viz/core/rsc/legend/AbstractLegendResource.java b/cave/com.raytheon.uf.viz.core.rsc/src/com/raytheon/uf/viz/core/rsc/legend/AbstractLegendResource.java index 28c06fd0b6..2586951ca5 100644 --- a/cave/com.raytheon.uf.viz.core.rsc/src/com/raytheon/uf/viz/core/rsc/legend/AbstractLegendResource.java +++ b/cave/com.raytheon.uf.viz.core.rsc/src/com/raytheon/uf/viz/core/rsc/legend/AbstractLegendResource.java @@ -27,6 +27,7 @@ import org.eclipse.jface.action.IMenuManager; import com.raytheon.uf.viz.core.DrawableString; import com.raytheon.uf.viz.core.IDisplayPane; +import com.raytheon.uf.viz.core.IDisplayPaneContainer; import com.raytheon.uf.viz.core.IExtent; import com.raytheon.uf.viz.core.IGraphicsTarget; import com.raytheon.uf.viz.core.drawables.IDescriptor; @@ -37,10 +38,12 @@ import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.legend.ILegendDecorator; import com.raytheon.uf.viz.core.rsc.AbstractResourceData; import com.raytheon.uf.viz.core.rsc.AbstractVizResource; +import com.raytheon.uf.viz.core.rsc.IInputHandler.InputPriority; import com.raytheon.uf.viz.core.rsc.LoadProperties; import com.raytheon.uf.viz.core.rsc.RenderingOrderFactory.ResourceOrder; import com.raytheon.viz.ui.cmenu.ContextMenuManager; import com.raytheon.viz.ui.cmenu.IContextMenuProvider; +import com.raytheon.viz.ui.input.InputAdapter; /** * Base legend resource class, does majority of work for drawing legends. @@ -68,6 +71,56 @@ public abstract class AbstractLegendResource private static final int RIGHT_OFFSET_IN_PIXELS = 18; + private InputAdapter resourceClickedHandler = new InputAdapter() { + + private boolean moved = false; + + private ResourcePair mouseDownRsc; + + @Override + public boolean handleMouseDown(int x, int y, int mouseButton) { + boolean handle = moved = false; + IDisplayPaneContainer container = getResourceContainer(); + IDisplayPane active = container.getActiveDisplayPane(); + if (active.getDescriptor() == getDescriptor()) { + mouseDownRsc = checkLabelSpace(getDescriptor(), + active.getTarget(), x, y); + if (mouseDownRsc != null) { + handle = AbstractLegendResource.this.checkResourceClick( + mouseDownRsc, mouseButton); + if (!handle) { + mouseDownRsc = null; + } + } + } + return handle; + } + + @Override + public boolean handleMouseDownMove(int x, int y, int mouseButton) { + if (mouseDownRsc != null) { + moved = true; + } + return moved; + } + + @Override + public boolean handleMouseUp(int x, int y, int mouseButton) { + if (mouseDownRsc != null) { + try { + if (!moved) { + resourceClicked(mouseDownRsc, mouseButton); + } + } finally { + mouseDownRsc = null; + } + return true; + } + return false; + } + + }; + /** * @param resourceData * @param loadProperties @@ -84,7 +137,10 @@ public abstract class AbstractLegendResource */ @Override protected void disposeInternal() { - + IDisplayPaneContainer container = getResourceContainer(); + if (container != null) { + container.unregisterMouseHandler(resourceClickedHandler); + } } /* @@ -148,7 +204,11 @@ public abstract class AbstractLegendResource */ @Override protected void initInternal(IGraphicsTarget target) throws VizException { - + IDisplayPaneContainer container = getResourceContainer(); + if (container != null) { + container.registerMouseHandler(resourceClickedHandler, + InputPriority.SYSTEM_RESOURCE); + } } protected ResourcePair checkLabelSpace(IDescriptor descriptor, @@ -289,4 +349,14 @@ public abstract class AbstractLegendResource ContextMenuManager.fillContextMenu(menuManager, selectedResource, getResourceContainer()); } + + protected boolean checkResourceClick(ResourcePair mouseDownRsc, + int mouseButton) { + // Do nothing + return false; + } + + protected void resourceClicked(ResourcePair resource, int mouseButton) { + // Do nothing + } } diff --git a/cave/com.raytheon.uf.viz.core.rsc/src/com/raytheon/uf/viz/core/rsc/legend/DefaultLegendResource.java b/cave/com.raytheon.uf.viz.core.rsc/src/com/raytheon/uf/viz/core/rsc/legend/DefaultLegendResource.java index a138d23808..83bee2fd90 100644 --- a/cave/com.raytheon.uf.viz.core.rsc/src/com/raytheon/uf/viz/core/rsc/legend/DefaultLegendResource.java +++ b/cave/com.raytheon.uf.viz.core.rsc/src/com/raytheon/uf/viz/core/rsc/legend/DefaultLegendResource.java @@ -30,10 +30,16 @@ import com.raytheon.uf.viz.core.rsc.AbstractVizResource; import com.raytheon.uf.viz.core.rsc.GenericResourceData; import com.raytheon.uf.viz.core.rsc.LoadProperties; import com.raytheon.uf.viz.core.rsc.ResourceList; +import com.raytheon.uf.viz.core.rsc.capabilities.BlendableCapability; +import com.raytheon.uf.viz.core.rsc.capabilities.BlendedCapability; import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability; +import com.raytheon.uf.viz.core.rsc.capabilities.EditableCapability; +import com.raytheon.viz.ui.input.EditableManager; +import com.raytheon.viz.ui.input.preferences.MousePreferenceManager; /** - * TODO Add Description + * Default legend resource, handles visibility toggling and editableness + * toggling. TODO: Make D2DLegendResource extend it * *
  * 
@@ -52,6 +58,13 @@ import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
 public class DefaultLegendResource extends
         AbstractLegendResource {
 
+    private static final String HIDE_RESOURCE_PREF = "com.raytheon.viz.d2d.ui.hide.resource";
+
+    private static final String EDIT_RESOURCE_PREF = "com.raytheon.viz.ui.input.resource.edit";
+
+    private MousePreferenceManager prefManager = MousePreferenceManager
+            .getInstance();
+
     /**
      * @param resourceData
      * @param loadProperties
@@ -94,6 +107,12 @@ public class DefaultLegendResource extends
                     } else {
                         legend.label = rsc.getName();
                         legend.resource = resourcePair;
+
+                        if (rsc.hasCapability(EditableCapability.class)
+                                && rsc.getCapability(EditableCapability.class)
+                                        .isEditable()) {
+                            legend.label += " (Editable)";
+                        }
                     }
 
                     if (!vis) {
@@ -115,4 +134,82 @@ public class DefaultLegendResource extends
         }
         return entries;
     }
+
+    @Override
+    protected boolean checkResourceClick(ResourcePair mouseDownRsc,
+            int mouseButton) {
+        return true;
+    }
+
+    @Override
+    protected void resourceClicked(ResourcePair resource, int mouseButton) {
+        if (prefManager.handleClick(EDIT_RESOURCE_PREF, mouseButton)
+                && resource.getResource().hasCapability(
+                        EditableCapability.class)) {
+            // Editable case
+            toggleEditability(resource);
+        } else {
+            // Visibility case
+            toggleVisibility(resource);
+        }
+        issueRefresh();
+    }
+
+    /**
+     * Toggle visibility of resource taking blended/blendable resources into
+     * account.
+     * 
+     * If resource to toggle is blended, 1st check to see if parent resource is
+     * visible. If not visible then make parent and all children visible.
+     * 
+     * @param rp
+     */
+    protected boolean toggleVisibility(ResourcePair rp) {
+        AbstractVizResource rsc = rp.getResource();
+        if (rsc != null) {
+            if (rsc.hasCapability(BlendedCapability.class)) {
+                ResourcePair parentRsc = rsc.getCapability(
+                        BlendedCapability.class).getBlendableResource();
+                ResourceList children = parentRsc.getResource()
+                        .getCapability(BlendableCapability.class)
+                        .getResourceList();
+                if (parentRsc.getProperties().isVisible() == false) {
+                    parentRsc.getProperties().setVisible(true);
+                    for (ResourcePair child : children) {
+                        child.getProperties().setVisible(true);
+                    }
+                } else {
+                    // topmost resource is visible, toggle us and other
+                    // rsc
+                    if (rp.getProperties().isVisible() == false) {
+                        rp.getProperties().setVisible(true);
+                        parentRsc
+                                .getResource()
+                                .getCapability(BlendableCapability.class)
+                                .setAlphaStep(BlendableCapability.BLEND_MAX / 2);
+                    } else {
+                        parentRsc.getResource()
+                                .getCapability(BlendableCapability.class)
+                                .toggle(rp);
+                    }
+                }
+                return rp.getProperties().isVisible();
+            }
+        }
+        rp.getProperties().setVisible(!rp.getProperties().isVisible());
+        return rp.getProperties().isVisible();
+    }
+
+    /**
+     * Toggles the editable flag on the resource
+     * 
+     * @param rp
+     * @return
+     */
+    protected boolean toggleEditability(ResourcePair rp) {
+        EditableManager.makeEditable(rp.getResource(), !rp.getResource()
+                .getCapability(EditableCapability.class).isEditable());
+        return rp.getResource().getCapability(EditableCapability.class)
+                .isEditable();
+    }
 }
diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/rsc/AbstractPluginDataObjectResource.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/rsc/AbstractPluginDataObjectResource.java
index 6348c19966..5c40a0fef7 100644
--- a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/rsc/AbstractPluginDataObjectResource.java
+++ b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/rsc/AbstractPluginDataObjectResource.java
@@ -21,7 +21,6 @@ package com.raytheon.uf.viz.core.rsc;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -58,14 +57,48 @@ public abstract class AbstractPluginDataObjectResource {
 
     private static class Frame {
+        boolean disposed = false;
+
+        Object lock = new Object();
+
         List records = new ArrayList();
 
         IRenderable renderable;
     }
 
-    private Map frames = new HashMap();
+    private IResourceDataChanged dataChangedListener = new IResourceDataChanged() {
+        @Override
+        public void resourceChanged(ChangeType type, Object object) {
+            if (type == ChangeType.DATA_UPDATE) {
+                if (object instanceof PluginDataObject) {
+                    addDataObject((PluginDataObject) object);
+                } else if (object instanceof PluginDataObject[]) {
+                    addDataObject((PluginDataObject[]) object);
+                } else if (object instanceof Object[]) {
+                    List pdos = new ArrayList();
+                    for (Object obj : (Object[]) object) {
+                        if (obj instanceof PluginDataObject) {
+                            pdos.add((PluginDataObject) obj);
+                        }
+                    }
+                    if (pdos.size() > 0) {
+                        addDataObject(pdos.toArray(new PluginDataObject[0]));
+                    }
+                }
+            } else if (type == ChangeType.CAPABILITY) {
+                if (object instanceof AbstractCapability) {
+                    AbstractCapability capability = (AbstractCapability) object;
+                    for (Frame frame : frames.values()) {
+                        if (frame.renderable != null) {
+                            capabilityChanged(frame.renderable, capability);
+                        }
+                    }
+                }
+            }
+        }
+    };
 
-    private Object lock = new Object();
+    private Map frames = new HashMap();
 
     /**
      * @param resourceData
@@ -75,35 +108,6 @@ public abstract class AbstractPluginDataObjectResource();
-        resourceData.addChangeListener(new IResourceDataChanged() {
-            @Override
-            public void resourceChanged(ChangeType type, Object object) {
-                if (type == ChangeType.DATA_UPDATE) {
-                    if (object instanceof PluginDataObject) {
-                        addDataObject((PluginDataObject) object);
-                    } else if (object instanceof PluginDataObject[]) {
-                        for (PluginDataObject pdo : (PluginDataObject[]) object) {
-                            addDataObject((PluginDataObject) pdo);
-                        }
-                    } else if (object instanceof Object[]) {
-                        for (Object obj : (Object[]) object) {
-                            if (obj instanceof PluginDataObject) {
-                                addDataObject((PluginDataObject) obj);
-                            }
-                        }
-                    }
-                } else if (type == ChangeType.CAPABILITY) {
-                    if (object instanceof AbstractCapability) {
-                        AbstractCapability capability = (AbstractCapability) object;
-                        for (Frame frame : frames.values()) {
-                            if (frame.renderable != null) {
-                                capabilityChanged(frame.renderable, capability);
-                            }
-                        }
-                    }
-                }
-            }
-        });
     }
 
     /**
@@ -112,30 +116,50 @@ public abstract class AbstractPluginDataObjectResource> pdoMap = new HashMap>();
+        for (PluginDataObject pdo : pdos) {
+            DataTime time = getDataObjectTime(pdo);
+            List list = pdoMap.get(time);
+            if (list == null) {
+                list = new ArrayList();
+                pdoMap.put(time, list);
+            }
+            list.add(pdo);
+        }
+
+        synchronized (this) {
+            if (getStatus() == ResourceStatus.DISPOSED) {
+                // Don't process if disposed
                 return;
             }
-            DataTime time = getDataObjectTime(pdo);
-            Frame frame = frames.get(time);
-            if (frame == null) {
-                frame = new Frame();
-                frames.put(time, frame);
-            }
-            if (frame.records.contains(pdo)) {
-                frame.records.remove(pdo);
-            }
-            frame.records.add(pdo);
-            if (frame.renderable != null) {
-                if (updateRenderable(frame.renderable, pdo) == false) {
-                    dispose(frame.renderable);
+
+            for (DataTime time : pdoMap.keySet()) {
+                Frame frame = frames.get(time);
+                if (frame == null) {
+                    frame = new Frame();
+                    frames.put(time, frame);
+                }
+                synchronized (frame.lock) {
+                    List pdoList = pdoMap.get(time);
+                    for (PluginDataObject pdo : pdoList) {
+                        if (frame.records.contains(pdo)) {
+                            frame.records.remove(pdo);
+                        }
+                        frame.records.add(pdo);
+                    }
+                    if (frame.renderable != null) {
+                        if (updateRenderable(frame.renderable,
+                                pdoList.toArray(new PluginDataObject[0])) == false) {
+                            dispose(frame.renderable);
+                        }
+                    }
+                }
+
+                if (!dataTimes.contains(dataTimes)) {
+                    dataTimes.add(time);
                 }
-            }
-            if (!dataTimes.contains(dataTimes)) {
-                dataTimes.add(time);
             }
         }
     }
@@ -159,9 +183,14 @@ public abstract class AbstractPluginDataObjectResource getPluginDataObjects(DataTime time) {
-        Frame frame = frames.get(time);
+        Frame frame = null;
+        synchronized (this) {
+            frame = frames.get(time);
+        }
         if (frame != null) {
-            return frame.records;
+            synchronized (frame.lock) {
+                return new ArrayList(frame.records);
+            }
         }
         return new ArrayList();
     }
@@ -185,14 +214,13 @@ public abstract class AbstractPluginDataObjectResource iter = frames.values().iterator();
-        while (iter.hasNext()) {
-            Frame frame = iter.next();
-            IRenderable renderable = frame.renderable;
-            if (renderable != null) {
-                if (!projectRenderable(renderable, crs)) {
-                    frame.renderable = null;
-                    dispose(renderable);
+        Map frames = null;
+        synchronized (this) {
+            frames = new HashMap(this.frames);
+        }
+
+        for (Frame frame : frames.values()) {
+            synchronized (frame.lock) {
+                IRenderable renderable = frame.renderable;
+                if (renderable != null) {
+                    if (!projectRenderable(renderable, crs)) {
+                        frame.renderable = null;
+                        dispose(renderable);
+                    }
                 }
             }
         }
     }
 
+    @Override
+    protected final void initInternal(IGraphicsTarget target)
+            throws VizException {
+        resourceData.addChangeListener(dataChangedListener);
+        initResource(target);
+    }
+
+    /**
+     * Init method for the resource to initialize any data needed for rendering
+     * which is not tied to a renderable.
+     */
+    protected void initResource(IGraphicsTarget target) throws VizException {
+
+    }
+
     /*
      * (non-Javadoc)
      * 
@@ -240,16 +288,28 @@ public abstract class AbstractPluginDataObjectResource(
+                                        currFrame.records));
+                    }
 
-            if (renderable != null) {
-                renderable.paint(target, paintProps);
+                    if (renderable != null) {
+                        renderable.paint(target, paintProps);
+                    }
+                }
             }
         }
     }
@@ -261,18 +321,39 @@ public abstract class AbstractPluginDataObjectResource frames = null;
+        synchronized (this) {
+            // Copy Frames and clear member
+            frames = new HashMap(this.frames);
+            this.frames.clear();
         }
+
+        // Dispose frame one by one
+        for (Frame frame : frames.values()) {
+            disposeFrame(frame);
+        }
+
+        resourceData.removeChangeListener(dataChangedListener);
         disposeResource();
     }
 
+    /**
+     * Disposes the Frame object by disposing the renderable and marking
+     * disposed
+     * 
+     * @param frame
+     */
+    private void disposeFrame(Frame frame) {
+        synchronized (frame.lock) {
+            if (frame.renderable != null) {
+                disposeRenderable(frame.renderable);
+                frame.renderable = null;
+            }
+            frame.records.clear();
+            frame.disposed = true;
+        }
+    }
+
     /**
      * Dispose method for the resource to dispose any data not tied to a
      * renderable. Called after all renderables have been disposed. Default impl
@@ -320,21 +401,22 @@ public abstract class AbstractPluginDataObjectResource records) throws VizException;
 
     /**
-     * Update the renderable with the new pdo, if the renderable is updatable,
+     * Update the renderable with the new pdos, if the renderable is updatable,
      * return true. If the renderable needs to be recreated from scratch, return
      * false
      * 
      * @param renderable
-     * @param pdo
+     * @param pdos
      * @return
      */
     protected abstract boolean updateRenderable(IRenderable renderable,
-            PluginDataObject pdo);
+            PluginDataObject... pdos);
 }
diff --git a/cave/com.raytheon.uf.viz.localization.perspective.feature/feature.xml b/cave/com.raytheon.uf.viz.localization.perspective.feature/feature.xml
index cb2bda8d00..10a9ad8912 100644
--- a/cave/com.raytheon.uf.viz.localization.perspective.feature/feature.xml
+++ b/cave/com.raytheon.uf.viz.localization.perspective.feature/feature.xml
@@ -18,8 +18,8 @@
    
 
    
-      
-      
+      
+