001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2016
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 {@literal "ComponentHolder Name>Panel 1"}.</p>
054 *
055 * <p>
056 * Note: ComponentHolder names always double as tab names! McIDAS-V also
057 * intercepts ComponentHolder renaming and updates the layer controls instantly.
058 * </p>
059 */
060public class McvComponentHolder extends IdvComponentHolder {
061
062    /** IDV friendly description of a dynamic XML skin. */
063    public static final String CATEGORY_DESCRIPTION = "UI Skin";
064
065    /** Used to distinguish a dynamic skin from other things. */
066    public static final String TYPE_DYNAMIC_SKIN = "dynamicskin";
067
068//    private static Logger logger = LoggerFactory.getLogger(McvComponentHolder.class);
069
070//    private Map<String, ViewManager> dynamicViewManagers = new HashMap<String, ViewManager>();
071
072    /** Kept around to avoid annoying casting. */
073    private UIManager uiManager;
074
075    private JComponent cached = null;
076
077    /**
078     * Default constructor for serialization.
079     */
080    public McvComponentHolder() {
081    }
082
083    /**
084     * Fairly typical constructor.
085     * 
086     * @param idv Reference to the main IDV object.
087     * @param obj object being held in this component holder.
088     */
089    public McvComponentHolder(IntegratedDataViewer idv, Object obj) {
090        super(idv, obj);
091        uiManager = (UIManager)idv.getIdvUIManager();
092    }
093
094    /**
095     * Overridden so that we can (one day) do the required extra work to write
096     * out the XML for this skin.
097     * 
098     * @param doc Parent document we'll use for XML generation.
099     * 
100     * @return XML representation of what is being held.
101     */
102    @Override public Element createXmlNode(Document doc) {
103        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
104            return super.createXmlNode(doc);
105        }
106
107        // keep in mind that the IDV expects that we're holding a path
108        // to a skin... I don't think that this will work how you want it...
109        // TODO: investigate this!
110        Element node = doc.createElement(IdvUIManager.COMP_COMPONENT_SKIN);
111        node.setAttribute("url", getObject().toString());
112
113        /*
114         * try { System.err.println(XmlUtil.toString((Element)getObject())); }
115         * catch (Exception e) { e.printStackTrace(); }
116         */
117
118        return node;
119    }
120
121    /**
122     * Overridden so that McV can do the required extra work if this holder is
123     * holding a dynamic XML skin.
124     * 
125     * @return Contents of this holder as a UI component.
126     */
127    @Override public JComponent doMakeContents() {
128        JComponent contents;
129        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
130            contents = super.doMakeContents();
131        } else {
132            contents = makeDynamicSkin();
133        }
134//        contents.addComponentListener(new ComponentListener() {
135//            @Override public void componentHidden(ComponentEvent e) {
136//                logger.trace("component hidden");
137//                GuiUtils.toggleHeavyWeightComponents(contents, false);
138//            }
139//            @Override public void componentShown(ComponentEvent e) {
140//                logger.trace("component shown");
141//                GuiUtils.toggleHeavyWeightComponents(contents, false);
142//            }
143//            @Override public void componentMoved(ComponentEvent e) {}
144//            @Override public void componentResized(ComponentEvent e) {}
145//        });
146        return contents;
147    }
148
149    /**
150     * Lets the IDV take care of the details, but does null out the local
151     * reference to the UIManager.
152     */
153    @Override public void doRemove() {
154        super.doRemove();
155        uiManager = null;
156    }
157
158    /**
159     * Overridden so that McV can return a more accurate category if this holder
160     * is holding a dynamic skin.
161     * 
162     * @return Category name for the type of thing we're holding.
163     */
164    @Override public String getCategory() {
165        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
166            return super.getCategory();
167        }
168        return CATEGORY_DESCRIPTION;
169    }
170
171    /**
172     * Overridden so that McV can return a more accurate description if this
173     * holder is holding a dynamic skin.
174     * 
175     * @return The description of what is being held.
176     */
177    @Override public String getTypeName() {
178        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
179            return super.getTypeName();
180        }
181        return CATEGORY_DESCRIPTION;
182    }
183
184    /**
185     * If the object being held in this component holder is a skin, calling this
186     * method will create a component based upon the skin.
187     * 
188     * <p>
189     * Overridden so that McV can tell the UIManager to associate the skin's
190     * ViewManagers with this component holder. That association is used to
191     * build the hierarchical names in the ViewPanel.
192     * </p>
193     * 
194     * @return The component represented by this holder's skin.
195     */
196    @Override protected JComponent makeSkin() {
197        JComponent comp = super.makeSkin();
198
199        // let's hope that *getViewManagers* only gives us a list of 
200        // ViewManagers
201        @SuppressWarnings("unchecked")
202        List<ViewManager> vms = getViewManagers();
203        if (vms != null) {
204            for (int i = 0; i < vms.size(); i++) {
205                uiManager.setViewManagerHolder(vms.get(i), this);
206                uiManager.getViewPanel().viewManagerChanged(vms.get(i));
207            }
208        }
209        return comp;
210    }
211
212    /**
213     * Mostly used to ensure that the local reference to the UI manager is valid
214     * when deserializing.
215     * 
216     * @param idv Main IDV reference!
217     */
218    @Override
219    public void setIdv(IntegratedDataViewer idv) {
220        super.setIdv(idv);
221        uiManager = (UIManager)idv.getIdvUIManager();
222    }
223
224    /**
225     * Set the name of this component holder to the contents of {@code value}.
226     * 
227     * <p>
228     * Overridden so that McV can tell the ViewPanel to update upon a name
229     * change.
230     * </p>
231     * 
232     * @param value New name of this component holder.
233     */
234    @Override public void setName(String value) {
235        super.setName(value);
236
237        // let's hope that *getViewManagers* only gives us a list of 
238        // ViewManagers
239        @SuppressWarnings("unchecked")
240        List<ViewManager> vms = getViewManagers();
241        if (vms != null) {
242            for (ViewManager vm : vms) {
243                uiManager.getViewPanel().viewManagerChanged(vm);
244            }
245        }
246    }
247
248    /**
249     * Build the UI component using the XML skin contained by this holder.
250     * 
251     * @return UI Component specified by the skin contained in this holder.
252     */
253    public JComponent makeDynamicSkin() {
254        if (cached != null) {
255            return cached;
256        }
257        try {
258            Element root = XmlUtil.getRoot((String) getObject());
259
260            IdvXmlUi ui = uiManager.doMakeIdvXmlUi(null, getViewManagers(),
261                    root);
262
263            // look for any "embedded" ViewManagers.
264            Element startNode = XmlUtil.findElement(root, null, "embeddednode",
265                    "true");
266            if (startNode != null) {
267                ui.setStartNode(startNode);
268            }
269
270            JComponent contents = (JComponent)ui.getContents();
271            setViewManagers(ui.getViewManagers());
272
273            cached = contents;
274            return contents;
275        } catch (Exception e) {
276            throw new WrapperException(e);
277        }
278    }
279
280    /**
281     * Tell this component holder's component group that the tab corresponding
282     * to this holder should become the active tab.
283     */
284    public void setAsActiveTab() {
285        McvComponentGroup parent = (McvComponentGroup)getParent();
286        if (parent != null) {
287            parent.setActiveComponentHolder(this);
288        }
289    }
290}