Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/geotools/resources/SwingUtilities.java


1   /*
2    * -*- mode: java; c-basic-indent: 4; indent-tabs-mode: nil -*-
3    * :indentSize=4:noTabs=true:tabSize=4:indentOnTab=true:indentOnEnter=true:mode=java:
4    * ex: set tabstop=4 expandtab:
5    *
6    * MrPostman - webmail <-> email gateway
7    * Copyright (C) 2002-2003 MrPostman Development Group
8    * Projectpage: http://mrbook.org/mrpostman/
9    *
10   *
11   * This program is free software; you can redistribute it and/or modify
12   * it under the terms of the GNU General Public License as published by
13   * the Free Software Foundation; either version 2 of the License, or
14   * (at your option) any later version.
15   *
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * In particular, this implies that users are responsible for
21   * using MrPostman after reading the terms and conditions given
22   * by their web-mail provider.
23   *
24   * You should have received a copy of the GNU General Public License
25   * Named LICENSE in the base directory of this distribution,
26   * if not, write to the Free Software
27   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28   */
29  
30  /*
31   * Geotools - OpenSource mapping toolkit
32   * (C) 2002, Centre for Computational Geography
33   * (C) 2001, Institut de Recherche pour le Développement
34   *
35   *    This library is free software; you can redistribute it and/or
36   *    modify it under the terms of the GNU Lesser General Public
37   *    License as published by the Free Software Foundation; either
38   *    version 2.1 of the License, or (at your option) any later version.
39   *
40   *    This library is distributed in the hope that it will be useful,
41   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
42   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43   *    Lesser General Public License for more details.
44   *
45   *    You should have received a copy of the GNU Lesser General Public
46   *    License along with this library; if not, write to the Free Software
47   *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
48   *
49   *
50   * Contacts:
51   *     UNITED KINGDOM: James Macgill
52   *             mailto:j.macgill@geog.leeds.ac.uk
53   *
54   *     FRANCE: Surveillance de l'Environnement Assistée par Satellite
55   *             Institut de Recherche pour le Développement / US-Espace
56   *             mailto:seasnet@teledetection.fr
57   *
58   *     CANADA: Observatoire du Saint-Laurent
59   *             Institut Maurice-Lamontagne
60   *             mailto:osl@osl.gc.ca
61   */
62  package org.geotools.resources;
63  
64  import org.geotools.resources.gui.ResourceKeys;
65  // Geotools dependencies
66  import org.geotools.resources.gui.Resources;
67  
68  import java.awt.Component;
69  import java.awt.Dialog;
70  import java.awt.Dimension;
71  import java.awt.EventQueue;
72  import java.awt.Frame;
73  import java.awt.event.ActionListener;
74  import java.awt.event.WindowListener;
75  
76  import java.lang.reflect.InvocationTargetException;
77  import java.lang.reflect.UndeclaredThrowableException;
78  
79  import javax.swing.Action;
80  import javax.swing.JButton;
81  import javax.swing.JComponent;
82  import javax.swing.JDesktopPane;
83  import javax.swing.JDialog;
84  import javax.swing.JFrame;
85  import javax.swing.JInternalFrame;
86  import javax.swing.JOptionPane;
87  import javax.swing.JTextArea;
88  import javax.swing.LookAndFeel;
89  
90  
91  /**
92   * A collection of utility methods for Swing.  All <code>show*</code> methods delegate
93   * their work to the corresponding method in {@link JOptionPane}, with two differences:
94   *
95   * <ul>
96   *   <li><code>SwingUtilities</code>'s method may be invoked from any thread. If they
97   *       are invoked from a non-Swing thread, execution will be delegate to the Swing
98   *       thread and the calling thread will block until completion.</li>
99   *   <li>If a parent component is a {@link JDesktopPane}, dialogs will be rendered as
100  *       internal frames instead of frames.</li>
101  * </ul>
102  *
103  * @version $Id: SwingUtilities.java,v 1.5 2003/02/09 23:38:12 lbruand Exp $
104  * @author Martin Desruisseaux
105  */
106 public final class SwingUtilities {
107     public static final String CVSID = "$Id: SwingUtilities.java,v 1.5 2003/02/09 23:38:12 lbruand Exp $";
108 
109     /**
110      * Do not allow any instance
111      * of this class to be created.
112      */
113     private SwingUtilities() {
114     }
115 
116     /**
117      * Insert a Swing component into a frame. The kind of frame depends on the owner:
118      *
119      * <ul>
120      *   <li>If <code>owner</code> or one of its parent is a {@link JDesktopPane},
121      *       then <code>panel</code> is added into a {@link JInternalFrame}.</li>
122      *   <li>If <code>owner</code> or one of its parent is a {@link Frame} or a {@link Dialog},
123      *       then <code>panel</code> is added into a {@link JDialog}.</li>
124      *   <li>Otherwise, <code>panel</code> is added into a {@link JFrame}.</li>
125      * </ul>
126      *
127      * @param  owner The frame's owner, or <code>null</code> if none.
128      * @param  panel The panel to insert into a frame.
129      * @param  title The frame's title.
130      * @param  listener A listener to receives frame events.  If non-null, then this listener will
131      *         be registered to whatever kind of frame this method will constructs. In the special
132      *         case where this method constructs an {@linkplain JInternalFrame internal frame} and
133      *         the <code>listener</code> is not an instance of {@link InternalFrameListener}, then
134      *         this method will wrap the <code>listener</code> into an
135      *         <code>InternalFrameListener</code>.
136      * @return The frame. This frame is not initially visible. The method
137      *         <code>Component.setVisible(true)</code> must be invoked
138      *         in order to show the frame.
139      */
140     public static Component toFrame(Component owner, final JComponent panel, final String title,
141         final WindowListener listener) {
142         while (owner != null) {
143             if (owner == panel) {
144                 throw new IllegalArgumentException();
145             }
146 
147             if (owner instanceof JDesktopPane) {
148                 final JInternalFrame frame = new JInternalFrame(title, true, true, true, true);
149                 frame.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
150                 frame.addInternalFrameListener(InternalWindowListener.wrap(listener));
151                 ((JDesktopPane) owner).add(frame);
152                 frame.getContentPane().add(panel);
153                 frame.pack();
154                 return frame;
155             }
156 
157             if (owner instanceof Frame) {
158                 final JDialog dialog = new JDialog((Frame) owner, title);
159                 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
160                 dialog.addWindowListener(listener);
161                 dialog.getContentPane().add(panel);
162                 dialog.pack();
163                 return dialog;
164             }
165 
166             if (owner instanceof Dialog) {
167                 final JDialog dialog = new JDialog((Dialog) owner, title);
168                 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
169                 dialog.addWindowListener(listener);
170                 dialog.getContentPane().add(panel);
171                 dialog.pack();
172                 return dialog;
173             }
174             owner = owner.getParent();
175         }
176 
177         //
178         // Add the panel as a standalone window.
179         // This window has its own button on the task bar.
180         //
181         final JFrame frame = new JFrame(title);
182         frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
183         frame.addWindowListener(listener);
184         frame.getContentPane().add(panel);
185         frame.pack();
186         return frame;
187     }
188 
189     /**
190      * Brings up a "Ok/Cancel" dialog with no icon. This method can be invoked
191      * from any thread and blocks until the user click on "Ok" or "Cancel".
192      *
193      * @param  owner  The parent component. Dialog will apears on top of this owner.
194      * @param  dialog The dialog content to show.
195      * @param  title  The title string for the dialog.
196      * @return <code>true</code> if user clicked "Ok", <code>false</code> otherwise.
197      */
198     public static boolean showOptionDialog(final Component owner, final Object dialog, final String title) {
199         return showOptionDialog(owner, dialog, title, null);
200     }
201 
202     /**
203      * Brings up a "Ok/Cancel/Reset" dialog with no icon. This method can be invoked
204      * from any thread and blocks until the user click on "Ok" or "Cancel".
205      *
206      * @param  owner  The parent component. Dialog will apears on top of this owner.
207      * @param  dialog The dialog content to show.
208      * @param  title  The title string for the dialog.
209      * @param  reset  Action to execute when user press "Reset", or <code>null</code>
210      *                if there is no "Reset" button. If <code>reset</code> is an
211      *                instance of {@link Action}, the button label will be set
212      *                according the action's properties.
213      * @return <code>true</code> if user clicked "Ok", <code>false</code> otherwise.
214      */
215     public static boolean showOptionDialog(final Component owner, final Object dialog, final String title,
216         final ActionListener reset) {
217         // Delegate to Swing thread if this method
218         // is invoked from an other thread.
219         if (!EventQueue.isDispatchThread()) {
220             final boolean[] result = new boolean[1];
221             invokeAndWait(new Runnable() {
222                     public void run() {
223                         result[0] = showOptionDialog(owner, dialog, title, reset);
224                     }
225                 });
226             return result[0];
227         }
228 
229         // Construct the buttons bar.
230         Object[] options = null;
231         Object initialValue = null;
232         int okChoice = JOptionPane.OK_OPTION;
233 
234         if (reset != null) {
235             final Resources resources = Resources.getResources((owner != null) ? owner.getLocale() : null);
236             final JButton button;
237 
238             if (reset instanceof Action) {
239                 button = new JButton((Action) reset);
240             } else {
241                 button = new JButton(resources.getString(ResourceKeys.RESET));
242                 button.addActionListener(reset);
243             }
244             options = new Object[] {
245                     resources.getString(ResourceKeys.OK), resources.getString(ResourceKeys.CANCEL), button
246                 };
247             initialValue = options[okChoice = 0];
248         }
249 
250         // Bring ups the dialog box.
251         final int choice;
252 
253         if (JOptionPane.getDesktopPaneForComponent(owner) != null) {
254             choice = JOptionPane.showInternalOptionDialog(owner,   // Composante parente
255                     dialog,   // Message
256                     title,   // Titre de la boîte de dialogue
257                     JOptionPane.OK_CANCEL_OPTION,   // Boutons à placer
258                     JOptionPane.PLAIN_MESSAGE,   // Type du message
259                     null,   // Icone
260                     options,   // Liste des boutons
261                     initialValue);   // Bouton par défaut
262         } else {
263             choice = JOptionPane.showOptionDialog(owner,   // Composante parente
264                     dialog,   // Message
265                     title,   // Titre de la boîte de dialogue
266                     JOptionPane.OK_CANCEL_OPTION,   // Boutons à placer
267                     JOptionPane.PLAIN_MESSAGE,   // Type du message
268                     null,   // Icone
269                     options,   // Liste des boutons
270                     initialValue);   // Bouton par défaut
271         }
272         return choice == okChoice;
273     }
274 
275     /**
276      * Brings up a message dialog with a "Ok" button. This method can be invoked
277      * from any thread and blocks until the user click on "Ok".
278      *
279      * @param  owner   The parent component. Dialog will apears on top of this owner.
280      * @param  message The dialog content to show.
281      * @param  title   The title string for the dialog.
282      * @param  type    The message type
283      *                ({@link JOptionPane#ERROR_MESSAGE},
284      *                 {@link JOptionPane#INFORMATION_MESSAGE},
285      *                 {@link JOptionPane#WARNING_MESSAGE},
286      *                 {@link JOptionPane#QUESTION_MESSAGE} or
287      *                 {@link JOptionPane#PLAIN_MESSAGE}).
288      */
289     public static void showMessageDialog(final Component owner, final Object message, final String title, final int type) {
290         if (!EventQueue.isDispatchThread()) {
291             invokeAndWait(new Runnable() {
292                     public void run() {
293                         showMessageDialog(owner, message, title, type);
294                     }
295                 });
296             return;
297         }
298 
299         if (JOptionPane.getDesktopPaneForComponent(owner) != null) {
300             JOptionPane.showInternalMessageDialog(owner,   // Composante parente
301                 message,   // Message
302                 title,   // Titre de la boîte de dialogue
303                 type);   // Type du message
304         } else {
305             JOptionPane.showMessageDialog(owner,   // Composante parente
306                 message,   // Message
307                 title,   // Titre de la boîte de dialogue
308                 type);   // Type du message
309         }
310     }
311 
312     /**
313      * Brings up a confirmation dialog with "Yes/No" buttons. This method can be
314      * invoked from any thread and blocks until the user click on "Yes" or "No".
315      *
316      * @param  owner   The parent component. Dialog will apears on top of this owner.
317      * @param  message The dialog content to show.
318      * @param  title   The title string for the dialog.
319      * @param  type    The message type
320      *                ({@link JOptionPane#ERROR_MESSAGE},
321      *                 {@link JOptionPane#INFORMATION_MESSAGE},
322      *                 {@link JOptionPane#WARNING_MESSAGE},
323      *                 {@link JOptionPane#QUESTION_MESSAGE} or
324      *                 {@link JOptionPane#PLAIN_MESSAGE}).
325      * @return <code>true</code> if user clicked on "Yes", <code>false</code> otherwise.
326      */
327     public static boolean showConfirmDialog(final Component owner, final Object message, final String title,
328         final int type) {
329         if (!EventQueue.isDispatchThread()) {
330             final boolean[] result = new boolean[1];
331             invokeAndWait(new Runnable() {
332                     public void run() {
333                         result[0] = showConfirmDialog(owner, message, title, type);
334                     }
335                 });
336             return result[0];
337         }
338         final int choice;
339 
340         if (JOptionPane.getDesktopPaneForComponent(owner) != null) {
341             choice = JOptionPane.showInternalConfirmDialog(owner,   // Composante parente
342                     message,   // Message
343                     title,   // Titre de la boîte de dialogue
344                     JOptionPane.YES_NO_OPTION,   // Boutons à faire apparaître
345                     type);   // Type du message
346         } else {
347             choice = JOptionPane.showConfirmDialog(owner,   // Composante parente
348                     message,   // Message
349                     title,   // Titre de la boîte de dialogue
350                     JOptionPane.YES_NO_OPTION,   // Boutons à faire apparaître
351                     type);   // Type du message
352         }
353         return choice == JOptionPane.YES_OPTION;
354     }
355 
356     /**
357      * Retourne une étiquette pour la composante spécifiée.
358      * Le texte de l'étiquette pourra éventuellement être
359      * distribué sur plusieurs lignes.
360      *
361      * @param owner Composante pour laquelle on construit une étiquette.
362      *              L'étiquette aura la même largeur que <code>owner</code>.
363      * @param text  Texte à placer dans l'étiquette.
364      */
365     public static JComponent getMultilineLabelFor(final JComponent owner, final String text) {
366         final JTextArea label = new JTextArea(text);
367         final Dimension size = owner.getPreferredSize();
368         size.height = label.getMaximumSize().height;
369         label.setMaximumSize(size);
370         label.setWrapStyleWord(true);
371         label.setLineWrap(true);
372         label.setEditable(false);
373         label.setFocusable(false);
374         label.setOpaque(false);
375         label.setBorder(null);   // Certains L&F placent une bordure.
376         LookAndFeel.installColorsAndFont(label, "Label.background", "Label.foreground", "Label.font");
377         return label;
378     }
379 
380     /**
381      * Causes runnable to have its run method called in the dispatch thread of
382      * the event queue. This will happen after all pending events are processed.
383      * The call blocks until this has happened.
384      */
385     public static void invokeAndWait(final Runnable runnable) {
386         if (EventQueue.isDispatchThread()) {
387             runnable.run();
388         } else {
389             try {
390                 EventQueue.invokeAndWait(runnable);
391             } catch (InterruptedException exception) {
392                 // Someone don't want to let us sleep. Go back to work.
393             } catch (InvocationTargetException target) {
394                 final Throwable exception = target.getTargetException();
395 
396                 if (exception instanceof RuntimeException) {
397                     throw (RuntimeException) exception;
398                 }
399 
400                 if (exception instanceof Error) {
401                     throw (Error) exception;
402                 }
403 
404                 // Should not happen, since {@link Runnable#run} do not allow checked exception.
405                 throw new UndeclaredThrowableException(exception, exception.getLocalizedMessage());
406             }
407         }
408     }
409 }