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 &lt;html&gt;, so
103     * you can use &lt;br&gt; 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}