001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
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    
029    package edu.wisc.ssec.mcidasv.ui;
030    
031    
032    import java.awt.Color;
033    import java.awt.Dimension;
034    import java.awt.Font;
035    import java.awt.Insets;
036    import java.awt.Toolkit;
037    import java.awt.event.ActionEvent;
038    import java.awt.event.ActionListener;
039    import java.util.ArrayList;
040    import java.util.Hashtable;
041    import java.util.List;
042    import java.util.Random;
043    
044    import javax.swing.BorderFactory;
045    import javax.swing.JButton;
046    import javax.swing.JCheckBox;
047    import javax.swing.JDialog;
048    import javax.swing.JEditorPane;
049    import javax.swing.JLabel;
050    import javax.swing.JMenu;
051    import javax.swing.JMenuBar;
052    import javax.swing.JMenuItem;
053    import javax.swing.JPanel;
054    import javax.swing.JScrollPane;
055    import javax.swing.border.BevelBorder;
056    import javax.swing.event.HyperlinkEvent;
057    import javax.swing.event.HyperlinkListener;
058    import javax.swing.text.html.HTMLDocument;
059    
060    import org.w3c.dom.Element;
061    import org.w3c.dom.Node;
062    
063    import edu.wisc.ssec.mcidasv.Constants;
064    import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
065    
066    import ucar.unidata.util.GuiUtils;
067    import ucar.unidata.util.ObjectListener;
068    import ucar.unidata.xml.XmlObjectStore;
069    import ucar.unidata.xml.XmlResourceCollection;
070    import ucar.unidata.xml.XmlUtil;
071    
072    
073    /**
074     * Represents a dialog that holds {@literal "help tips"}. This class is based
075     * upon {@link ucar.unidata.ui.HelpTipDialog}, but adds new functionality:
076     * <ul>
077     * <li>tip counter</li>
078     * <li>formatting</li>
079     * <li>random tips</li>
080     * </ul>
081     */
082    @SuppressWarnings("serial")
083    public class McvHelpTipDialog extends JDialog implements Constants, 
084        HyperlinkListener 
085    {
086    
087            /** help tip preference */
088            public static final String PREF_HELPTIPSHOW = "help.helptip.Show";
089    
090            /** help tip index */
091            public static final String PREF_HELPTIPIDX = "help.helptip.Index";
092    
093            /** list of tips */
094            private List helpTips = new ArrayList();
095    
096            /** resources */
097            private XmlResourceCollection resources;
098    
099            /** index */
100            private int idx = 0;
101    
102            /** count */
103            private JLabel counterText;
104    
105            /** label */
106            private JLabel titleText;
107    
108            /** message */
109            private JEditorPane messageText;
110    
111            /** checkbox */
112            private JCheckBox showCbx;
113    
114            /** store */
115            private XmlObjectStore store;
116    
117            /** action listener */
118            private ActionListener actionListener;
119    
120            /**
121             * Create the HelpTipDialog
122             *
123             * @param resources    list of XML resources
124             * @param actionListener  listener for changes
125             * @param store           store for persistence
126             * @param origin          calling class
127             * @param showByDefault   true to show by default
128             *
129             */
130            public McvHelpTipDialog(XmlResourceCollection resources,
131                            ActionListener actionListener, XmlObjectStore store,
132                            Class origin, boolean showByDefault) {
133    
134                    this.actionListener = actionListener;
135                    this.resources      = resources;
136                    if ((resources == null) || (resources.size() == 0)) {
137                            return;
138                    }
139                    this.store = store;
140    
141                    String title = null;
142                    String icon  = null;
143    
144                    for (int i = 0; i < resources.size(); i++) {
145                            Element helpTipRoot = resources.getRoot(i);
146                            if (helpTipRoot == null) {
147                                    continue;
148                            }
149                            if (title == null) {
150                                    title = XmlUtil.getAttribute(helpTipRoot, "title", (String) null);
151                            }
152                            if (icon == null) {
153                                    icon = XmlUtil.getAttribute(helpTipRoot, "icon", (String) null);
154                            }
155                            helpTips.addAll(XmlUtil.findChildren(helpTipRoot, "helptip"));
156                    }
157    
158                    if (title == null) {
159                            title = "McIDAS-V Help Tips";
160                    }
161                    setTitle(title);
162                    if (icon == null) {
163                            icon = "/edu/wisc/ssec/mcidasv/resources/icons/toolbar/dialog-information32.png";
164                    }
165                    JLabel imageLabel = GuiUtils.getImageLabel(icon);
166    
167                    // Build the "Tips" menu
168                    JMenu     topMenu    = new JMenu("Tips");
169                    Hashtable menus      = new Hashtable();
170                    menus.put("top", topMenu);
171                    for (int i = 0; i < helpTips.size(); i++) {
172                            Element helpTip = (Element) helpTips.get(i);
173                            String tipTitle = XmlUtil.getAttribute(helpTip, "title", (String) null);
174                            if (tipTitle == null) {
175                                    tipTitle = getMessage(helpTip).substring(0, 20);
176                            }
177                            if (tipTitle.trim().length() == 0) {
178                                    continue;
179                            }
180    
181                            String category = XmlUtil.getAttribute(helpTip, "category", "None");
182                            JMenu m = (JMenu) menus.get(category);
183                            if (m == null) {
184                                    m = new JMenu(category);
185                                    menus.put(category, m);
186                                    topMenu.add(m);
187                            }
188                            JMenuItem mi = new JMenuItem(tipTitle);
189                            mi.addActionListener(new ObjectListener(new Integer(i)) {
190                                    public void actionPerformed(ActionEvent ae) {
191                                            idx = ((Integer) theObject).intValue();
192                                            showTip();
193                                    }
194                            });
195                            m.add(mi);
196                    }
197    
198                    titleText = new JLabel("Title");
199                    counterText = McVGuiUtils.makeLabelRight("0/0");
200    
201                    JPanel topPanel = GuiUtils.left(
202                                    GuiUtils.hbox(imageLabel, titleText, GAP_RELATED)
203                    );
204    
205                    Dimension helpDimension = new Dimension(400, 200);
206                    messageText     = new JEditorPane();
207                    messageText.setMinimumSize(helpDimension);
208                    messageText.setPreferredSize(helpDimension);
209                    messageText.setEditable(false);
210                    messageText.addHyperlinkListener(this);
211                    messageText.setContentType("text/html");
212                    Font font = javax.swing.UIManager.getFont("Label.font");
213                    String rule = "body { font-family:"+font.getFamily()+"; font-size:"+font.getSize()+"pt; }";
214                    ((HTMLDocument)messageText.getDocument()).getStyleSheet().addRule(rule);
215                    //        messageText.setBackground(new JPanel().getBackground());
216                    JScrollPane scroller = GuiUtils.makeScrollPane(messageText, 0, 0);
217                    scroller.setBorder(BorderFactory.createLoweredBevelBorder());
218                    scroller.setPreferredSize(helpDimension);
219                    scroller.setMinimumSize(helpDimension);
220    
221                    showCbx = new JCheckBox("Show tips on startup", showByDefault);
222                    showCbx.addActionListener(new ActionListener() {
223                            public void actionPerformed(ActionEvent ae) {
224                                    writeShowNextTime();
225                            }
226                    });
227    
228                    JPanel centerPanel = GuiUtils.center(scroller);
229    
230                    JButton prevBtn = McVGuiUtils.makeImageButton(ICON_PREVIOUS_SMALL, "Previous");
231                    prevBtn.addActionListener(new ActionListener() {
232                            public void actionPerformed(ActionEvent event) {
233                                    previous();
234                            }
235                    });
236    
237                    JButton nextBtn = McVGuiUtils.makeImageButton(ICON_NEXT_SMALL, "Next");
238                    nextBtn.addActionListener(new ActionListener() {
239                            public void actionPerformed(ActionEvent event) {
240                                    next();
241                            }
242                    });
243    
244                    JButton randBtn = McVGuiUtils.makeImageButton(ICON_RANDOM_SMALL, "Random");
245                    randBtn.addActionListener(new ActionListener() {
246                            public void actionPerformed(ActionEvent event) {
247                                    random();
248                            }
249                    });
250    
251                    JButton closeBtn = McVGuiUtils.makePrettyButton("Close");
252                    closeBtn.addActionListener(new ActionListener() {
253                            public void actionPerformed(ActionEvent event) {
254                                    close();
255                            }
256                    });
257    
258    //              JPanel navBtns = GuiUtils.hbox(prevBtn, randBtn, nextBtn, GAP_RELATED);
259                    JPanel navBtns = GuiUtils.hbox(counterText, prevBtn, nextBtn, GAP_RELATED);
260                    JPanel bottomPanel = GuiUtils.leftRight(showCbx, navBtns);
261    
262                    JMenuBar bar = new JMenuBar();
263                    bar.add(topMenu);
264                    add("North", bar);
265    
266                    JPanel contents = GuiUtils.topCenterBottom(
267                                    GuiUtils.inset(topPanel, GAP_RELATED),
268                                    GuiUtils.inset(centerPanel, new Insets(0, GAP_RELATED, 0, GAP_RELATED)),
269                                    GuiUtils.inset(bottomPanel, GAP_RELATED)
270                    );
271                    contents.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED,
272                                    Color.gray, Color.gray));
273    
274                    JPanel bottom = new JPanel();
275                    bottom.add(closeBtn);
276                    add(GuiUtils.centerBottom(contents, bottom));
277                    pack();
278    
279                    random();
280                    Dimension size = getSize();
281                    Dimension ss   = Toolkit.getDefaultToolkit().getScreenSize();
282                    setLocation(ss.width / 2 - size.width / 2, ss.height / 2 - size.height / 2);
283    
284                    setMinimumSize(helpDimension);
285                    setVisible(true);
286            }
287    
288            /**
289             * Write show next time
290             */
291            public void writeShowNextTime() {
292                    if (getStore().get(PREF_HELPTIPSHOW, true) != showCbx.isSelected()) {
293                            getStore().put(PREF_HELPTIPSHOW, showCbx.isSelected());
294                            getStore().save();
295                    }
296            }
297    
298            /**
299             * Close the dialog
300             */
301            public void close() {
302                    writeShowNextTime();
303                    setVisible(false);
304            }
305    
306            /**
307             * Get the persistence store
308             * @return  the persistence
309             */
310            public XmlObjectStore getStore() {
311                    return store;
312            }
313    
314            /**
315             * Go to the next tip.
316             */
317            private void previous() {
318                    idx--;
319                    showTip();
320            }
321    
322            /**
323             * Go to the next tip.
324             */
325            private void next() {
326                    idx++;
327                    showTip();
328            }
329    
330            /**
331             * Go to the next tip.
332             */
333            private void random() {
334                    Random rand = new Random();
335                    idx = rand.nextInt(helpTips.size());
336                    showTip();
337            }
338    
339            /**
340             * Handle a change to a link
341             *
342             * @param e  the link's event
343             */
344            public void hyperlinkUpdate(HyperlinkEvent e) {
345                    if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
346                            if (e.getURL() == null) {
347                                    click(e.getDescription());
348                            } else {
349                                    click(e.getURL().toString());
350                            }
351                    }
352            }
353    
354            /**
355             * Handle a click on a link
356             *
357             * @param url  the link definition
358             */
359            public void click(String url) {
360                    actionListener.actionPerformed(new ActionEvent(this, 0, url));
361            }
362    
363            /**
364             * Get the title for this tip
365             *
366             * @param helpTip  the tip node
367             * @return  the title
368             */
369            private String getTitle(Node helpTip) {
370                    String title = XmlUtil.getAttribute(helpTip, "title", (String) null);
371                    if (title == null) {
372                            title = "";
373                    }
374                    return "<html><h2>" + title + "</h2></html>";
375            }
376    
377            /**
378             * Get the message for this tip
379             *
380             * @param helpTip  the tip node
381             * @return  the message
382             */
383            private String getMessage(Node helpTip) {
384                    String message = XmlUtil.getAttribute(helpTip, "message", (String) null);
385                    if (message == null) {
386                            message = XmlUtil.getChildText(helpTip);
387                    }
388                    return message;
389            }
390    
391            /**
392             * Show the current tip.
393             */
394            private void showTip() {
395    
396                    // Show the first tip if we have no tip history
397                    if (getStore().get(PREF_HELPTIPIDX, -1) < 0) idx=0;
398    
399                    if (helpTips.size() == 0) {
400                            return;
401                    }
402                    if (idx >= helpTips.size()) {
403                            idx = 0;
404                            getStore().put(PREF_HELPTIPIDX, idx);
405                    } else if (idx < 0) {
406                            idx = helpTips.size() - 1;
407                    }
408                    Node   helpTip = (Node) helpTips.get(idx);
409    
410                    counterText.setText((int)(idx+1) + "/" + helpTips.size());
411                    titleText.setText(getTitle(helpTip));
412                    messageText.setText(getMessage(helpTip));
413    
414                    getStore().put(PREF_HELPTIPIDX, idx);
415                    getStore().save();
416            }
417    
418    }