001    /*
002     * $Id: JXTaskPane.java,v 1.8 2006/05/14 08:12:18 dmouse Exp $
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     */
021    package org.jdesktop.swingx;
022    
023    import java.awt.BorderLayout;
024    import java.awt.Component;
025    import java.awt.Container;
026    import java.awt.LayoutManager;
027    import java.beans.PropertyChangeEvent;
028    import java.beans.PropertyChangeListener;
029    
030    import javax.swing.Action;
031    import javax.swing.Icon;
032    import javax.swing.JComponent;
033    import javax.swing.JPanel;
034    import javax.swing.UIManager;
035    
036    import org.jdesktop.swingx.plaf.JXTaskPaneAddon;
037    import org.jdesktop.swingx.plaf.LookAndFeelAddons;
038    import org.jdesktop.swingx.plaf.TaskPaneUI;
039    
040    /**
041     * <code>JXTaskPane</code> is a container for tasks and other
042     * arbitrary components.
043     * 
044     * <p>
045     * Several <code>JXTaskPane</code>s are usually grouped together within a
046     * {@link org.jdesktop.swingx.JXTaskPaneContainer}. However it is not mandatory
047     * to use a JXTaskPaneContainer as the parent for JXTaskPane. The JXTaskPane can
048     * be added to any other container. See
049     * {@link org.jdesktop.swingx.JXTaskPaneContainer} to understand the benefits of
050     * using it as the parent container.
051     * 
052     * <p>
053     * <code>JXTaskPane</code> provides control to expand and
054     * collapse the content area in order to show or hide the task list. It can have an
055     * <code>icon</code>, a <code>title</code> and can be marked as
056     * <code>special</code>. Marking a <code>JXTaskPane</code> as
057     * <code>special</code> ({@link #setSpecial(boolean)} is only a hint for
058     * the pluggable UI which will usually paint it differently (by example by
059     * using another color for the border of the pane).
060     * 
061     * <p> 
062     * When the JXTaskPane is expanded or collapsed, it will be
063     * animated with a fade effect. The animated can be disabled on a per
064     * component basis through {@link #setAnimated(boolean)}.
065     * 
066     * To disable the animation for all newly created <code>JXTaskPane</code>,
067     * use the UIManager property:
068     * <code>UIManager.put("TaskPane.animate", Boolean.FALSE);</code>.
069     * 
070     * <p>
071     * Example:
072     * <pre>
073     * <code>
074     * JXFrame frame = new JXFrame();
075     * 
076     * // a container to put all JXTaskPane together
077     * JXTaskPaneContainer taskPaneContainer = new JXTaskPaneContainer();
078     * 
079     * // create a first taskPane with common actions
080     * JXTaskPane actionPane = new JXTaskPane();
081     * actionPane.setTitle("Files and Folders");
082     * actionPane.setSpecial(true);
083     * 
084     * // actions can be added, an hyperlink will be created
085     * Action renameSelectedFile = createRenameFileAction();
086     * actionPane.add(renameSelectedFile);
087     * actionPane.add(createDeleteFileAction());
088     * 
089     * // add this taskPane to the taskPaneContainer
090     * taskPaneContainer.add(actionPane);
091     * 
092     * // create another taskPane, it will show details of the selected file
093     * JXTaskPane details = new JXTaskPane();
094     * details.setTitle("Details");
095     *  
096     * // add standard components to the details taskPane
097     * JLabel searchLabel = new JLabel("Search:");
098     * JTextField searchField = new JTextField("");
099     * details.add(searchLabel);
100     * details.add(searchField);
101     * 
102     * taskPaneContainer.add(details);
103     * 
104     * // put the action list on the left 
105     * frame.add(taskPaneContainer, BorderLayout.EAST);
106     * 
107     * // and a file browser in the middle
108     * frame.add(fileBrowser, BorderLayout.CENTER);
109     * 
110     * frame.pack().
111     * frame.setVisible(true);
112     * </code>
113     * </pre>
114     * 
115     * @see org.jdesktop.swingx.JXTaskPaneContainer
116     * @see org.jdesktop.swingx.JXCollapsiblePane
117     * @author <a href="mailto:fred@L2FProd.com">Frederic Lavigne</a>
118     * 
119     * @javabean.attribute
120     *          name="isContainer"
121     *          value="Boolean.TRUE"
122     *          rtexpr="true"
123     *          
124     * @javabean.attribute
125     *          name="containerDelegate"
126     *          value="getContentPane"
127     *          
128     * @javabean.class
129     *          name="JXTaskPane"
130     *          shortDescription="JXTaskPane is a container for tasks and other arbitrary components."
131     *          stopClass="java.awt.Component"
132     * 
133     * @javabean.icons
134     *          mono16="JXTaskPane16-mono.gif"
135     *          color16="JXTaskPane16.gif"
136     *          mono32="JXTaskPane32-mono.gif"
137     *          color32="JXTaskPane32.gif"
138     */
139    public class JXTaskPane extends JPanel implements
140      JXCollapsiblePane.JCollapsiblePaneContainer {
141    
142      /**
143       * JXTaskPane pluggable UI key <i>swingx/TaskPaneUI</i> 
144       */
145      public final static String uiClassID = "swingx/TaskPaneUI";
146      
147      // ensure at least the default ui is registered
148      static {
149        LookAndFeelAddons.contribute(new JXTaskPaneAddon());
150      }
151    
152      /**
153       * Used when generating PropertyChangeEvents for the "expanded" property
154       */
155      public static final String EXPANDED_CHANGED_KEY = "expanded";
156    
157      /**
158       * Used when generating PropertyChangeEvents for the "scrollOnExpand" property
159       */
160      public static final String SCROLL_ON_EXPAND_CHANGED_KEY = "scrollOnExpand";
161    
162      /**
163       * Used when generating PropertyChangeEvents for the "title" property
164       */
165      public static final String TITLE_CHANGED_KEY = "title";
166    
167      /**
168       * Used when generating PropertyChangeEvents for the "icon" property
169       */
170      public static final String ICON_CHANGED_KEY = "icon";
171    
172      /**
173       * Used when generating PropertyChangeEvents for the "special" property
174       */
175      public static final String SPECIAL_CHANGED_KEY = "special";
176    
177      /**
178       * Used when generating PropertyChangeEvents for the "animated" property
179       */
180      public static final String ANIMATED_CHANGED_KEY = "animated";
181    
182      private String title;
183      private Icon icon;
184      private boolean special;
185      private boolean expanded = true;
186      private boolean scrollOnExpand;
187    
188      private JXCollapsiblePane collapsePane;
189      
190      /**
191       * Creates a new empty <code>JXTaskPane</code>.
192       */
193      public JXTaskPane() {
194        collapsePane = new JXCollapsiblePane();
195        super.setLayout(new BorderLayout(0, 0));
196        super.addImpl(collapsePane, BorderLayout.CENTER, -1);
197        
198        updateUI();
199        setFocusable(true);
200        setOpaque(false);
201    
202        // disable animation if specified in UIManager
203        setAnimated(!Boolean.FALSE.equals(UIManager.get("TaskPane.animate")));
204        
205        // listen for animation events and forward them to registered listeners
206        collapsePane.addPropertyChangeListener(
207          JXCollapsiblePane.ANIMATION_STATE_KEY, new PropertyChangeListener() {
208            public void propertyChange(PropertyChangeEvent evt) {
209              JXTaskPane.this.firePropertyChange(evt.getPropertyName(), evt
210                .getOldValue(), evt.getNewValue());
211            }
212          });
213      }
214    
215      /**
216       * Returns the contentPane object for this JXTaskPane.
217       * @return the contentPane property
218       */
219      public Container getContentPane() {
220        return collapsePane.getContentPane();
221      }
222      
223      /**
224       * Notification from the <code>UIManager</code> that the L&F has changed.
225       * Replaces the current UI object with the latest version from the <code>UIManager</code>.
226       * 
227       * @see javax.swing.JComponent#updateUI
228       */
229      @Override
230      public void updateUI() {
231        // collapsePane is null when updateUI() is called by the "super()"
232        // constructor
233        if (collapsePane == null) {
234          return;
235        }
236        setUI((TaskPaneUI)LookAndFeelAddons.getUI(this, TaskPaneUI.class));
237      }
238      
239      /**
240       * Sets the L&F object that renders this component.
241       * 
242       * @param ui the <code>TaskPaneUI</code> L&F object
243       * @see javax.swing.UIDefaults#getUI
244       * 
245       * @beaninfo bound: true hidden: true description: The UI object that
246       * implements the taskpane group's LookAndFeel.
247       */
248      public void setUI(TaskPaneUI ui) {
249        super.setUI(ui);
250      }
251    
252      /**
253       * Returns the name of the L&F class that renders this component.
254       * 
255       * @return the string {@link #uiClassID}
256       * @see javax.swing.JComponent#getUIClassID
257       * @see javax.swing.UIDefaults#getUI
258       */
259      @Override
260      public String getUIClassID() {
261        return uiClassID;
262      }
263    
264      /**
265       * Returns the title currently displayed in the border of this pane.
266       * 
267       * @return the title currently displayed in the border of this pane
268       */
269      public String getTitle() {
270        return title;
271      }
272    
273      /**
274       * Sets the title to be displayed in the border of this pane.
275       * 
276       * @param title the title to be displayed in the border of this pane
277       * @javabean.property
278       *          bound="true"
279       *          preferred="true"
280       */
281      public void setTitle(String title) {
282        String old = this.title;
283        this.title = title;
284        firePropertyChange(TITLE_CHANGED_KEY, old, title);
285      }
286    
287      /**
288       * Returns the icon currently displayed in the border of this pane.
289       * 
290       * @return the icon currently displayed in the border of this pane
291       */
292      public Icon getIcon() {
293        return icon;
294      }
295    
296      /**
297       * Sets the icon to be displayed in the border of this pane. Some pluggable
298       * UIs may impose size constraints for the icon. A size of 16x16 pixels is
299       * the recommended icon size.
300       * 
301       * @param icon the icon to be displayed in the border of this pane
302       * @javabean.property
303       *          bound="true"
304       *          preferred="true"
305       */
306      public void setIcon(Icon icon) {
307        Icon old = this.icon;
308        this.icon = icon;
309        firePropertyChange(ICON_CHANGED_KEY, old, icon);
310      }
311    
312      /**
313       * Returns true if this pane is "special".
314       * 
315       * @return true if this pane is "special"
316       * @see #setSpecial(boolean)
317       */
318      public boolean isSpecial() {
319        return special;
320      }
321    
322      /**
323       * Sets this pane to be "special" or not. Marking a <code>JXTaskPane</code>
324       * as <code>special</code> is only a hint for the pluggable UI which will
325       * usually paint it differently (by example by using another color for the
326       * border of the pane).
327       * 
328       * <p>
329       * Usually the first JXTaskPane in a JXTaskPaneContainer is marked as special
330       * because it contains the default set of actions which can be executed given
331       * the current context.
332       * 
333       * @param special
334       *          true if this pane is "special", false otherwise
335       * @javabean.property bound="true" preferred="true"
336       */
337      public void setSpecial(boolean special) {
338        if (this.special != special) {
339          this.special = special;
340          firePropertyChange(SPECIAL_CHANGED_KEY, !special, special);
341        }
342      }
343    
344      /**
345       * Should this group be scrolled to be visible on expand.
346       * 
347       * @param scrollOnExpand true to scroll this group to be
348       * visible if this group is expanded.
349       * 
350       * @see #setExpanded(boolean)
351       * 
352       * @javabean.property
353       *          bound="true"
354       *          preferred="true"
355       */
356      public void setScrollOnExpand(boolean scrollOnExpand) {
357        if (this.scrollOnExpand != scrollOnExpand) {
358          this.scrollOnExpand = scrollOnExpand;
359          firePropertyChange(SCROLL_ON_EXPAND_CHANGED_KEY,
360            !scrollOnExpand, scrollOnExpand);
361        }
362      }
363      
364      /**
365       * Should this group scroll to be visible after
366       * this group was expanded.
367       * 
368       * @return true if we should scroll false if nothing
369       * should be done.
370       */
371      public boolean isScrollOnExpand() {
372        return scrollOnExpand;
373      }
374      
375      /**
376       * Expands or collapses this group.
377       * 
378       * @param expanded true to expand the group, false to collapse it
379       * @javabean.property
380       *          bound="true"
381       *          preferred="true"
382       */
383      public void setExpanded(boolean expanded) {
384        if (this.expanded != expanded) {
385          this.expanded = expanded;
386          collapsePane.setCollapsed(!expanded);
387          firePropertyChange(EXPANDED_CHANGED_KEY, !expanded, expanded);
388        }
389      }
390    
391      /**
392       * Returns true if this taskpane is expanded, false if it is collapsed.
393       * 
394       * @return true if this taskpane is expanded, false if it is collapsed.
395       */
396      public boolean isExpanded() {
397        return expanded;
398      }
399    
400      /**
401       * Enables or disables animation during expand/collapse transition.
402       * 
403       * @param animated
404       * @javabean.property
405       *          bound="true"
406       *          preferred="true"
407       */
408      public void setAnimated(boolean animated) {
409        if (isAnimated() != animated) {
410          collapsePane.setAnimated(animated);
411          firePropertyChange(ANIMATED_CHANGED_KEY, !isAnimated(), isAnimated());
412        }
413      }
414      
415      /**
416       * Returns true if this taskpane is animated during expand/collapse
417       * transition.
418       * 
419       * @return true if this taskpane is animated during expand/collapse
420       *         transition.
421       */
422      public boolean isAnimated() {
423        return collapsePane.isAnimated();
424      }
425      
426      /**
427       * Adds an action to this <code>JXTaskPane</code>. Returns a
428       * component built from the action. The returned component has been
429       * added to the <code>JXTaskPane</code>.
430       * 
431       * @param action
432       * @return a component built from the action
433       */
434      public Component add(Action action) {
435        Component c = ((TaskPaneUI)ui).createAction(action);
436        add(c);
437        return c;
438      }
439    
440      /**
441       * @see JXCollapsiblePane.JCollapsiblePaneContainer
442       */
443      public Container getValidatingContainer() {
444        return getParent();
445      }
446      
447      /**
448       * Overriden to redirect call to the content pane.
449       */
450      @Override
451      protected void addImpl(Component comp, Object constraints, int index) {
452        getContentPane().add(comp, constraints, index);
453      }
454    
455      /**
456       * Overriden to redirect call to the content pane.
457       */
458      @Override
459      public void setLayout(LayoutManager mgr) {
460        if (collapsePane != null) {
461          getContentPane().setLayout(mgr);
462        }
463      }
464      
465      /**
466       * Overriden to redirect call to the content pane
467       */
468      @Override
469      public void remove(Component comp) {
470        getContentPane().remove(comp);
471      }
472    
473      /**
474       * Overriden to redirect call to the content pane.
475       */
476      @Override
477      public void remove(int index) {
478        getContentPane().remove(index);
479      }
480      
481      /**
482       * Overriden to redirect call to the content pane.
483       */
484      @Override
485      public void removeAll() {
486        getContentPane().removeAll();
487      }
488      
489      /**
490       * @see JComponent#paramString()
491       */
492      @Override
493      protected String paramString() {
494        return super.paramString()
495          + ",title="
496          + getTitle()
497          + ",icon="
498          + getIcon()
499          + ",expanded="
500          + String.valueOf(isExpanded())
501          + ",special="
502          + String.valueOf(isSpecial())
503          + ",scrollOnExpand=" 
504          + String.valueOf(isScrollOnExpand())
505          + ",ui=" + getUI();
506      }
507    
508    }