001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
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 https://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 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        return node;
111    }
112
113    /**
114     * Overridden so that McV can do the required extra work if this holder is
115     * holding a dynamic XML skin.
116     * 
117     * @return Contents of this holder as a UI component.
118     */
119    @Override public JComponent doMakeContents() {
120        JComponent contents;
121        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
122            contents = super.doMakeContents();
123        } else {
124            contents = makeDynamicSkin();
125        }
126//        contents.addComponentListener(new ComponentListener() {
127//            @Override public void componentHidden(ComponentEvent e) {
128//                logger.trace("component hidden");
129//                GuiUtils.toggleHeavyWeightComponents(contents, false);
130//            }
131//            @Override public void componentShown(ComponentEvent e) {
132//                logger.trace("component shown");
133//                GuiUtils.toggleHeavyWeightComponents(contents, false);
134//            }
135//            @Override public void componentMoved(ComponentEvent e) {}
136//            @Override public void componentResized(ComponentEvent e) {}
137//        });
138        return contents;
139    }
140
141    /**
142     * Lets the IDV take care of the details, but does null out the local
143     * reference to the UIManager.
144     */
145    @Override public void doRemove() {
146        super.doRemove();
147        uiManager = null;
148    }
149
150    /**
151     * Overridden so that McV can return a more accurate category if this holder
152     * is holding a dynamic skin.
153     * 
154     * @return Category name for the type of thing we're holding.
155     */
156    @Override public String getCategory() {
157        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
158            return super.getCategory();
159        }
160        return CATEGORY_DESCRIPTION;
161    }
162
163    /**
164     * Overridden so that McV can return a more accurate description if this
165     * holder is holding a dynamic skin.
166     * 
167     * @return The description of what is being held.
168     */
169    @Override public String getTypeName() {
170        if (!getType().equals(TYPE_DYNAMIC_SKIN)) {
171            return super.getTypeName();
172        }
173        return CATEGORY_DESCRIPTION;
174    }
175
176    /**
177     * If the object being held in this component holder is a skin, calling this
178     * method will create a component based upon the skin.
179     * 
180     * <p>
181     * Overridden so that McV can tell the UIManager to associate the skin's
182     * ViewManagers with this component holder. That association is used to
183     * build the hierarchical names in the ViewPanel.
184     * </p>
185     * 
186     * @return The component represented by this holder's skin.
187     */
188    @Override protected JComponent makeSkin() {
189        JComponent comp = super.makeSkin();
190
191        // let's hope that *getViewManagers* only gives us a list of 
192        // ViewManagers
193        @SuppressWarnings("unchecked")
194        List<ViewManager> vms = getViewManagers();
195        if (vms != null) {
196            for (int i = 0; i < vms.size(); i++) {
197                uiManager.setViewManagerHolder(vms.get(i), this);
198                uiManager.getViewPanel().viewManagerChanged(vms.get(i));
199            }
200        }
201        return comp;
202    }
203
204    /**
205     * Mostly used to ensure that the local reference to the UI manager is valid
206     * when deserializing.
207     * 
208     * @param idv Main IDV reference!
209     */
210    @Override
211    public void setIdv(IntegratedDataViewer idv) {
212        super.setIdv(idv);
213        uiManager = (UIManager)idv.getIdvUIManager();
214    }
215
216    /**
217     * Set the name of this component holder to the contents of {@code value}.
218     * 
219     * <p>
220     * Overridden so that McV can tell the ViewPanel to update upon a name
221     * change.
222     * </p>
223     * 
224     * @param value New name of this component holder.
225     */
226    @Override public void setName(String value) {
227        super.setName(value);
228
229        // let's hope that *getViewManagers* only gives us a list of 
230        // ViewManagers
231        @SuppressWarnings("unchecked")
232        List<ViewManager> vms = getViewManagers();
233        if (vms != null) {
234            for (ViewManager vm : vms) {
235                uiManager.getViewPanel().viewManagerChanged(vm);
236            }
237        }
238    }
239
240    /**
241     * Build the UI component using the XML skin contained by this holder.
242     * 
243     * @return UI Component specified by the skin contained in this holder.
244     */
245    public JComponent makeDynamicSkin() {
246        if (cached != null) {
247            return cached;
248        }
249        try {
250            Element root = XmlUtil.getRoot((String) getObject());
251
252            IdvXmlUi ui = uiManager.doMakeIdvXmlUi(null, getViewManagers(),
253                    root);
254
255            // look for any "embedded" ViewManagers.
256            Element startNode = XmlUtil.findElement(root, null, "embeddednode",
257                    "true");
258            if (startNode != null) {
259                ui.setStartNode(startNode);
260            }
261
262            JComponent contents = (JComponent)ui.getContents();
263            setViewManagers(ui.getViewManagers());
264
265            cached = contents;
266            return contents;
267        } catch (Exception e) {
268            throw new WrapperException(e);
269        }
270    }
271
272    /**
273     * Tell this component holder's component group that the tab corresponding
274     * to this holder should become the active tab.
275     */
276    public void setAsActiveTab() {
277        McvComponentGroup parent = (McvComponentGroup)getParent();
278        if (parent != null) {
279            parent.setActiveComponentHolder(this);
280        }
281    }
282}