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