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 }