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