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.event.ActionEvent; 039import java.awt.event.ActionListener; 040import java.awt.event.KeyAdapter; 041import java.awt.event.KeyEvent; 042import java.awt.event.KeyListener; 043import java.util.Hashtable; 044import java.util.List; 045 046import javax.swing.GroupLayout; 047import javax.swing.JButton; 048import javax.swing.JComboBox; 049import javax.swing.JComponent; 050import javax.swing.JLabel; 051import javax.swing.JPanel; 052import javax.swing.JRadioButton; 053import javax.swing.JScrollPane; 054import javax.swing.JTextArea; 055import javax.swing.JTextField; 056import javax.swing.JToggleButton; 057 058import org.w3c.dom.Element; 059 060import edu.wisc.ssec.mcidasv.Constants; 061import edu.wisc.ssec.mcidasv.util.McVGuiUtils; 062import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position; 063import edu.wisc.ssec.mcidasv.util.McVGuiUtils.TextColor; 064import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width; 065 066import ucar.unidata.data.DataManager; 067import ucar.unidata.idv.chooser.IdvChooserManager; 068import ucar.unidata.util.GuiUtils; 069import ucar.unidata.util.LayoutUtil; 070import ucar.unidata.util.PreferenceList; 071import ucar.unidata.util.StringUtil; 072 073/** 074 * Allows the user to select a url as a data source. 075 */ 076public class UrlChooser extends ucar.unidata.idv.chooser.UrlChooser implements Constants { 077 078 /** Property ID for the contents of {@link #textArea}. */ 079 public static final String PROP_MULTI_URLS = 080 "mcidasv.chooser.url.multiurlcontents"; 081 082 /** 083 * Property ID for the {@literal "URL type"} selection. 084 * Values will be either {@link #MULTIPLE_URL_VALUE} or 085 * {@link #SINGLE_URL_VALUE}. 086 */ 087 public static final String PROP_URL_TYPE = "mcidasv.chooser.url.urltype"; 088 089 /** Multiple URL selection value for {@link #PROP_URL_TYPE}. */ 090 public static final String MULTIPLE_URL_VALUE = "multiple"; 091 092 /** Single URL selection value for {@link #PROP_URL_TYPE}. */ 093 public static final String SINGLE_URL_VALUE = "single"; 094 095 /** Manages the pull down list of URLs. */ 096 private PreferenceList prefList; 097 098 /** List of URLs. */ 099 private JComboBox box; 100 101 private JTextField boxEditor; 102 103 /** Text area for multi-line URLs. */ 104 private JTextArea textArea; 105 106 /** Text scroller. */ 107 private JScrollPane textScroller; 108 109 /** Holds the combo box. */ 110 private JPanel urlPanel; 111 112 /** Holds the text area. */ 113 private JPanel textPanel; 114 115 /** Are we showing {@link #textPanel}? */ 116 private boolean showBox = true; 117 118 /** 119 * Panel that allows switching between {@link #urlPanel} and 120 * {@link #textPanel}. 121 */ 122 private GuiUtils.CardLayoutPanel cardLayoutPanel; 123 124 private JLabel statusLabel = new JLabel("Status"); 125 126 /** 127 * Create the {@code UrlChooser}. 128 * 129 * @param mgr {@code IdvChooserManager}. 130 * @param root XML root that defines this chooser. 131 */ 132 public UrlChooser(IdvChooserManager mgr, Element root) { 133 super(mgr, root); 134 135 String loadCommand = getLoadCommandName(); 136 loadButton = 137 McVGuiUtils.makeImageTextButton(ICON_ACCEPT_SMALL, loadCommand); 138 loadButton.setActionCommand(getLoadCommandName()); 139 loadButton.addActionListener(this); 140 141 } 142 143 /** 144 * Show {@link #urlPanel} or {@link #textPanel}, depending on the value of 145 * {@link #showBox}. 146 */ 147 @Override public void switchFields() { 148 if (showBox) { 149 cardLayoutPanel.show(urlPanel); 150 } else { 151 cardLayoutPanel.show(textPanel); 152 } 153 updateStatus(); 154 } 155 156 /** 157 * Disable/enable any components that depend on the server. 158 * Try to update the status label with what we know here. 159 */ 160 @Override protected void updateStatus() { 161 if ((boxEditor != null) && (textArea != null)) { 162 if (showBox) { 163 setHaveData(!boxEditor.getText().trim().isEmpty()); 164 } else { 165 setHaveData(!textArea.getText().trim().isEmpty()); 166 } 167 super.updateStatus(); 168 if (!getHaveData()) { 169 if (showBox) { 170 setStatus("Enter a URL."); 171 } else { 172 setStatus("Enter one or more URLs."); 173 } 174 } 175 } 176 } 177 178 /** 179 * Handle the action event from the GUI. 180 */ 181 @Override public void doLoadInThread() { 182 loadURL(); 183 } 184 185 /** 186 * Wrapper around {@link #loadURLInner()}, showing the wait cursor. 187 */ 188 private void loadURL() { 189 showWaitCursor(); 190 loadURLInner(); 191 showNormalCursor(); 192 } 193 194 /** 195 * Load the URL. 196 */ 197 private void loadURLInner() { 198 199 String url = ""; 200 String dataSourceId = getDataSourceId(); 201 if (showBox) { 202 Object selectedItem = box.getSelectedItem(); 203 if (selectedItem != null) { 204 url = selectedItem.toString().trim(); 205 } 206 if (url.isEmpty() && (dataSourceId == null)) { 207 userMessage("Please specify a URL."); 208 return; 209 } 210 } 211 212 Hashtable props = new Hashtable(); 213 if (dataSourceId != null) { 214 props.put(DataManager.DATATYPE_ID, dataSourceId); 215 } 216 217 if (showBox) { 218 if (getIdv().handleAction(url, props)) { 219 closeChooser(); 220 prefList.saveState(box); 221 } 222 } else { 223 List<String> urls = 224 StringUtil.split(textArea.getText(), "\n", true, true); 225 226 if (!urls.isEmpty() && makeDataSource(urls, dataSourceId, props)) { 227 closeChooser(); 228 getIdv().getStore().put(PROP_MULTI_URLS, textArea.getText()); 229 } 230 } 231 } 232 233 /** 234 * Make the contents. 235 * 236 * @return the contents 237 */ 238 protected JPanel doMakeInnerPanel() { 239 240 String urlType = 241 getIdv().getStore().get(PROP_URL_TYPE, SINGLE_URL_VALUE); 242 if (MULTIPLE_URL_VALUE.equals(urlType)) { 243 showBox = false; 244 } 245 246 final JToggleButton singleBtn = new JRadioButton("Single", showBox); 247 singleBtn.addActionListener(e -> { 248 getIdv().getStore().put(PROP_URL_TYPE, SINGLE_URL_VALUE); 249 showBox = true; 250 switchFields(); 251 }); 252 253 final JToggleButton multipleBtn = 254 new JRadioButton("Multiple", !showBox); 255 multipleBtn.addActionListener(e -> { 256 getIdv().getStore().put(PROP_URL_TYPE, MULTIPLE_URL_VALUE); 257 showBox = false; 258 switchFields(); 259 }); 260 GuiUtils.buttonGroup(singleBtn, multipleBtn); 261 JPanel radioPanel = LayoutUtil.hbox(singleBtn, multipleBtn); 262 263 prefList = getPreferenceList(PREF_URLLIST); 264 box = prefList.createComboBox(CMD_LOAD, this); 265 boxEditor = (JTextField)box.getEditor().getEditorComponent(); 266 boxEditor.addKeyListener(new KeyAdapter() { 267 @Override public void keyReleased(KeyEvent e) { 268 updateStatus(); 269 } 270 }); 271 272 textArea = new JTextArea(5, 30); 273 textScroller = new JScrollPane(textArea); 274 textArea.addKeyListener(new KeyAdapter() { 275 @Override public void keyReleased(KeyEvent e) { 276 updateStatus(); 277 } 278 }); 279 280 urlPanel = LayoutUtil.top(box); 281 textPanel = LayoutUtil.top(textScroller); 282 283 cardLayoutPanel = new GuiUtils.CardLayoutPanel(); 284 cardLayoutPanel.addCard(urlPanel); 285 cardLayoutPanel.addCard(textPanel); 286 287 JPanel showPanel = 288 McVGuiUtils.topBottom(radioPanel, cardLayoutPanel, null); 289 290 setHaveData(false); 291 updateStatus(); 292 if (!showBox) { 293 textArea.setText(getIdv().getStore().get(PROP_MULTI_URLS, "")); 294 switchFields(); 295 } 296 return McVGuiUtils.makeLabeledComponent("URL:", showPanel); 297 } 298 299 @Override public void setStatus(String statusString, String foo) { 300 if (statusString == null) { 301 statusString = ""; 302 } 303 statusLabel.setText(statusString); 304 } 305 306 /** 307 * Create a more McIDAS-V-like GUI layout 308 */ 309 protected JComponent doMakeContents() { 310 JComponent typeComponent = getDataSourcesComponent(); 311 if (typeComponent == null) { 312 typeComponent = new JLabel("No data type specified"); 313 } 314 315 JPanel innerPanel = doMakeInnerPanel(); 316 317 // Start building the whole thing here 318 JPanel outerPanel = new JPanel(); 319 320 JLabel typeLabel = McVGuiUtils.makeLabelRight("Data Type:"); 321 322 JLabel statusLabelLabel = McVGuiUtils.makeLabelRight(""); 323 324 statusLabel.setText("Status"); 325 McVGuiUtils.setLabelPosition(statusLabel, Position.RIGHT); 326 McVGuiUtils.setComponentColor(statusLabel, TextColor.STATUS); 327 328 JButton helpButton = 329 McVGuiUtils.makeImageButton(ICON_HELP, "Show help"); 330 helpButton.setActionCommand(GuiUtils.CMD_HELP); 331 helpButton.addActionListener(this); 332 333 JButton refreshButton = 334 McVGuiUtils.makeImageButton(ICON_REFRESH, "Refresh"); 335 refreshButton.setActionCommand(GuiUtils.CMD_UPDATE); 336 refreshButton.addActionListener(this); 337 338 McVGuiUtils.setComponentWidth(loadButton, Width.DOUBLE); 339 340 GroupLayout layout = new GroupLayout(outerPanel); 341 outerPanel.setLayout(layout); 342 layout.setHorizontalGroup( 343 layout.createParallelGroup(LEADING) 344 .addGroup(TRAILING, layout.createSequentialGroup() 345 .addGroup(layout.createParallelGroup(TRAILING) 346 .addGroup(layout.createSequentialGroup() 347 .addContainerGap() 348 .addComponent(helpButton) 349 .addGap(GAP_RELATED) 350 .addComponent(refreshButton) 351 .addPreferredGap(RELATED) 352 .addComponent(loadButton)) 353 .addGroup(LEADING, layout.createSequentialGroup() 354 .addContainerGap() 355 .addGroup(layout.createParallelGroup(LEADING) 356 .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 357 .addGroup(layout.createSequentialGroup() 358 .addComponent(typeLabel) 359 .addGap(GAP_RELATED) 360 .addComponent(typeComponent)) 361 .addGroup(layout.createSequentialGroup() 362 .addComponent(statusLabelLabel) 363 .addGap(GAP_RELATED) 364 .addComponent(statusLabel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))))) 365 .addContainerGap()) 366 ); 367 layout.setVerticalGroup( 368 layout.createParallelGroup(LEADING) 369 .addGroup(layout.createSequentialGroup() 370 .addContainerGap() 371 .addGroup(layout.createParallelGroup(BASELINE) 372 .addComponent(typeLabel) 373 .addComponent(typeComponent)) 374 .addPreferredGap(UNRELATED) 375 .addComponent(innerPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) 376 .addPreferredGap(UNRELATED) 377 .addGroup(layout.createParallelGroup(BASELINE) 378 .addComponent(statusLabelLabel) 379 .addComponent(statusLabel)) 380 .addPreferredGap(UNRELATED) 381 .addGroup(layout.createParallelGroup(BASELINE) 382 .addComponent(loadButton) 383 .addComponent(refreshButton) 384 .addComponent(helpButton)) 385 .addContainerGap()) 386 ); 387 return outerPanel; 388 } 389} 390