001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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 static edu.wisc.ssec.mcidasv.util.McVGuiUtils.getAllComponentHolders; 032import static edu.wisc.ssec.mcidasv.util.McVGuiUtils.getComponentHolders; 033import static edu.wisc.ssec.mcidasv.util.McVGuiUtils.getWindowForHolder; 034 035import java.awt.*; 036import java.util.List; 037 038import javax.swing.JComponent; 039 040import org.w3c.dom.Document; 041import org.w3c.dom.Element; 042 043import ucar.unidata.idv.IntegratedDataViewer; 044import ucar.unidata.idv.ui.IdvComponentHolder; 045import ucar.unidata.idv.ui.IdvUIManager; 046import ucar.unidata.idv.ui.IdvWindow; 047import ucar.unidata.idv.ui.IdvXmlUi; 048import ucar.unidata.idv.ViewManager; 049import ucar.unidata.util.WrapperException; 050import ucar.unidata.xml.XmlUtil; 051 052/** 053 * McIDAS-V needs its own ComponentHolder merely to associate ViewManagers with 054 * their parent ComponentHolders. This association is later used in 055 * McIDASVViewPanel to create a "hierarchical name" for each ViewManager. 056 * 057 * <p> 058 * Instead of having something like "Panel 1" appearing in the layer controls, 059 * we now have {@literal "ComponentHolder Name>Panel 1"}.</p> 060 * 061 * <p> 062 * Note: ComponentHolder names always double as tab names! McIDAS-V also 063 * intercepts ComponentHolder renaming and updates the layer controls instantly. 064 * </p> 065 */ 066public class McvComponentHolder extends IdvComponentHolder { 067 068 /** IDV friendly description of a dynamic XML skin. */ 069 public static final String CATEGORY_DESCRIPTION = "UI Skin"; 070 071 /** Used to distinguish a dynamic skin from other things. */ 072 public static final String TYPE_DYNAMIC_SKIN = "dynamicskin"; 073 074// private Map<String, ViewManager> dynamicViewManagers = new HashMap<String, ViewManager>(); 075 076 /** Kept around to avoid annoying casting. */ 077 private UIManager uiManager; 078 079 private JComponent cached = null; 080 081 /** 082 * Default constructor for serialization. 083 */ 084 public McvComponentHolder() { 085 } 086 087 /** 088 * Fairly typical constructor. 089 * 090 * @param idv Reference to the main IDV object. 091 * @param obj object being held in this component holder. 092 */ 093 public McvComponentHolder(IntegratedDataViewer idv, Object obj) { 094 super(idv, obj); 095 uiManager = (UIManager)idv.getIdvUIManager(); 096 } 097 098 /** 099 * Overridden so that we can (one day) do the required extra work to write 100 * out the XML for this skin. 101 * 102 * @param doc Parent document we'll use for XML generation. 103 * 104 * @return XML representation of what is being held. 105 */ 106 @Override public Element createXmlNode(Document doc) { 107 if (!getType().equals(TYPE_DYNAMIC_SKIN)) { 108 return super.createXmlNode(doc); 109 } 110 111 // keep in mind that the IDV expects that we're holding a path 112 // to a skin... I don't think that this will work how you want it... 113 // TODO: investigate this! 114 Element node = doc.createElement(IdvUIManager.COMP_COMPONENT_SKIN); 115 node.setAttribute("url", getObject().toString()); 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 * Handles the user attempting to remove this {@code ComponentHolder}. 158 * 159 * <p>Overridden in McIDAS-V to intercept what happens when this holder is 160 * either the last tab in the current window, or the last tab in the application 161 * session.</p> 162 * 163 * <p>If either of the above are true, we hand control off to {@link IdvWindow#doClose()}, 164 * which knows when to close the current window vs the entire application.</p> 165 * 166 * <p>Otherwise, we proceed as normal using {@link IdvComponentHolder#removeDisplayComponent()}.</p> 167 * 168 * @return {@code true} if the user decided to close the tab, {@code false} otherwise. 169 */ 170 @Override public boolean removeDisplayComponent() { 171 IdvWindow currentWindow = getWindowForHolder(this); 172 List<IdvComponentHolder> localTabs = getComponentHolders(currentWindow); 173 List<IdvComponentHolder> allTabs = getAllComponentHolders(); 174 if (allTabs.size() == 1 || localTabs.size() == 1) { 175 // current holder is either the last tab in the app, 176 // or merely the last tab in current window. 177 return currentWindow.doClose(true); 178 } else { 179 // we only need to worry about closing the current tab 180 return super.removeDisplayComponent(); 181 } 182 } 183 184 /** 185 * Overridden so that McV can return a more accurate category if this holder 186 * is holding a dynamic skin. 187 * 188 * @return Category name for the type of thing we're holding. 189 */ 190 @Override public String getCategory() { 191 if (!getType().equals(TYPE_DYNAMIC_SKIN)) { 192 return super.getCategory(); 193 } 194 return CATEGORY_DESCRIPTION; 195 } 196 197 /** 198 * Overridden so that McV can return a more accurate description if this 199 * holder is holding a dynamic skin. 200 * 201 * @return The description of what is being held. 202 */ 203 @Override public String getTypeName() { 204 if (!getType().equals(TYPE_DYNAMIC_SKIN)) { 205 return super.getTypeName(); 206 } 207 return CATEGORY_DESCRIPTION; 208 } 209 210 /** 211 * If the object being held in this component holder is a skin, calling this 212 * method will create a component based upon the skin. 213 * 214 * <p> 215 * Overridden so that McV can tell the UIManager to associate the skin's 216 * ViewManagers with this component holder. That association is used to 217 * build the hierarchical names in the ViewPanel. 218 * </p> 219 * 220 * @return The component represented by this holder's skin. 221 */ 222 @Override protected JComponent makeSkin() { 223 JComponent comp = super.makeSkin(); 224 225 // let's hope that *getViewManagers* only gives us a list of 226 // ViewManagers 227 @SuppressWarnings("unchecked") 228 List<ViewManager> vms = getViewManagers(); 229 if (vms != null) { 230 for (int i = 0; i < vms.size(); i++) { 231 uiManager.setViewManagerHolder(vms.get(i), this); 232 uiManager.getViewPanel().viewManagerChanged(vms.get(i)); 233 } 234 } 235 return comp; 236 } 237 238 /** 239 * Mostly used to ensure that the local reference to the UI manager is valid 240 * when deserializing. 241 * 242 * @param idv Main IDV reference! 243 */ 244 @Override 245 public void setIdv(IntegratedDataViewer idv) { 246 super.setIdv(idv); 247 uiManager = (UIManager)idv.getIdvUIManager(); 248 } 249 250 /** 251 * Set the name of this component holder to the contents of {@code value}. 252 * 253 * <p> 254 * Overridden so that McV can tell the ViewPanel to update upon a name 255 * change. 256 * </p> 257 * 258 * @param value New name of this component holder. 259 */ 260 @Override public void setName(String value) { 261 super.setName(value); 262 263 // let's hope that *getViewManagers* only gives us a list of 264 // ViewManagers 265 @SuppressWarnings("unchecked") 266 List<ViewManager> vms = getViewManagers(); 267 if (vms != null) { 268 for (ViewManager vm : vms) { 269 uiManager.getViewPanel().viewManagerChanged(vm); 270 } 271 } 272 } 273 274 /** 275 * Build the UI component using the XML skin contained by this holder. 276 * 277 * @return UI Component specified by the skin contained in this holder. 278 */ 279 public JComponent makeDynamicSkin() { 280 if (cached != null) { 281 return cached; 282 } 283 try { 284 Element root = XmlUtil.getRoot((String) getObject()); 285 286 IdvXmlUi ui = uiManager.doMakeIdvXmlUi(null, getViewManagers(), 287 root); 288 289 // look for any "embedded" ViewManagers. 290 Element startNode = XmlUtil.findElement(root, null, "embeddednode", 291 "true"); 292 if (startNode != null) { 293 ui.setStartNode(startNode); 294 } 295 296 JComponent contents = (JComponent)ui.getContents(); 297 setViewManagers(ui.getViewManagers()); 298 299 cached = contents; 300 return contents; 301 } catch (Exception e) { 302 throw new WrapperException(e); 303 } 304 } 305 306 /** 307 * Tell this component holder's component group that the tab corresponding 308 * to this holder should become the active tab. 309 */ 310 public void setAsActiveTab() { 311 McvComponentGroup parent = (McvComponentGroup)getParent(); 312 if (parent != null) { 313 parent.setActiveComponentHolder(this); 314 } 315 } 316}