001/*
002 * $Id: McvHelpTipDialog.java,v 1.10 2011/03/24 16:06:34 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
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
031package edu.wisc.ssec.mcidasv.ui;
032
033
034import java.awt.Color;
035import java.awt.Dimension;
036import java.awt.Font;
037import java.awt.Insets;
038import java.awt.Toolkit;
039import java.awt.event.ActionEvent;
040import java.awt.event.ActionListener;
041import java.util.ArrayList;
042import java.util.Hashtable;
043import java.util.List;
044import java.util.Random;
045
046import javax.swing.BorderFactory;
047import javax.swing.JButton;
048import javax.swing.JCheckBox;
049import javax.swing.JDialog;
050import javax.swing.JEditorPane;
051import javax.swing.JLabel;
052import javax.swing.JMenu;
053import javax.swing.JMenuBar;
054import javax.swing.JMenuItem;
055import javax.swing.JPanel;
056import javax.swing.JScrollPane;
057import javax.swing.border.BevelBorder;
058import javax.swing.event.HyperlinkEvent;
059import javax.swing.event.HyperlinkListener;
060import javax.swing.text.html.HTMLDocument;
061
062import org.w3c.dom.Element;
063import org.w3c.dom.Node;
064
065import edu.wisc.ssec.mcidasv.Constants;
066import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
067
068import ucar.unidata.util.GuiUtils;
069import ucar.unidata.util.ObjectListener;
070import ucar.unidata.xml.XmlObjectStore;
071import ucar.unidata.xml.XmlResourceCollection;
072import 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")
085public 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}