001/*
002 * $Id: BasicTipOfTheDayUI.java 3927 2011-02-22 16:34:11Z kleopatra $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 * 
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 * 
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 */
021package org.jdesktop.swingx.plaf.basic;
022
023import java.awt.BorderLayout;
024import java.awt.Component;
025import java.awt.Dialog;
026import java.awt.Dimension;
027import java.awt.Font;
028import java.awt.Frame;
029import java.awt.GridLayout;
030import java.awt.Window;
031import java.awt.event.ActionEvent;
032import java.awt.event.ActionListener;
033import java.awt.event.KeyEvent;
034import java.awt.event.WindowAdapter;
035import java.awt.event.WindowEvent;
036import java.beans.PropertyChangeEvent;
037import java.beans.PropertyChangeListener;
038import java.util.Locale;
039
040import javax.swing.AbstractAction;
041import javax.swing.ActionMap;
042import javax.swing.BorderFactory;
043import javax.swing.Icon;
044import javax.swing.JButton;
045import javax.swing.JCheckBox;
046import javax.swing.JComponent;
047import javax.swing.JDialog;
048import javax.swing.JEditorPane;
049import javax.swing.JLabel;
050import javax.swing.JOptionPane;
051import javax.swing.JPanel;
052import javax.swing.JScrollPane;
053import javax.swing.JTextArea;
054import javax.swing.KeyStroke;
055import javax.swing.LookAndFeel;
056import javax.swing.SwingUtilities;
057import javax.swing.UIManager;
058import javax.swing.plaf.ActionMapUIResource;
059import javax.swing.plaf.ComponentUI;
060import javax.swing.plaf.basic.BasicHTML;
061import javax.swing.text.html.HTMLDocument;
062
063import org.jdesktop.swingx.JXTipOfTheDay;
064import org.jdesktop.swingx.SwingXUtilities;
065import org.jdesktop.swingx.JXTipOfTheDay.ShowOnStartupChoice;
066import org.jdesktop.swingx.plaf.TipOfTheDayUI;
067import org.jdesktop.swingx.plaf.UIManagerExt;
068import org.jdesktop.swingx.tips.TipOfTheDayModel.Tip;
069
070/**
071 * Base implementation of the <code>JXTipOfTheDay</code> UI.
072 * 
073 * @author <a href="mailto:fred@L2FProd.com">Frederic Lavigne</a>
074 */
075public class BasicTipOfTheDayUI extends TipOfTheDayUI {
076
077  public static ComponentUI createUI(JComponent c) {
078    return new BasicTipOfTheDayUI((JXTipOfTheDay)c);
079  }
080
081  protected JXTipOfTheDay tipPane;
082  protected JPanel tipArea;
083  protected Component currentTipComponent;
084
085  protected Font tipFont;
086  protected PropertyChangeListener changeListener;
087
088  public BasicTipOfTheDayUI(JXTipOfTheDay tipPane) {
089    this.tipPane = tipPane;
090  }
091
092  @Override
093  public JDialog createDialog(Component parentComponent,
094    final ShowOnStartupChoice choice) {
095    return createDialog(parentComponent, choice, true);
096  }
097  
098  protected JDialog createDialog(Component parentComponent,
099    final ShowOnStartupChoice choice,
100    boolean showPreviousButton) {
101    Locale locale = parentComponent==null ? null : parentComponent.getLocale();
102    String title = UIManagerExt.getString("TipOfTheDay.dialogTitle", locale);
103
104    final JDialog dialog;
105
106    Window window;
107    if (parentComponent == null) {
108      window = JOptionPane.getRootFrame();
109    } else {
110      window = (parentComponent instanceof Window)?(Window)parentComponent
111        :SwingUtilities.getWindowAncestor(parentComponent);
112    }
113
114    if (window instanceof Frame) {
115      dialog = new JDialog((Frame)window, title, true);
116    } else {
117      dialog = new JDialog((Dialog)window, title, true);
118    }
119
120    dialog.getContentPane().setLayout(new BorderLayout(10, 10));
121    dialog.getContentPane().add(tipPane, BorderLayout.CENTER);
122    ((JComponent)dialog.getContentPane()).setBorder(BorderFactory
123      .createEmptyBorder(10, 10, 10, 10));
124
125    final JCheckBox showOnStartupBox;
126
127    // tip controls
128    JPanel controls = new JPanel(new BorderLayout());
129    dialog.add("South", controls);
130
131    if (choice != null) {
132      showOnStartupBox = new JCheckBox(UIManagerExt
133        .getString("TipOfTheDay.showOnStartupText", locale), choice
134        .isShowingOnStartup());
135      controls.add(showOnStartupBox, BorderLayout.CENTER);
136    } else {
137      showOnStartupBox = null;
138    }
139
140    JPanel buttons =
141      new JPanel(new GridLayout(1, showPreviousButton?3:2, 9, 0));
142    controls.add(buttons, BorderLayout.LINE_END);
143    
144    if (showPreviousButton) {
145      JButton previousTipButton = new JButton(UIManagerExt
146        .getString("TipOfTheDay.previousTipText", locale));
147      buttons.add(previousTipButton);
148      previousTipButton.addActionListener(getActionMap().get("previousTip"));
149    }
150    
151    JButton nextTipButton = new JButton(UIManagerExt
152      .getString("TipOfTheDay.nextTipText", locale));
153    buttons.add(nextTipButton);
154    nextTipButton.addActionListener(getActionMap().get("nextTip"));
155    
156    JButton closeButton = new JButton(UIManagerExt
157      .getString("TipOfTheDay.closeText", locale));
158    buttons.add(closeButton);
159    
160    final ActionListener saveChoice = new ActionListener() {
161      @Override
162    public void actionPerformed(ActionEvent e) {
163        if (choice != null) {
164          choice.setShowingOnStartup(showOnStartupBox.isSelected());
165        }
166        dialog.setVisible(false);
167      }
168    };
169
170    closeButton.addActionListener(new ActionListener() {
171      @Override
172    public void actionPerformed(ActionEvent e) {        
173        dialog.setVisible(false);
174        saveChoice.actionPerformed(null);
175      }
176    });
177    dialog.getRootPane().setDefaultButton(closeButton);
178    
179    dialog.addWindowListener(new WindowAdapter() {
180      @Override
181    public void windowClosing(WindowEvent e) {
182        saveChoice.actionPerformed(null);
183      }
184    });
185    
186    ((JComponent)dialog.getContentPane()).registerKeyboardAction(saveChoice,
187      KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
188      JComponent.WHEN_IN_FOCUSED_WINDOW);
189    
190    dialog.pack();
191    dialog.setLocationRelativeTo(parentComponent);
192
193    return dialog;
194  }
195
196  @Override
197  public void installUI(JComponent c) {
198    super.installUI(c);
199    installDefaults();
200    installKeyboardActions();
201    installComponents();
202    installListeners();
203
204    showCurrentTip();
205  }
206
207  protected void installKeyboardActions() {
208    ActionMap map = getActionMap();
209    if (map != null) {
210      SwingUtilities.replaceUIActionMap(tipPane, map);
211    }
212  }
213
214  ActionMap getActionMap() {
215    ActionMap map = new ActionMapUIResource();
216    map.put("previousTip", new PreviousTipAction());
217    map.put("nextTip", new NextTipAction());
218    return map;
219  }
220
221  protected void installListeners() {
222    changeListener = createChangeListener();
223    tipPane.addPropertyChangeListener(changeListener);
224  }
225
226  protected PropertyChangeListener createChangeListener() {
227    return new ChangeListener();
228  }
229
230  protected void installDefaults() {
231    LookAndFeel.installColorsAndFont(tipPane, "TipOfTheDay.background",
232      "TipOfTheDay.foreground", "TipOfTheDay.font");
233    LookAndFeel.installBorder(tipPane, "TipOfTheDay.border");
234    LookAndFeel.installProperty(tipPane, "opaque", Boolean.TRUE);
235    tipFont = UIManager.getFont("TipOfTheDay.tipFont");
236  }
237
238  protected void installComponents() {
239    tipPane.setLayout(new BorderLayout());
240
241    // tip icon
242    JLabel tipIcon = new JLabel(UIManagerExt
243      .getString("TipOfTheDay.didYouKnowText", tipPane.getLocale()));
244    tipIcon.setIcon(UIManager.getIcon("TipOfTheDay.icon"));
245    tipIcon.setBorder(BorderFactory.createEmptyBorder(22, 15, 22, 15));
246    tipPane.add("North", tipIcon);
247
248    // tip area
249    tipArea = new JPanel(new BorderLayout(2, 2));
250    tipArea.setOpaque(false);
251    tipArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
252    tipPane.add("Center", tipArea);
253  }
254
255  @Override
256  public Dimension getPreferredSize(JComponent c) {
257    return new Dimension(420, 175);
258  }
259
260  protected void showCurrentTip() {
261    if (currentTipComponent != null) {
262      tipArea.remove(currentTipComponent);
263    }
264
265    int currentTip = tipPane.getCurrentTip();
266    if (currentTip == -1) {
267      JLabel label = new JLabel();
268      label.setOpaque(true);
269      label.setBackground(UIManager.getColor("TextArea.background"));
270      currentTipComponent = label;
271      tipArea.add("Center", currentTipComponent);
272      return;
273    }
274
275    // tip does not fall in current tip range
276    if (tipPane.getModel() == null || tipPane.getModel().getTipCount() == 0
277      || (currentTip < 0 && currentTip >= tipPane.getModel().getTipCount())) {
278      currentTipComponent = new JLabel();
279    } else {    
280      Tip tip = tipPane.getModel().getTipAt(currentTip);
281
282      Object tipObject = tip.getTip();
283      if (tipObject instanceof Component) {
284        currentTipComponent = (Component)tipObject;
285      } else if (tipObject instanceof Icon) {
286        currentTipComponent = new JLabel((Icon)tipObject);
287      } else {
288        JScrollPane tipScroll = new JScrollPane();
289        tipScroll.setBorder(null);
290        tipScroll.setOpaque(false);
291        tipScroll.getViewport().setOpaque(false);
292        tipScroll.setBorder(null);
293
294        String text = tipObject == null?"":tipObject.toString();
295
296        if (BasicHTML.isHTMLString(text)) {
297          JEditorPane editor = new JEditorPane("text/html", text);
298          editor.setFont(tipPane.getFont());
299//          BasicHTML.updateRenderer(editor, text);
300          SwingXUtilities.setHtmlFont(
301                  (HTMLDocument) editor.getDocument(), tipPane.getFont());
302          editor.setEditable(false);
303          editor.setBorder(null);
304          editor.setMargin(null);
305          editor.setOpaque(false);
306          tipScroll.getViewport().setView(editor);
307        } else {
308          JTextArea area = new JTextArea(text);
309          area.setFont(tipPane.getFont());
310          area.setEditable(false);
311          area.setLineWrap(true);
312          area.setWrapStyleWord(true);
313          area.setBorder(null);
314          area.setMargin(null);
315          area.setOpaque(false);
316          tipScroll.getViewport().setView(area);
317        }
318
319        currentTipComponent = tipScroll;
320      }
321    }
322    
323    tipArea.add("Center", currentTipComponent);
324    tipArea.revalidate();
325    tipArea.repaint();
326  }
327
328  @Override
329  public void uninstallUI(JComponent c) {
330    uninstallListeners();
331    uninstallComponents();
332    uninstallDefaults();
333    super.uninstallUI(c);
334  }
335
336  protected void uninstallListeners() {
337    tipPane.removePropertyChangeListener(changeListener);
338  }
339
340  protected void uninstallComponents() {}
341
342  protected void uninstallDefaults() {}
343
344  class ChangeListener implements PropertyChangeListener {
345    @Override
346    public void propertyChange(PropertyChangeEvent evt) {
347      if (JXTipOfTheDay.CURRENT_TIP_CHANGED_KEY.equals(evt.getPropertyName())) {
348        showCurrentTip();
349      }
350    }
351  }
352
353  class PreviousTipAction extends AbstractAction {
354    public PreviousTipAction() {
355      super("previousTip");
356    }
357    @Override
358    public void actionPerformed(ActionEvent e) {
359      tipPane.previousTip();
360    }
361    @Override
362    public boolean isEnabled() {
363      return tipPane.isEnabled();
364    }
365  }
366
367  class NextTipAction extends AbstractAction {
368    public NextTipAction() {
369      super("nextTip");
370    }
371    @Override
372    public void actionPerformed(ActionEvent e) {
373      tipPane.nextTip();
374    }
375    @Override
376    public boolean isEnabled() {
377      return tipPane.isEnabled();
378    }
379  }
380  
381}