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