001 /* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2013 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 029 package edu.wisc.ssec.mcidasv.ui; 030 031 import java.awt.Component; 032 import java.awt.event.ActionEvent; 033 import java.util.HashMap; 034 import java.util.List; 035 import java.util.Map; 036 037 import javax.swing.ImageIcon; 038 import javax.swing.JComponent; 039 import javax.swing.event.HyperlinkEvent; 040 import javax.swing.event.HyperlinkListener; 041 import javax.swing.event.HyperlinkEvent.EventType; 042 043 import org.w3c.dom.Element; 044 import org.w3c.dom.NamedNodeMap; 045 import org.w3c.dom.Node; 046 import org.w3c.dom.NodeList; 047 048 import ucar.unidata.idv.IntegratedDataViewer; 049 import ucar.unidata.idv.ViewManager; 050 import ucar.unidata.idv.ui.IdvComponentGroup; 051 import ucar.unidata.idv.ui.IdvComponentHolder; 052 import ucar.unidata.idv.ui.IdvUIManager; 053 import ucar.unidata.idv.ui.IdvWindow; 054 import ucar.unidata.idv.ui.IdvXmlUi; 055 import ucar.unidata.ui.ComponentHolder; 056 import ucar.unidata.ui.HtmlComponent; 057 import ucar.unidata.util.GuiUtils; 058 import ucar.unidata.util.IOUtil; 059 import ucar.unidata.xml.XmlUtil; 060 061 import edu.wisc.ssec.mcidasv.util.TreePanel; 062 063 /** 064 * <p> 065 * McIDAS-V mostly extends this class to preempt the IDV. McIDAS-V needs to 066 * control some HTML processing, ensure that {@link McvComponentGroup}s 067 * and {@link McvComponentHolder}s are created, and handle some special 068 * problems that occur when attempting to load bundles that do not contain 069 * component groups. 070 * </p> 071 */ 072 @SuppressWarnings("unchecked") 073 public class McIDASVXmlUi extends IdvXmlUi { 074 075 /** 076 * Maps an ID to an {@link Element}. 077 */ 078 // private Hashtable<String, Element> idToElement; 079 private Map<String, Element> idToElement; 080 081 /** Avoids unneeded getIdv() calls. */ 082 private IntegratedDataViewer idv; 083 084 /** 085 * Keep around a reference to the window we were built for, useful for 086 * associated component groups with the appropriate window. 087 */ 088 private IdvWindow window; 089 090 public McIDASVXmlUi(IntegratedDataViewer idv, Element root) { 091 super(idv, root); 092 if (idToElement == null) { 093 idToElement = new HashMap<String, Element>(); 094 } 095 } 096 097 public McIDASVXmlUi(IdvWindow window, List viewManagers, 098 IntegratedDataViewer idv, Element root) 099 { 100 super(window, viewManagers, idv, root); 101 this.idv = idv; 102 this.window = window; 103 if (idToElement == null) { 104 idToElement = new HashMap<String, Element>(); 105 } 106 } 107 108 /** 109 * Convert the &gt; and &lt; entities to > and <. 110 * 111 * @param text The text you'd like to convert. 112 * 113 * @return The converted text! 114 */ 115 private static String decodeHtml(String text) { 116 return text.replace(">", ">").replace("<", ">"); 117 } 118 119 /** 120 * Add the component 121 * 122 * @param id id 123 * @param component component 124 */ 125 @Override public void addComponent(String id, Element component) { 126 // this needs to be here because even if you create idToElement in the 127 // constructor, this method will get called from 128 // ucar.unidata.xml.XmlUi#initialize(Element) before control has 129 // returned to the McIDASVXmlUi constructor! 130 if (idToElement == null) { 131 idToElement = new HashMap<String, Element>(); 132 } 133 super.addComponent(id, component); 134 idToElement.put(id, component); 135 } 136 137 /** 138 * <p> 139 * Overridden so that any attempts to generate 140 * {@link IdvComponentGroup}s or {@link IdvComponentHolder}s will return 141 * the respective McIDAS-V equivalents. 142 * </p> 143 * 144 * <p> 145 * It makes things like the draggable tabs possible. 146 * </p> 147 * 148 * @param node The XML representation of the desired component group. 149 * 150 * @return An honest-to-goodness McIDASVComponentGroup based upon the 151 * contents of <code>node</code>. 152 * 153 * @see ucar.unidata.idv.ui.IdvXmlUi#makeComponentGroup(Element) 154 */ 155 @Override protected IdvComponentGroup makeComponentGroup(Element node) { 156 McvComponentGroup group = new McvComponentGroup(idv, "", window); 157 group.initWith(node); 158 159 NodeList elements = XmlUtil.getElements(node); 160 for (int i = 0; i < elements.getLength(); i++) { 161 Element child = (Element)elements.item(i); 162 163 String tag = child.getTagName(); 164 165 if (tag.equals(IdvUIManager.COMP_MAPVIEW) 166 || tag.equals(IdvUIManager.COMP_VIEW)) 167 { 168 ViewManager viewManager = getViewManager(child); 169 group.addComponent(new McvComponentHolder(idv, viewManager)); 170 } 171 else if (tag.equals(IdvUIManager.COMP_COMPONENT_CHOOSERS)) { 172 IdvComponentHolder comp = 173 new McvComponentHolder(idv, "choosers"); 174 comp.setType(IdvComponentHolder.TYPE_CHOOSERS); 175 comp.setName(XmlUtil.getAttribute(child, "name", "Choosers")); 176 group.addComponent(comp); 177 } 178 else if (tag.equals(IdvUIManager.COMP_COMPONENT_SKIN)) { 179 IdvComponentHolder comp = new McvComponentHolder(idv, 180 XmlUtil.getAttribute(child, "url")); 181 182 comp.setType(IdvComponentHolder.TYPE_SKIN); 183 comp.setName(XmlUtil.getAttribute(child, "name", "UI")); 184 group.addComponent(comp); 185 } 186 else if (tag.equals(IdvUIManager.COMP_COMPONENT_HTML)) { 187 String text = XmlUtil.getChildText(child); 188 text = new String(XmlUtil.decodeBase64(text.trim())); 189 ComponentHolder comp = new HtmlComponent("Html Text", text); 190 comp.setShowHeader(false); 191 comp.setName(XmlUtil.getAttribute(child, "name", "HTML")); 192 group.addComponent(comp); 193 } 194 else if (tag.equals(IdvUIManager.COMP_DATASELECTOR)) { 195 group.addComponent(new McvComponentHolder(idv, 196 idv.getIdvUIManager().createDataSelector(false, false))); 197 } 198 else if (tag.equals(IdvUIManager.COMP_COMPONENT_GROUP)) { 199 group.addComponent(makeComponentGroup(child)); 200 } 201 else { 202 System.err.println("Unknown component element:" 203 + XmlUtil.toString(child)); 204 } 205 } 206 return group; 207 } 208 209 /** 210 * <p> 211 * McIDAS-V overrides this so that it can seize control of some HTML 212 * processing in addition to attempting to associate newly-created 213 * {@link ucar.unidata.idv.ViewManager}s with ViewManagers found in a 214 * bundle. 215 * </p> 216 * 217 * <p> 218 * The latter is done so that McIDAS-V can load bundles that do not use 219 * component groups. A "dynamic skin" is built with ViewManagers 220 * for each ViewManager in the bundle. The "viewid" attribute of 221 * the dynamic skin ViewManager is the name of the 222 * {@link ucar.unidata.idv.ViewDescriptor} from the bundled ViewManager. 223 * <tt>createViewManager()</tt> is used to actually associate the new 224 * ViewManager with its bundled ViewManager. 225 * </p> 226 * 227 * @param node The XML describing the component to be created. 228 * @param id <tt>node</tt>'s ID. 229 * 230 * @return The {@link java.awt.Component} described by <tt>node</tt>. 231 * 232 * @see ucar.unidata.idv.ui.IdvXmlUi#createComponent(Element, String) 233 * @see edu.wisc.ssec.mcidasv.ui.McIDASVXmlUi#createViewManager(Element) 234 */ 235 @Override public Component createComponent(Element node, String id) { 236 Component comp = null; 237 String tagName = node.getTagName(); 238 if (tagName.equals(TAG_HTML)) { 239 String text = getAttr(node, ATTR_TEXT, NULLSTRING); 240 text = decodeHtml(text); 241 if (text == null) { 242 String url = getAttr(node, ATTR_URL, NULLSTRING); 243 if (url != null) { 244 text = IOUtil.readContents(url, (String)null); 245 } 246 if (text == null) { 247 text = XmlUtil.getChildText(node); 248 } 249 } 250 HyperlinkListener linkListener = new HyperlinkListener() { 251 public void hyperlinkUpdate(HyperlinkEvent e) { 252 if (e.getEventType() != EventType.ACTIVATED) 253 return; 254 255 String url; 256 if (e.getURL() == null) { 257 url = e.getDescription(); 258 } else { 259 url = e.getURL().toString(); 260 } 261 actionPerformed(new ActionEvent(this, 0, url)); 262 } 263 }; 264 Component[] comps = 265 GuiUtils.getHtmlComponent(text, linkListener, getAttr(node, 266 ATTR_WIDTH, 200), getAttr(node, ATTR_HEIGHT, 200)); 267 comp = comps[1]; 268 } else if (tagName.equals(UIManager.COMP_MAPVIEW) 269 || tagName.equals(UIManager.COMP_VIEW)) { 270 271 // if we're creating a VM for a dynamic skin that was created for 272 // a bundle, createViewManager() will return the bundled VM. 273 ViewManager vm = createViewManager(node); 274 if (vm != null) { 275 comp = vm.getContents(); 276 } else { 277 comp = super.createComponent(node, id); 278 } 279 } else if (tagName.equals(TAG_TREEPANEL)) { 280 comp = createTreePanel(node, id); 281 } else { 282 comp = super.createComponent(node, id); 283 } 284 285 return comp; 286 } 287 288 /** 289 * <p> 290 * Attempts to build a {@link ucar.unidata.idv.ViewManager} based upon 291 * <tt>node</tt>. If the XML has a "viewid" attribute, the 292 * value will be used to search for a ViewManager that has been cached by 293 * the McIDAS-V {@link UIManager}. If the UIManager has a matching 294 * ViewManager, we'll use the cached ViewManager to initialize a 295 * "blank" ViewManager. The cached ViewManager is then removed 296 * from the cache and deleted. This method will return <code>null</code> if 297 * no cached ViewManager was found. 298 * </p> 299 * 300 * <p> 301 * The ViewManager "cache" will only contain bundled ViewManagers 302 * that were not held in a component holder. This means that any 303 * ViewManager returned was created for a dynamic skin, but initialized 304 * with the contents of the corresponding bundled ViewManager. 305 * </p> 306 * 307 * @param node The XML description of the ViewManager that needs building. 308 * 309 * @return Null if there was no cached ViewManager, otherwise a ViewManager 310 * that has been initialized with a bundled ViewManager. 311 */ 312 private ViewManager createViewManager(final Element node) { 313 final String viewId = getAttr(node, "viewid", NULLSTRING); 314 ViewManager vm = null; 315 if (viewId != null) { 316 ViewManager old = UIManager.savedViewManagers.remove(viewId); 317 if (old != null) { 318 vm = getViewManager(node); 319 vm.initWith(old); 320 old.destroy(); 321 } 322 } 323 return vm; 324 } 325 326 private TreePanel createTreePanel(final Element node, final String id) { 327 328 TreePanel treePanel = 329 new TreePanel(getAttr(node, ATTR_USESPLITPANE, false), 330 getAttr(node, ATTR_TREEWIDTH, -1)); 331 332 List<Element> kids = XmlUtil.getListOfElements(node); 333 334 for (Element kid : kids) { 335 Component comp = xmlToUi(kid); 336 if (comp == null) { 337 continue; 338 } 339 340 String label = getAttr(kid, ATTR_TITLE, ""); 341 342 ImageIcon icon = getAttr(kid, ATTR_ICON, (ImageIcon)null); 343 String cat = getAttr(kid, ATTR_CATEGORY, (String)null); 344 if (XmlUtil.getAttribute(kid, ATTR_CATEGORYCOMPONENT, false)) { 345 treePanel.addCategoryComponent(cat, (JComponent)comp); 346 } else { 347 treePanel.addComponent((JComponent)comp, cat, label, icon); 348 } 349 } 350 treePanel.closeAll(); 351 treePanel.showPersistedSelection(); 352 return treePanel; 353 } 354 355 /** 356 * The xml nodes can contain an idref field. If so this returns the 357 * node that that id defines 358 * 359 * @param node node 360 * 361 * @return The node or the referenced node 362 */ 363 private Element getReffedNode(Element node) { 364 String idRef = getAttr(node, ATTR_IDREF, NULLSTRING); 365 if (idRef == null) { 366 return node; 367 } 368 369 Element reffedNode = (Element)idToElement.get(idRef); 370 if (reffedNode == null) { 371 throw new IllegalStateException("Could not find idref=" + idRef); 372 } 373 374 // TODO(unidata): Make a new copy of the node 375 // reffedNode = reffedNode.copy (); 376 NamedNodeMap map = node.getAttributes(); 377 for (int i = 0; i < map.getLength(); i++) { 378 Node n = map.item(i); 379 if (!n.getNodeName().equals(ATTR_IDREF)) { 380 reffedNode.setAttribute(n.getNodeName(), n.getNodeValue()); 381 } 382 } 383 return reffedNode; 384 } 385 }