001 /* DefaultDesktopManager.java --
002 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing;
040
041 import java.awt.Component;
042 import java.awt.Container;
043 import java.awt.Dimension;
044 import java.awt.Insets;
045 import java.awt.Rectangle;
046 import java.beans.PropertyVetoException;
047 import java.io.Serializable;
048
049 import 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 */
057 public 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 }