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 }