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}