001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2015
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see http://www.gnu.org/licenses.
027 */
028
029package edu.wisc.ssec.mcidasv.ui;
030
031import java.util.List;
032
033import javax.swing.JComponent;
034
035import org.w3c.dom.Document;
036import org.w3c.dom.Element;
037
038import ucar.unidata.idv.IntegratedDataViewer;
039import ucar.unidata.idv.ui.IdvComponentHolder;
040import ucar.unidata.idv.ui.IdvUIManager;
041import ucar.unidata.idv.ui.IdvXmlUi;
042import ucar.unidata.idv.ViewManager;
043import ucar.unidata.util.WrapperException;
044import ucar.unidata.xml.XmlUtil;
045
046/**
047 * McIDAS-V needs its own ComponentHolder merely to associate ViewManagers with
048 * their parent ComponentHolders. This association is later used in
049 * McIDASVViewPanel to create a "hierarchical name" for each ViewManager.
050 * 
051 * <p>
052 * Instead of having something like "Panel 1" appearing in the layer controls,
053 * we now have "ComponentHolder Name>Panel 1". Note: ComponentHolder names
054 * always double as tab names! McV also intercepts ComponentHolder renaming and
055 * updates the layer controls instantly.
056 * </p>
057 */
058public class McvComponentHolder extends IdvComponentHolder {
059
060    /** IDV friendly description of a dynamic XML skin. */
061    public static final String CATEGORY_DESCRIPTION = "UI Skin";
062
063    /** Used to distinguish a dynamic skin from other things. */
064    public static final String TYPE_DYNAMIC_SKIN = "dynamicskin";
065
066//    private static Logger logger = LoggerFactory.getLogger(McvComponentHolder.class);
067
068//    private Map<String, ViewManager> dynamicViewManagers = new HashMap<String, ViewManager>();
069
070    /** Kept around to avoid annoying casting. */
071    private UIManager uiManager;
072
073    private JComponent cached = null;
074
075    /**
076     * Default constructor for serialization.
077     */
078    public McvComponentHolder() {
079    }
080
081    /**
082     * Fairly typical constructor.
083     * 
084     * @param idv Reference to the main IDV object.
085     * @param obj object being held in this component holder.
086     */
087    public McvComponentHolder(IntegratedDataViewer idv, Object obj) {
088        super(idv, obj);
089        uiManager = (UIManager)idv.getIdvUIManager();
090    }
091
092    /**
093     * Overridden so that we can (one day) do the required extra work to write
094     * out the XML for this skin.
095     * 
096     * @param doc Parent document we'll use for XML generation.
097     * 
098     * @return XML representation of what is being held.
099     */
100    @Override public Element createXmlNode(Document doc) {
101        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
102            return super.createXmlNode(doc);
103        }
104
105        // keep in mind that the IDV expects that we're holding a path
106        // to a skin... I don't think that this will work how you want it...
107        // TODO: investigate this!
108        Element node = doc.createElement(IdvUIManager.COMP_COMPONENT_SKIN);
109        node.setAttribute("url", getObject().toString());
110
111        /*
112         * try { System.err.println(XmlUtil.toString((Element)getObject())); }
113         * catch (Exception e) { e.printStackTrace(); }
114         */
115
116        return node;
117    }
118
119    /**
120     * Overridden so that McV can do the required extra work if this holder is
121     * holding a dynamic XML skin.
122     * 
123     * @return Contents of this holder as a UI component.
124     */
125    @Override public JComponent doMakeContents() {
126        JComponent contents;
127        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
128            contents = super.doMakeContents();
129        } else {
130            contents = makeDynamicSkin();
131        }
132//        contents.addComponentListener(new ComponentListener() {
133//            @Override public void componentHidden(ComponentEvent e) {
134//                logger.trace("component hidden");
135//                GuiUtils.toggleHeavyWeightComponents(contents, false);
136//            }
137//            @Override public void componentShown(ComponentEvent e) {
138//                logger.trace("component shown");
139//                GuiUtils.toggleHeavyWeightComponents(contents, false);
140//            }
141//            @Override public void componentMoved(ComponentEvent e) {}
142//            @Override public void componentResized(ComponentEvent e) {}
143//        });
144        return contents;
145    }
146
147    /**
148     * Lets the IDV take care of the details, but does null out the local
149     * reference to the UIManager.
150     */
151    @Override public void doRemove() {
152        super.doRemove();
153        uiManager = null;
154    }
155
156    /**
157     * Overridden so that McV can return a more accurate category if this holder
158     * is holding a dynamic skin.
159     * 
160     * @return Category name for the type of thing we're holding.
161     */
162    @Override public String getCategory() {
163        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
164            return super.getCategory();
165        }
166        return CATEGORY_DESCRIPTION;
167    }
168
169    /**
170     * Overridden so that McV can return a more accurate description if this
171     * holder is holding a dynamic skin.
172     * 
173     * @return The description of what is being held.
174     */
175    @Override public String getTypeName() {
176        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
177            return super.getTypeName();
178        }
179        return CATEGORY_DESCRIPTION;
180    }
181
182    /**
183     * If the object being held in this component holder is a skin, calling this
184     * method will create a component based upon the skin.
185     * 
186     * <p>
187     * Overridden so that McV can tell the UIManager to associate the skin's
188     * ViewManagers with this component holder. That association is used to
189     * build the hierarchical names in the ViewPanel.
190     * </p>
191     * 
192     * @return The component represented by this holder's skin.
193     */
194    @Override protected JComponent makeSkin() {
195        JComponent comp = super.makeSkin();
196
197        // let's hope that *getViewManagers* only gives us a list of 
198        // ViewManagers
199        @SuppressWarnings("unchecked")
200        List<ViewManager> vms = getViewManagers();
201        if (vms != null) {
202            for (int i = 0; i < vms.size(); i++) {
203                uiManager.setViewManagerHolder(vms.get(i), this);
204                uiManager.getViewPanel().viewManagerChanged(vms.get(i));
205            }
206        }
207        return comp;
208    }
209
210    /**
211     * Mostly used to ensure that the local reference to the UI manager is valid
212     * when deserializing.
213     * 
214     * @param idv Main IDV reference!
215     */
216    @Override
217    public void setIdv(IntegratedDataViewer idv) {
218        super.setIdv(idv);
219        uiManager = (UIManager)idv.getIdvUIManager();
220    }
221
222    /**
223     * Set the name of this component holder to the contents of {@code value}.
224     * 
225     * <p>
226     * Overridden so that McV can tell the ViewPanel to update upon a name
227     * change.
228     * </p>
229     * 
230     * @param value New name of this component holder.
231     */
232    @Override public void setName(String value) {
233        super.setName(value);
234
235        // let's hope that *getViewManagers* only gives us a list of 
236        // ViewManagers
237        @SuppressWarnings("unchecked")
238        List<ViewManager> vms = getViewManagers();
239        if (vms != null) {
240            for (ViewManager vm : vms) {
241                uiManager.getViewPanel().viewManagerChanged(vm);
242            }
243        }
244    }
245
246    /**
247     * Build the UI component using the XML skin contained by this holder.
248     * 
249     * @return UI Component specified by the skin contained in this holder.
250     */
251    public JComponent makeDynamicSkin() {
252        if (cached != null) {
253            return cached;
254        }
255        try {
256            Element root = XmlUtil.getRoot((String) getObject());
257
258            IdvXmlUi ui = uiManager.doMakeIdvXmlUi(null, getViewManagers(),
259                    root);
260
261            // look for any "embedded" ViewManagers.
262            Element startNode = XmlUtil.findElement(root, null, "embeddednode",
263                    "true");
264            if (startNode != null) {
265                ui.setStartNode(startNode);
266            }
267
268            JComponent contents = (JComponent)ui.getContents();
269            setViewManagers(ui.getViewManagers());
270
271            cached = contents;
272            return contents;
273        } catch (Exception e) {
274            throw new WrapperException(e);
275        }
276    }
277
278    /**
279     * Tell this component holder's component group that the tab corresponding
280     * to this holder should become the active tab.
281     */
282    public void setAsActiveTab() {
283        McvComponentGroup parent = (McvComponentGroup)getParent();
284        if (parent != null) {
285            parent.setActiveComponentHolder(this);
286        }
287    }
288}