001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2023
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.chooser;
030
031import static javax.swing.GroupLayout.DEFAULT_SIZE;
032import static javax.swing.GroupLayout.Alignment.BASELINE;
033import static javax.swing.GroupLayout.Alignment.LEADING;
034import static javax.swing.GroupLayout.Alignment.TRAILING;
035import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
036import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
037
038import java.awt.Component;
039import java.util.ArrayList;
040import java.util.List;
041
042import javax.swing.GroupLayout;
043import javax.swing.JButton;
044import javax.swing.JComboBox;
045import javax.swing.JComponent;
046import javax.swing.JLabel;
047import javax.swing.JPanel;
048
049import org.w3c.dom.Document;
050import org.w3c.dom.Element;
051
052import ucar.unidata.idv.chooser.IdvChooserManager;
053import ucar.unidata.idv.chooser.XmlHandler;
054import ucar.unidata.util.CatalogUtil;
055import ucar.unidata.util.GuiUtils;
056import ucar.unidata.util.WmsUtil;
057
058import edu.wisc.ssec.mcidasv.Constants;
059import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
060import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position;
061import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor;
062import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
063
064/**
065 * This handles a variety of flavors of xml documents (e.g., thredds
066 * query capability, thredds catalogs, idv menus) to create data
067 * choosers from. It provides a combobox to enter urls to xml
068 * documents. It retrieves the xml and creates a {@link XmlHandler}
069 * based on the type of xml. Currently this class handles two
070 * types of xml: Thredds catalog and Web Map Server (WMS)
071 * capability documents. The XmlHandler does most of the work.
072 * <p>
073 * This class maintains the different xml docs the user has gone
074 * to coupled with the XmlHandler for each doc. It uses this list
075 * to support navigating back and forth through the history of
076 * documents.
077 *
078 * @author IDV development team
079 * @version $Revision$Date: 2011/03/24 16:06:31 $
080 */
081public class XmlChooser extends ucar.unidata.idv.chooser.XmlChooser implements Constants {
082    
083    /** Catalog browser panel */
084    JPanel catalogPanel;
085    
086    /** Catalog browser panel label history */
087    int labelHistoryIdx = -1;
088    List labelHistory = new ArrayList();
089    
090    /**
091     * Create the {@code XmlChooser}.
092     *
093     * @param mgr {@code IdvChooserManager}
094     * @param root XML root that defines this chooser.
095     *
096     */
097    public XmlChooser(IdvChooserManager mgr, Element root) {
098        super(mgr, root);
099        
100        loadButton = McVGuiUtils.makeImageTextButton(ICON_ACCEPT_SMALL, getLoadCommandName());
101        loadButton.setActionCommand(getLoadCommandName());
102        loadButton.addActionListener(this);
103
104    }
105    
106    private void repaintCatalog() {
107        if (catalogPanel != null) {
108            catalogPanel.invalidate();
109            catalogPanel.validate();
110            catalogPanel.repaint();
111        }
112        String labelName = (String)labelHistory.get(labelHistoryIdx);
113        catalogPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(labelName));
114    }
115    
116    /**
117     *  Go back and display  the previous  document.
118     */
119    public void goBack() {
120        super.goBack();
121        labelHistoryIdx--;
122        repaintCatalog();
123    }
124
125    /**
126     *  Go forward and display  the next   document in the history list.
127     */
128    public void goForward() {
129        super.goForward();
130        labelHistoryIdx++;
131        repaintCatalog();
132    }
133    
134    /**
135     * Add a labeled border to the catalog browser indicating the data type
136     */
137    protected void makeUi(Document doc, Element xmlRoot, String path) {
138        super.makeUi(doc, xmlRoot, path);
139
140        String tagName = xmlRoot.getTagName();
141        String labelName = "Unknown Catalog Type";
142        if (tagName.equals(WmsUtil.TAG_WMS1) || tagName.equals(WmsUtil.TAG_WMS2)) labelName = "Web Map Service";
143        else if (tagName.equals(CatalogUtil.TAG_CATALOG)) labelName = "THREDDS";
144        else if (tagName.equals("menus")) labelName = "Menu";
145        
146        labelHistoryIdx++;
147        labelHistory.add(labelHistoryIdx, labelName);
148        repaintCatalog();
149    }
150        
151    private JLabel statusLabel = new JLabel("Status");
152
153    @Override
154    public void setStatus(String statusString, String foo) {
155        if (statusString == null)
156            statusString = "";
157        statusLabel.setText(statusString);
158    }
159
160    /**
161     *  Create and return the Gui contents.
162     *
163     *  @return The gui contents.
164     */
165    protected JComponent doMakeContents() {
166        JComponent parentContents = super.doMakeContents();
167        
168        // Pull apart the panels
169        // Expected:
170        // Top: URL chooser
171        // Center: Catalog chooser
172        // Bottom: chooser buttons
173        // This takes a bit of digging--some of the components are really buried!
174        Component[] parentComps = parentContents.getComponents();
175        if (parentComps.length != 3 ||
176                !(parentComps[0] instanceof JPanel) ||
177                !(parentComps[1] instanceof JPanel) ||
178                !(parentComps[2] instanceof JPanel)
179        ) return parentContents;
180        
181        // Assign file picker to fileComponent
182        JPanel topPanel = (JPanel)parentComps[0];
183        Component[] panels = topPanel.getComponents();
184        if (panels.length != 2 ||
185                !(panels[0] instanceof JPanel) ||
186                !(panels[1] instanceof JPanel)
187        ) return parentContents;
188        // Found the navigation panel
189        JPanel navigationPanel = (JPanel)panels[0];
190        panels = ((JPanel)panels[1]).getComponents();
191        if (panels.length != 3 ||
192                !(panels[0] instanceof JLabel) ||
193                !(panels[1] instanceof JPanel) ||
194                !(panels[2] instanceof JButton)
195        ) return parentContents;
196        // Found the button
197        JButton fileButton = (JButton)panels[2];
198        panels = ((JPanel)panels[1]).getComponents();
199        if (panels.length != 1 ||
200                !(panels[0] instanceof JComboBox)
201        ) return parentContents;
202        JComboBox fileComponent = (JComboBox)panels[0];
203        McVGuiUtils.setButtonImage(fileButton, ICON_OPEN_SMALL);
204        McVGuiUtils.setComponentWidth(fileButton, Width.DOUBLE);
205        McVGuiUtils.setComponentWidth(fileComponent, Width.DOUBLEDOUBLE);
206        McVGuiUtils.setComponentHeight(fileComponent, fileButton);
207
208        // Deal with the navigation buttons
209        panels = navigationPanel.getComponents();
210        if (panels.length != 2 ||
211                !(panels[0] instanceof JButton) ||
212                !(panels[1] instanceof JButton)
213        ) return parentContents;
214        McVGuiUtils.setButtonImage((JButton)panels[0], Constants.ICON_PREVIOUS_SMALL);
215        McVGuiUtils.setButtonImage((JButton)panels[1], Constants.ICON_NEXT_SMALL);
216        JLabel navigationLabel = McVGuiUtils.makeLabelRight("History:");
217        navigationPanel = GuiUtils.hbox(panels[0], panels[1]);
218        
219        // Rearrange the catalog browser and assign it to innerPanel
220        catalogPanel = (JPanel)parentComps[1];
221        JPanel innerPanel = McVGuiUtils.makeLabeledComponent("Browse:", catalogPanel);
222
223        // Start building the whole thing here
224        JPanel outerPanel = new JPanel();
225
226        JLabel fileLabel = McVGuiUtils.makeLabelRight("Catalog:");
227
228        JLabel statusLabelLabel = McVGuiUtils.makeLabelRight("");
229
230        McVGuiUtils.setLabelPosition(statusLabel, Position.RIGHT);
231        McVGuiUtils.setComponentColor(statusLabel, TextColor.STATUS);
232
233        JButton helpButton = McVGuiUtils.makeImageButton(ICON_HELP, "Show help");
234        helpButton.setActionCommand(GuiUtils.CMD_HELP);
235        helpButton.addActionListener(this);
236
237        JButton refreshButton = McVGuiUtils.makeImageButton(ICON_REFRESH, "Refresh");
238        refreshButton.setActionCommand(GuiUtils.CMD_UPDATE);
239        refreshButton.addActionListener(this);
240
241        McVGuiUtils.setButtonImage(loadButton, ICON_ACCEPT_SMALL);
242        McVGuiUtils.setComponentWidth(loadButton, Width.DOUBLE);
243
244        GroupLayout layout = new GroupLayout(outerPanel);
245        outerPanel.setLayout(layout);
246        layout.setHorizontalGroup(
247                layout.createParallelGroup(LEADING)
248                .addGroup(TRAILING, layout.createSequentialGroup()
249                        .addGroup(layout.createParallelGroup(TRAILING)
250                                .addGroup(layout.createSequentialGroup()
251                                        .addContainerGap()
252                                        .addComponent(helpButton)
253                                        .addGap(GAP_RELATED)
254                                        .addComponent(refreshButton)
255                                        .addPreferredGap(RELATED)
256                                        .addComponent(loadButton))
257                                        .addGroup(LEADING, layout.createSequentialGroup()
258                                                .addContainerGap()
259                                                .addGroup(layout.createParallelGroup(LEADING)
260                                                        .addGroup(layout.createSequentialGroup()
261                                                                .addComponent(navigationLabel)
262                                                                .addGap(GAP_RELATED)
263                                                                .addComponent(navigationPanel))
264                                                        .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
265                                                        .addGroup(layout.createSequentialGroup()
266                                                                .addComponent(fileLabel)
267                                                                .addGap(GAP_RELATED)
268                                                                .addComponent(fileComponent)
269                                                                .addGap(GAP_UNRELATED)
270                                                                .addComponent(fileButton))
271                                                                .addGroup(layout.createSequentialGroup()
272                                                                        .addComponent(statusLabelLabel)
273                                                                        .addGap(GAP_RELATED)
274                                                                        .addComponent(statusLabel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))))
275                                                                        .addContainerGap())
276        );
277        layout.setVerticalGroup(
278                layout.createParallelGroup(LEADING)
279                .addGroup(layout.createSequentialGroup()
280                        .addContainerGap()
281                        .addGroup(layout.createParallelGroup(BASELINE)
282                                .addComponent(fileLabel)
283                                .addComponent(fileComponent)
284                                .addComponent(fileButton))
285                                .addPreferredGap(UNRELATED)
286                                                .addGroup(layout.createParallelGroup(BASELINE)
287                    .addComponent(navigationLabel)
288                    .addComponent(navigationPanel))
289                .addPreferredGap(RELATED)
290
291                                .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
292                                .addPreferredGap(UNRELATED)
293                                .addGroup(layout.createParallelGroup(BASELINE)
294                                        .addComponent(statusLabelLabel)
295                                        .addComponent(statusLabel))
296                                        .addPreferredGap(UNRELATED)
297                                        .addGroup(layout.createParallelGroup(BASELINE)
298                                                .addComponent(loadButton)
299                                                .addComponent(refreshButton)
300                                                .addComponent(helpButton))
301                                                .addContainerGap())
302        );
303
304        return outerPanel;
305
306    }
307
308}
309