001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui; 003 004import java.awt.Color; 005import java.awt.Component; 006import java.util.Objects; 007 008import javax.swing.Icon; 009import javax.swing.JEditorPane; 010import javax.swing.JOptionPane; 011import javax.swing.UIManager; 012import javax.swing.text.JTextComponent; 013 014import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 015import org.openstreetmap.josm.spi.preferences.Config; 016 017/** 018 * A Notification Message similar to a popup window, but without disrupting the 019 * user's workflow. 020 * 021 * Non-modal info panel that vanishes after a certain time. 022 * 023 * This class only holds the data for a notification, {@link NotificationManager} 024 * is responsible for building the message panel and displaying it on screen. 025 * 026 * example: 027 * <pre> 028 * Notification note = new Notification("Hi there!"); 029 * note.setIcon(JOptionPane.INFORMATION_MESSAGE); // optional 030 * note.setDuration(Notification.TIME_SHORT); // optional 031 * note.show(); 032 * </pre> 033 */ 034public class Notification { 035 036 /** 037 * Default width of a notification 038 */ 039 public static final int DEFAULT_CONTENT_WIDTH = 350; 040 041 // some standard duration values (in milliseconds) 042 043 /** 044 * Very short and very easy to grasp message (3 s). 045 * E.g. "Please select at least one node". 046 */ 047 public static final int TIME_SHORT = Config.getPref().getInt("notification-time-short-ms", 3000); 048 049 /** 050 * Short message of one or two lines (5 s). 051 */ 052 public static final int TIME_DEFAULT = Config.getPref().getInt("notification-time-default-ms", 5000); 053 054 /** 055 * Somewhat longer message (10 s). 056 */ 057 public static final int TIME_LONG = Config.getPref().getInt("notification-time-long-ms", 10_000); 058 059 /** 060 * Long text. 061 * (Make sure is still sensible to show as a notification) 062 */ 063 public static final int TIME_VERY_LONG = Config.getPref().getInt("notification-time-very_long-ms", 20_000); 064 065 private Component content; 066 private int duration = Notification.TIME_DEFAULT; 067 private Icon icon; 068 private String helpTopic; 069 070 /** 071 * Constructs a new {@code Notification} without content. 072 */ 073 public Notification() { 074 // nothing to do. 075 } 076 077 /** 078 * Constructs a new {@code Notification} with the given textual content. 079 * @param msg The text to display 080 */ 081 public Notification(String msg) { 082 this(); 083 setContent(msg); 084 } 085 086 /** 087 * Set the content of the message. 088 * 089 * @param content any Component to be shown 090 * 091 * @return the current Object, for convenience 092 * @see #setContent(java.lang.String) 093 */ 094 public Notification setContent(Component content) { 095 this.content = content; 096 return this; 097 } 098 099 /** 100 * Set the notification text. (Convenience method) 101 * 102 * @param msg the message String. Will be wrapped in <html>, so 103 * you can use <br> and other markup directly. 104 * 105 * @return the current Object, for convenience 106 * @see #Notification(java.lang.String) 107 */ 108 public Notification setContent(String msg) { 109 JMultilineLabel lbl = new JMultilineLabel(msg); 110 lbl.setMaxWidth(DEFAULT_CONTENT_WIDTH); 111 lbl.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); 112 lbl.setForeground(Color.BLACK); 113 content = lbl; 114 return this; 115 } 116 117 /** 118 * Set the time after which the message is hidden. 119 * 120 * @param duration the time (in milliseconds) 121 * Preset values {@link #TIME_SHORT}, {@link #TIME_DEFAULT}, {@link #TIME_LONG} 122 * and {@link #TIME_VERY_LONG} can be used. 123 * @return the current Object, for convenience 124 */ 125 public Notification setDuration(int duration) { 126 this.duration = duration; 127 return this; 128 } 129 130 /** 131 * Set an icon to display on the left part of the message window. 132 * 133 * @param icon the icon (null means no icon is displayed) 134 * @return the current Object, for convenience 135 */ 136 public Notification setIcon(Icon icon) { 137 this.icon = icon; 138 return this; 139 } 140 141 /** 142 * Set an icon to display on the left part of the message window by 143 * choosing from the default JOptionPane icons. 144 * 145 * @param messageType one of the following: JOptionPane.ERROR_MESSAGE, 146 * JOptionPane.INFORMATION_MESSAGE, JOptionPane.WARNING_MESSAGE, 147 * JOptionPane.QUESTION_MESSAGE, JOptionPane.PLAIN_MESSAGE 148 * @return the current Object, for convenience 149 */ 150 public Notification setIcon(int messageType) { 151 switch (messageType) { 152 case JOptionPane.ERROR_MESSAGE: 153 return setIcon(UIManager.getIcon("OptionPane.errorIcon")); 154 case JOptionPane.INFORMATION_MESSAGE: 155 return setIcon(UIManager.getIcon("OptionPane.informationIcon")); 156 case JOptionPane.WARNING_MESSAGE: 157 return setIcon(UIManager.getIcon("OptionPane.warningIcon")); 158 case JOptionPane.QUESTION_MESSAGE: 159 return setIcon(UIManager.getIcon("OptionPane.questionIcon")); 160 case JOptionPane.PLAIN_MESSAGE: 161 return setIcon(null); 162 default: 163 throw new IllegalArgumentException("Unknown message type!"); 164 } 165 } 166 167 /** 168 * Display a help button at the bottom of the notification window. 169 * @param helpTopic the help topic 170 * @return the current Object, for convenience 171 */ 172 public Notification setHelpTopic(String helpTopic) { 173 this.helpTopic = helpTopic; 174 return this; 175 } 176 177 /** 178 * Gets the content component to use. 179 * @return The content 180 */ 181 public Component getContent() { 182 return content; 183 } 184 185 /** 186 * Gets the time the notification should be displayed 187 * @return The time to display the notification 188 */ 189 public int getDuration() { 190 return duration; 191 } 192 193 /** 194 * Gets the icon that should be displayed next to the notification 195 * @return The icon to display 196 */ 197 public Icon getIcon() { 198 return icon; 199 } 200 201 /** 202 * Gets the help topic for this notification 203 * @return The help topic 204 */ 205 public String getHelpTopic() { 206 return helpTopic; 207 } 208 209 /** 210 * Display the notification. 211 */ 212 public void show() { 213 NotificationManager.getInstance().showNotification(this); 214 } 215 216 /** 217 * Display the notification by replacing the given queued/displaying notification 218 * @param oldNotification the notification to replace 219 * @since 17628 220 */ 221 public void replaceExisting(Notification oldNotification) { 222 NotificationManager.getInstance().replaceExistingNotification(oldNotification, this); 223 } 224 225 private Object getContentTextOrComponent() { 226 return content instanceof JTextComponent ? ((JTextComponent) content).getText() : content; 227 } 228 229 @Override 230 public boolean equals(Object o) { 231 if (this == o) return true; 232 if (o == null || getClass() != o.getClass()) return false; 233 Notification that = (Notification) o; 234 return duration == that.duration 235 && Objects.equals(getContentTextOrComponent(), that.getContentTextOrComponent()) 236 && Objects.equals(helpTopic, that.helpTopic); 237 } 238 239 @Override 240 public int hashCode() { 241 return Objects.hash(getContentTextOrComponent(), duration, helpTopic); 242 } 243 244 @Override 245 public String toString() { 246 return "Notification{" + 247 "content=" + getContentTextOrComponent() + 248 ", duration=" + duration + 249 ", helpTopic='" + helpTopic + '\'' + 250 '}'; 251 } 252}