001/* DefaultDesktopManager.java --
002   Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing;
040
041import java.awt.Component;
042import java.awt.Container;
043import java.awt.Dimension;
044import java.awt.Insets;
045import java.awt.Rectangle;
046import java.beans.PropertyVetoException;
047import java.io.Serializable;
048
049import javax.swing.JInternalFrame.JDesktopIcon;
050
051/**
052 * The default implementation of DesktopManager for
053 * Swing. It implements the basic beaviours for JInternalFrames in arbitrary
054 * parents. The methods provided by the class are not meant to be called by
055 * the user, instead, the JInternalFrame methods will call these methods.
056 */
057public class DefaultDesktopManager implements DesktopManager, Serializable
058{
059  /** DOCUMENT ME! */
060  private static final long serialVersionUID = 4657624909838017887L;
061
062  /** The property change event fired when the wasIcon property changes. */
063  static final String WAS_ICON_ONCE_PROPERTY = "wasIconOnce";
064
065  /**
066   * The method of dragging used by the JDesktopPane that parents the
067   * JInternalFrame that is being dragged.
068   */
069  private int currentDragMode = 0;
070
071  /**
072   * The cache of the bounds used to draw the outline rectangle when
073   * OUTLINE_DRAG_MODE is used.
074   */
075  private transient Rectangle dragCache = new Rectangle();
076
077  /**
078   * A cached JDesktopPane that is stored when the JInternalFrame is initially
079   * dragged.
080   */
081  private transient Container pane;
082
083  /**
084   * An array of Rectangles that holds the bounds of the JDesktopIcons in the
085   * JDesktopPane when looking for where to place a new icon.
086   */
087  private transient Rectangle[] iconRects;
088
089  /**
090   * This creates a new DefaultDesktopManager object.
091   */
092  public DefaultDesktopManager()
093  {
094    // Nothing to do here.
095  }
096
097  /**
098   * This method is not normally called since the user will typically add the
099   * JInternalFrame to a Container. If this is called, it will try to
100   * determine the parent of the JInternalFrame and remove any icon that
101   * represents this JInternalFrame and add this JInternalFrame.
102   *
103   * @param frame The JInternalFrame to open.
104   */
105  public void openFrame(JInternalFrame frame)
106  {
107    Container c = frame.getParent();
108    if (c == null)
109      c = frame.getDesktopIcon().getParent();
110    if (c == null)
111      return;
112
113    c.remove(frame.getDesktopIcon());
114    c.add(frame);
115    frame.setVisible(true);
116  }
117
118  /**
119   * This method removes the JInternalFrame and JDesktopIcon (if one is
120   * present) from their parents.
121   *
122   * @param frame The JInternalFrame to close.
123   */
124  public void closeFrame(JInternalFrame frame)
125  {
126    Container c = frame.getParent();
127    if (c != null)
128      {
129        if (frame.isIcon())
130          c.remove(frame.getDesktopIcon());
131        else
132          c.remove(frame);
133        c.repaint();
134      }
135  }
136
137  /**
138   * This method resizes the JInternalFrame to match its parent's bounds.
139   *
140   * @param frame The JInternalFrame to maximize.
141   */
142  public void maximizeFrame(JInternalFrame frame)
143  {
144    // Can't maximize from iconified state.
145    // It can only return to maximized state, but that would fall under
146    // deiconify.
147    if (frame.isIcon())
148      return;
149    frame.setNormalBounds(frame.getBounds());
150
151    Container p = frame.getParent();
152    if (p != null)
153      {
154        Rectangle pBounds = p.getBounds();
155        Insets insets = p.getInsets();
156        pBounds.width -= insets.left + insets.right;
157        pBounds.height -= insets.top + insets.bottom;
158
159        setBoundsForFrame(frame, 0, 0, pBounds.width, pBounds.height);
160      }
161    if (p instanceof JDesktopPane)
162      ((JDesktopPane) p).setSelectedFrame(frame);
163    else
164      {
165        try
166          {
167            frame.setSelected(true);
168          }
169        catch (PropertyVetoException e)
170          {
171            // Do nothing.
172          }
173      }
174  }
175
176  /**
177   * This method restores the JInternalFrame's bounds to what they were
178   * previous to the setMaximize call.
179   *
180   * @param frame The JInternalFrame to minimize.
181   */
182  public void minimizeFrame(JInternalFrame frame)
183  {
184    Rectangle normalBounds = frame.getNormalBounds();
185
186    JDesktopPane p = frame.getDesktopPane();
187    if (p != null)
188      p.setSelectedFrame(frame);
189    else
190      {
191        try
192          {
193            frame.setSelected(true);
194          }
195        catch (PropertyVetoException e)
196          {
197            // Do nothing.
198          }
199      }
200
201    setBoundsForFrame(frame, normalBounds.x, normalBounds.y,
202                      normalBounds.width, normalBounds.height);
203  }
204
205  /**
206   * This method removes the JInternalFrame from its parent and adds its
207   * JDesktopIcon representation.
208   *
209   * @param frame The JInternalFrame to iconify.
210   */
211  public void iconifyFrame(JInternalFrame frame)
212  {
213    JDesktopPane p = frame.getDesktopPane();
214    JDesktopIcon icon = frame.getDesktopIcon();
215    if (p != null && p.getSelectedFrame() == frame)
216      p.setSelectedFrame(null);
217    else
218      {
219        try
220          {
221            frame.setSelected(false);
222          }
223        catch (PropertyVetoException e)
224          {
225            // Do nothing if attempt is vetoed.
226          }
227      }
228
229    Container c = frame.getParent();
230
231    if (!wasIcon(frame))
232      {
233        Rectangle r = getBoundsForIconOf(frame);
234        icon.setBounds(r);
235        setWasIcon(frame, Boolean.TRUE);
236      }
237
238    if (c != null)
239      {
240        if (icon != null)
241          {
242            c.add(icon);
243            icon.setVisible(true);
244          }
245        Rectangle b = frame.getBounds();
246        c.remove(frame);
247        c.repaint(b.x, b.y, b.width, b.height);
248      }
249  }
250
251  /**
252   * This method removes the JInternalFrame's JDesktopIcon representation and
253   * adds the JInternalFrame back to its parent.
254   *
255   * @param frame The JInternalFrame to deiconify.
256   */
257  public void deiconifyFrame(JInternalFrame frame)
258  {
259    JDesktopIcon icon = frame.getDesktopIcon();
260    Container c = icon.getParent();
261
262    removeIconFor(frame);
263    c.add(frame);
264    frame.setVisible(true);
265
266    if (!frame.isSelected())
267      {
268        JDesktopPane p = frame.getDesktopPane();
269        if (p != null)
270          p.setSelectedFrame(frame);
271        else
272          {
273            try
274              {
275                frame.setSelected(true);
276              }
277            catch (PropertyVetoException e)
278              {
279                // Do nothing.
280              }
281          }
282      }
283
284    c.invalidate();
285  }
286
287  /**
288   * This method activates the JInternalFrame by moving it to the front and
289   * selecting it.
290   *
291   * @param frame The JInternalFrame to activate.
292   */
293  public void activateFrame(JInternalFrame frame)
294  {
295    JDesktopPane p = frame.getDesktopPane();
296    JInternalFrame active = null;
297    if (p != null)
298      active = p.getSelectedFrame();
299    if (active == null)
300      {
301        if (p != null)
302          {
303            p.setSelectedFrame(frame);
304          }
305      }
306    else if (active != frame)
307      {
308        if (active.isSelected())
309          {
310            try
311              {
312                active.setSelected(false);
313              }
314            catch (PropertyVetoException ex)
315              {
316                // Not allowed.
317              }
318          }
319        if (p != null)
320          {
321            p.setSelectedFrame(frame);
322          }
323        
324      }
325    frame.toFront();
326  }
327
328  /**
329   * This method is called when the JInternalFrame loses focus.
330   *
331   * @param frame The JInternalFram to deactivate.
332   */
333  public void deactivateFrame(JInternalFrame frame)
334  {
335    JDesktopPane p = frame.getDesktopPane();
336    if (p != null)
337      {
338        if (p.getSelectedFrame() == frame)
339          p.setSelectedFrame(null);
340      }
341    else
342      {
343        try
344          {
345            frame.setSelected(false);
346          }
347        catch (PropertyVetoException e)
348          {
349            // Do nothing if attempt is vetoed.
350          }
351      }
352  }
353
354  /**
355   * This method is called to indicate that the DesktopManager should prepare
356   * to drag the JInternalFrame. Any state information needed to drag the
357   * frame will be prepared now.
358   *
359   * @param component The JComponent to drag, usually a JInternalFrame.
360   */
361  public void beginDraggingFrame(JComponent component)
362  {
363    if (component instanceof JDesktopIcon)
364      pane = ((JDesktopIcon) component).getInternalFrame().getDesktopPane();
365    else
366      pane = ((JInternalFrame) component).getDesktopPane();
367    if (pane == null)
368      return;
369
370    dragCache = component.getBounds();
371
372    if (! (pane instanceof JDesktopPane))
373      currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
374    else
375      currentDragMode = ((JDesktopPane) pane).getDragMode();
376  }
377
378  /**
379   * This method is called to drag the JInternalFrame to a new location.
380   *
381   * @param component The JComponent to drag, usually a JInternalFrame.
382   *
383   * @param newX The new x coordinate.
384   * @param newY The new y coordinate.
385   */
386  public void dragFrame(JComponent component, int newX, int newY)
387  {
388    if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
389      {
390        // FIXME: Do outline drag mode painting.
391      }
392    else
393      {
394        Rectangle b = component.getBounds();
395        if (component instanceof JDesktopIcon)
396          component.setBounds(newX, newY, b.width, b.height);
397        else
398          setBoundsForFrame((JInternalFrame) component, newX, newY, b.width,
399                            b.height);
400      }
401  }
402
403  /**
404   * This method indicates that the dragging is done. Any state information
405   * stored by the DesktopManager can be cleared.
406   *
407   * @param component The JComponent that has finished dragging.
408   */
409  public void endDraggingFrame(JComponent component)
410  {
411    if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
412      {
413        setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
414                          dragCache.width, dragCache.height);
415        pane = null;
416        dragCache = null;
417        component.repaint();        
418      }
419  }
420
421  /**
422   * This method is called to indicate that the given JComponent will be
423   * resized. Any state information necessary to resize the JComponent will
424   * be prepared now.
425   *
426   * @param component The JComponent to resize, usually a JInternalFrame.
427   * @param direction The direction to drag in (a SwingConstant).
428   */
429  public void beginResizingFrame(JComponent component, int direction)
430  {
431    pane = ((JInternalFrame) component).getDesktopPane();
432    if (pane == null)
433      return;
434
435    dragCache = component.getBounds();
436    if (! (pane instanceof JDesktopPane))
437      currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
438    else
439      currentDragMode = ((JDesktopPane) pane).getDragMode();
440  }
441
442  /**
443   * This method resizes the give JComponent.
444   *
445   * @param component The JComponent to resize.
446   * @param newX The new x coordinate.
447   * @param newY The new y coordinate.
448   * @param newWidth The new width.
449   * @param newHeight The new height.
450   */
451  public void resizeFrame(JComponent component, int newX, int newY,
452                          int newWidth, int newHeight)
453  {
454    dragCache.setBounds(newX, newY, newWidth, newHeight);
455
456    if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
457      {
458        // FIXME: Do outline drag painting.
459      }
460    else
461      setBoundsForFrame(component, dragCache.x, dragCache.y, dragCache.width,
462                        dragCache.height);
463  }
464
465  /**
466   * This method is called to indicate that the given JComponent has finished
467   * dragging. Any state information stored by the DesktopManager can be
468   * cleared.
469   *
470   * @param component The JComponent that finished resizing.
471   */
472  public void endResizingFrame(JComponent component)
473  {
474    if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
475      {
476        setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
477                          dragCache.width, dragCache.height);
478        pane = null;
479        dragCache = null;
480        component.repaint();        
481      }
482  }
483
484  /**
485   * This method calls setBounds with the given parameters and repaints the
486   * JComponent.
487   *
488   * @param component The JComponent to set bounds for.
489   * @param newX The new x coordinate.
490   * @param newY The new y coordinate.
491   * @param newWidth The new width.
492   * @param newHeight The new height.
493   */
494  public void setBoundsForFrame(JComponent component, int newX, int newY,
495                                int newWidth, int newHeight)
496  {
497    component.setBounds(newX, newY, newWidth, newHeight);
498  }
499
500  /**
501   * This is a helper method that removes the JDesktopIcon of the given
502   * JInternalFrame from the parent.
503   *
504   * @param frame The JInternalFrame to remove an icon for.
505   */
506  protected void removeIconFor(JInternalFrame frame)
507  {
508    JDesktopIcon icon = frame.getDesktopIcon();
509    Container c = icon.getParent();
510    if (c != null && icon != null)
511      {
512        Rectangle b = icon.getBounds();
513        c.remove(icon);
514        c.repaint(b.x, b.y, b.width, b.height);
515      }
516  }
517
518  /**
519   * This method is called by iconifyFrame to determine the bounds of the
520   * JDesktopIcon for the given JInternalFrame.
521   *
522   * @param frame The JInternalFrame to find the bounds of its JDesktopIcon
523   *        for.
524   *
525   * @return The bounds of the JDesktopIcon.
526   */
527  protected Rectangle getBoundsForIconOf(JInternalFrame frame)
528  {
529    // IconRects has no order to it.
530    // The icon _must_ be placed in the first free slot (working from 
531    // the bottom left corner)
532    // The icon also must not be placed where another icon is placed 
533    // (regardless whether that frame is an icon currently or not)
534    JDesktopPane desktopPane = frame.getDesktopPane();
535
536    if (desktopPane == null)
537      return frame.getDesktopIcon().getBounds();
538
539    Rectangle paneBounds = desktopPane.getBounds();
540    Insets insets = desktopPane.getInsets();
541    Dimension pref = frame.getDesktopIcon().getPreferredSize();
542
543    Component[] frames = desktopPane.getComponents();
544
545    int count = 0;
546    for (int i = 0, j = 0; i < frames.length; i++)
547      if (frames[i] instanceof JDesktopIcon
548          || frames[i] instanceof JInternalFrame
549          && ((JInternalFrame) frames[i]).getWasIcon() && frames[i] != frame)
550        count++;
551    iconRects = new Rectangle[count];
552    for (int i = 0, j = 0; i < frames.length; i++)
553      if (frames[i] instanceof JDesktopIcon)
554        iconRects[--count] = frames[i].getBounds();
555      else if (frames[i] instanceof JInternalFrame
556               && ((JInternalFrame) frames[i]).getWasIcon()
557               && frames[i] != frame)
558        iconRects[--count] = ((JInternalFrame) frames[i])
559                                                 .getDesktopIcon().getBounds();
560
561    int startingX = insets.left;
562    int startingY = paneBounds.height - insets.bottom - pref.height;
563    Rectangle ideal = new Rectangle(startingX, startingY, pref.width,
564                                    pref.height);
565    boolean clear = true;
566
567    while (iconRects.length > 0)
568      {
569        clear = true;
570        for (int i = 0; i < iconRects.length; i++)
571          {
572            if (iconRects[i] != null && iconRects[i].intersects(ideal))
573              {
574                clear = false;
575                break;
576              }
577          }
578        if (clear)
579          return ideal;
580
581        startingX += pref.width;
582        if (startingX + pref.width > paneBounds.width - insets.right)
583          {
584            startingX = insets.left;
585            startingY -= pref.height;
586          }
587        ideal.setBounds(startingX, startingY, pref.width, pref.height);
588      }
589
590    return ideal;
591  }
592
593  /**
594   * This method sets the bounds of the JInternalFrame right before the
595   * maximizeFrame call.
596   *
597   * @param frame The JInternalFrame being maximized.
598   * @param rect The normal bounds.
599   */
600  protected void setPreviousBounds(JInternalFrame frame, Rectangle rect)
601  {
602    frame.setNormalBounds(rect);
603  }
604
605  /**
606   * This method returns the normal bounds of the JInternalFrame from before
607   * the maximize call.
608   *
609   * @param frame The JInternalFrame that is being restored.
610   *
611   * @return The previous bounds of the JInternalFrame.
612   */
613  protected Rectangle getPreviousBounds(JInternalFrame frame)
614  {
615    return frame.getNormalBounds();
616  }
617
618  /**
619   * This method sets the value to true if the given JInternalFrame has been
620   * iconized and the bounds of its DesktopIcon are valid.
621   *
622   * @param frame The JInternalFrame for the JDesktopIcon.
623   * @param value True if the JInternalFrame has been iconized and the bounds
624   *        of the JDesktopIcon are valid.
625   */
626  protected void setWasIcon(JInternalFrame frame, Boolean value)
627  {
628    frame.setWasIcon(value.booleanValue(), WAS_ICON_ONCE_PROPERTY);
629  }
630
631  /**
632   * This method returns true if the given JInternalFrame has been iconized
633   * and the bounds of its DesktopIcon are valid.
634   *
635   * @param frame The JInternalFrame for the JDesktopIcon.
636   *
637   * @return True if the given JInternalFrame has been iconized and the bounds
638   *         of its DesktopIcon are valid.
639   */
640  protected boolean wasIcon(JInternalFrame frame)
641  {
642    return frame.getWasIcon();
643  }
644}