001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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.ui; 030 031import static java.awt.Color.GRAY; 032 033import static javax.swing.BorderFactory.createBevelBorder; 034import static javax.swing.GroupLayout.Alignment.LEADING; 035import static javax.swing.border.BevelBorder.RAISED; 036 037import static ucar.unidata.util.GuiUtils.getImageIcon; 038import static ucar.unidata.util.LayoutUtil.inset; 039import static ucar.unidata.util.LayoutUtil.topCenter; 040 041import java.awt.Cursor; 042import java.awt.Font; 043import java.awt.event.MouseAdapter; 044import java.awt.event.MouseEvent; 045 046import java.net.MalformedURLException; 047import java.net.URL; 048 049import java.util.Objects; 050import java.util.concurrent.atomic.AtomicBoolean; 051 052import javax.swing.GroupLayout; 053import javax.swing.JEditorPane; 054import javax.swing.JFrame; 055import javax.swing.JLabel; 056import javax.swing.JPanel; 057import javax.swing.JScrollPane; 058import javax.swing.JTabbedPane; 059import javax.swing.JTextArea; 060import javax.swing.SwingUtilities; 061import javax.swing.WindowConstants; 062import javax.swing.event.ChangeEvent; 063import javax.swing.event.ChangeListener; 064import javax.swing.event.HyperlinkEvent; 065import javax.swing.text.DefaultCaret; 066 067import org.slf4j.Logger; 068import org.slf4j.LoggerFactory; 069 070import ucar.unidata.util.Misc; 071 072import edu.wisc.ssec.mcidasv.Constants; 073import edu.wisc.ssec.mcidasv.McIDASV; 074import edu.wisc.ssec.mcidasv.StateManager; 075import edu.wisc.ssec.mcidasv.util.SystemState; 076 077/** 078 * Class that represents the {@literal "Help>About McIDAS-V"} window. 079 */ 080class AboutFrame extends JFrame implements ChangeListener { 081 082 /** Logging object. */ 083 private static final Logger logger = 084 LoggerFactory.getLogger(AboutFrame.class); 085 086 /** 087 * Initial message in text area within {@literal "System Information"} tab. 088 */ 089 private static final String PLEASE_WAIT = 090 "Please wait, collecting system information..."; 091 092 /** Text used as the title for this window. */ 093 private static final String WINDOW_TITLE = "About McIDAS-V"; 094 095 /** Name of the first tab. */ 096 private static final String MCV_TAB_TITLE = "McIDAS-V"; 097 098 /** Name of the second tab. */ 099 private static final String SYS_TAB_TITLE = "System Information"; 100 101 /** Reference to the main McIDAS-V object. */ 102 private final McIDASV mcv; 103 104 /** 105 * Text area within the {@literal "System Information"} tab. 106 * Value may be {@code null}. 107 */ 108 private JTextArea sysTextArea; 109 110 /** Whether or not the system information has been collected. */ 111 private final AtomicBoolean hasSysInfo; 112 113 /** 114 * Creates new form AboutFrame 115 * 116 * @param mcv McIDAS-V object. Cannot be {@code null}. 117 * 118 * @throws NullPointerException if {@code mcv} is {@code null}. 119 */ 120 AboutFrame(final McIDASV mcv) { 121 Objects.requireNonNull("mcv reference cannot be null"); 122 this.mcv = mcv; 123 this.hasSysInfo = new AtomicBoolean(false); 124 initComponents(); 125 } 126 127 private String getSystemInformation() { 128 return SystemState.getStateAsString(mcv, true); 129 } 130 131 // TODO: refactor the initComponents and buildAboutMcv methods. 132 133 /** 134 * Called by the constructor to initialize the {@literal "About"} window. 135 */ 136 private void initComponents() { 137 138 JTabbedPane tabbedPanel = new JTabbedPane(); 139 JPanel mcvTab = new JPanel(); 140 JPanel mcvPanel = buildAboutMcv(); 141 JPanel sysTab = new JPanel(); 142 JScrollPane sysScrollPane = new JScrollPane(); 143 sysTextArea = new JTextArea(); 144 145 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 146 setTitle(WINDOW_TITLE); 147 148 GroupLayout mcvTabLayout = new GroupLayout(mcvTab); 149 mcvTab.setLayout(mcvTabLayout); 150 mcvTabLayout.setHorizontalGroup( 151 mcvTabLayout.createParallelGroup(LEADING) 152 .addComponent(mcvPanel) 153 ); 154 mcvTabLayout.setVerticalGroup( 155 mcvTabLayout.createParallelGroup(LEADING) 156 .addComponent(mcvPanel) 157 ); 158 159 tabbedPanel.addTab(MCV_TAB_TITLE, mcvTab); 160 161 sysTextArea.setText(PLEASE_WAIT); 162 163 sysTextArea.setEditable(false); 164 sysTextArea.setFont(new Font(Font.MONOSPACED, 0, 12)); // NOI18N 165 sysTextArea.setCaretPosition(0); 166 sysTextArea.setLineWrap(false); 167 sysScrollPane.setViewportView(sysTextArea); 168 169 GroupLayout sysTabLayout = new GroupLayout(sysTab); 170 sysTab.setLayout(sysTabLayout); 171 sysTabLayout.setHorizontalGroup( 172 sysTabLayout.createParallelGroup(LEADING) 173 .addGroup(sysTabLayout.createSequentialGroup() 174 .addComponent(sysScrollPane)) 175 ); 176 sysTabLayout.setVerticalGroup( 177 sysTabLayout.createParallelGroup(LEADING) 178 .addGroup(sysTabLayout.createSequentialGroup() 179 .addComponent(sysScrollPane)) 180 ); 181 182 tabbedPanel.addTab(SYS_TAB_TITLE, sysTab); 183 184 GroupLayout layout = new GroupLayout(getContentPane()); 185 getContentPane().setLayout(layout); 186 layout.setHorizontalGroup( 187 layout.createParallelGroup(LEADING) 188 .addComponent(tabbedPanel) 189 ); 190 layout.setVerticalGroup( 191 layout.createParallelGroup(LEADING) 192 .addComponent(tabbedPanel) 193 ); 194 195 tabbedPanel.addChangeListener(this); 196 197 pack(); 198 setSize(450, 375); 199 setLocationRelativeTo(mcv.getIdvUIManager().getFrame()); 200 } 201 202 private JPanel buildAboutMcv() { 203 StateManager stateManager = (StateManager)mcv.getStateManager(); 204 205 JEditorPane editor = new JEditorPane(); 206 editor.setEditable(false); 207 editor.setContentType("text/html"); 208 String html = stateManager.getMcIdasVersionAbout(); 209 editor.setText(html); 210 editor.setBackground(new JPanel().getBackground()); 211 editor.addHyperlinkListener(mcv); 212 213 String splashIcon = mcv.getProperty(Constants.PROP_SPLASHICON, ""); 214 final JLabel iconLbl = new JLabel(getImageIcon(splashIcon)); 215 216 iconLbl.setToolTipText("McIDAS-V Homepage"); 217 iconLbl.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 218 iconLbl.addMouseListener(new MouseAdapter() { 219 public void mouseClicked(MouseEvent evt) { 220 String url = mcv.getProperty(Constants.PROP_HOMEPAGE, ""); 221 try { 222 HyperlinkEvent link = new HyperlinkEvent( 223 iconLbl, 224 HyperlinkEvent.EventType.ACTIVATED, 225 new URL(url) 226 ); 227 mcv.hyperlinkUpdate(link); 228 } catch (MalformedURLException e) { 229 logger.warn("Malformed URL: '"+url+"'", e); 230 } 231 232 } 233 }); 234 JPanel contents = topCenter(inset(iconLbl, 5), inset(editor, 5)); 235 contents.setBorder(createBevelBorder(RAISED, GRAY, GRAY)); 236 return contents; 237 } 238 239 /** 240 * Populates the {@literal "System Information"} tab. 241 * 242 * The system information is collected on a separate thread, and when done, 243 * the results are added to the tab (on the Event Dispatch Thread). 244 */ 245 void populateSystemTab() { 246 Misc.runInABit(500, () -> { 247 if (!hasSysInfo.get()) { 248 String sysInfo = getSystemInformation(); 249 SwingUtilities.invokeLater(() -> { 250 // the caret manipulation is done so that the "append" 251 // call doesn't result in the text area being 252 // auto-scrolled to the end. however, it's also nice to 253 // have the caret available so that keystroke navigation 254 // of the text area still works. 255 DefaultCaret caret = (DefaultCaret)sysTextArea.getCaret(); 256 caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); 257 sysTextArea.setText(sysInfo); 258 caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); 259 hasSysInfo.set(true); 260 }); 261 } 262 }); 263 } 264 265 /** 266 * Respond to a change in the {@link JTabbedPane}. 267 * 268 * If the user has decided to make the {@literal "System Information"} tab 269 * visible, this method will call {@link #populateSystemTab()}. 270 * 271 * @param e Event that represents the state change. Cannot be {@code null}. 272 */ 273 public void stateChanged(ChangeEvent e) { 274 JTabbedPane tabPane = (JTabbedPane)e.getSource(); 275 int newIndex = tabPane.getSelectedIndex(); 276 if (newIndex == 1) { 277 populateSystemTab(); 278 } 279 } 280}