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 }