1. /*
  2. * @(#)DefaultDesktopManager.java 1.41 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. import java.awt.*;
  12. import java.beans.PropertyVetoException;
  13. import java.beans.PropertyChangeEvent;
  14. import javax.swing.border.Border;
  15. import java.awt.event.ComponentListener;
  16. import java.awt.event.ComponentAdapter;
  17. import java.awt.event.ComponentEvent;
  18. /** This is an implementaion of the DesktopManager. It currently implements a
  19. * the basic behaviors for managing JInternalFrames in an arbitrary parent.
  20. * JInternalFrames that are not children of a JDesktop will use this component
  21. * to handle their desktop-like actions.
  22. * @see JDesktopPane
  23. * @see JInternalFrame
  24. * @version 1.41 02/02/00
  25. * @author David Kloba
  26. * @author Steve Wilson
  27. */
  28. public class DefaultDesktopManager implements DesktopManager, java.io.Serializable {
  29. final static String HAS_BEEN_ICONIFIED_PROPERTY = "wasIconOnce";
  30. final static int DEFAULT_DRAG_MODE = 0;
  31. final static int OUTLINE_DRAG_MODE = 1;
  32. final static int FASTER_DRAG_MODE = 2;
  33. int dragMode = DEFAULT_DRAG_MODE;
  34. private transient Rectangle currentBounds = null;
  35. private transient Graphics desktopGraphics = null;
  36. private transient Rectangle desktopBounds = null;
  37. private transient Rectangle[] floatingItems = {};
  38. /** Normally this method will not be called. If it is, it
  39. * try to determine the appropriate parent from the desktopIcon of the frame.
  40. * Will remove the desktopIcon from its parent if it successfully adds the frame.
  41. */
  42. public void openFrame(JInternalFrame f) {
  43. if(f.getDesktopIcon().getParent() != null) {
  44. f.getDesktopIcon().getParent().add(f);
  45. removeIconFor(f);
  46. }
  47. }
  48. /** Removes the frame, and if necessary the desktopIcon, from its parent. */
  49. public void closeFrame(JInternalFrame f) {
  50. boolean findNext = f.isSelected();
  51. Container c = f.getParent();
  52. if (findNext)
  53. try { f.setSelected(false); } catch (PropertyVetoException e2) { }
  54. if(c != null) {
  55. c.remove(f);
  56. c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
  57. }
  58. removeIconFor(f);
  59. if(f.getNormalBounds() != null)
  60. f.setNormalBounds(null);
  61. if(wasIcon(f))
  62. setWasIcon(f, null);
  63. if (findNext) activateNextFrame(c);
  64. }
  65. /** Resizes the frame to fill its parents bounds. */
  66. public void maximizeFrame(JInternalFrame f) {
  67. Rectangle p;
  68. if(!f.isIcon()) {
  69. p = f.getParent().getBounds();
  70. } else {
  71. Container c = f.getDesktopIcon().getParent();
  72. if(c == null)
  73. return;
  74. p = c.getBounds();
  75. try { f.setIcon(false); } catch (PropertyVetoException e2) { }
  76. }
  77. f.setNormalBounds(f.getBounds());
  78. setBoundsForFrame(f, 0, 0, p.width, p.height);
  79. try { f.setSelected(true); } catch (PropertyVetoException e2) { }
  80. removeIconFor(f);
  81. }
  82. /** Restores the frame back to its size and position prior to a maximizeFrame()
  83. * call.
  84. */
  85. public void minimizeFrame(JInternalFrame f) {
  86. if((f.getNormalBounds()) != null) {
  87. Rectangle r = f.getNormalBounds();
  88. f.setNormalBounds(null);
  89. try { f.setSelected(true); } catch (PropertyVetoException e2) { }
  90. setBoundsForFrame(f, r.x, r.y, r.width, r.height);
  91. }
  92. removeIconFor(f);
  93. }
  94. /** Removes the frame from its parent and adds its desktopIcon to the parent. */
  95. public void iconifyFrame(JInternalFrame f) {
  96. JInternalFrame.JDesktopIcon desktopIcon;
  97. Container c;
  98. JDesktopPane d = f.getDesktopPane();
  99. boolean findNext = f.isSelected();
  100. desktopIcon = f.getDesktopIcon();
  101. if(!wasIcon(f)) {
  102. Rectangle r = getBoundsForIconOf(f);
  103. desktopIcon.setBounds(r.x, r.y, r.width, r.height);
  104. setWasIcon(f, Boolean.TRUE);
  105. }
  106. c = f.getParent();
  107. if (c == null) return;
  108. if (c instanceof JLayeredPane) {
  109. JLayeredPane lp = (JLayeredPane)c;
  110. int layer = lp.getLayer(f);
  111. lp.putLayer(desktopIcon, layer);
  112. }
  113. if (f.isMaximum()) {
  114. try { f.setMaximum(false); } catch (PropertyVetoException e2) { }
  115. }
  116. c.remove(f);
  117. c.add(desktopIcon);
  118. c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
  119. try { f.setSelected(false); } catch (PropertyVetoException e2) { }
  120. /* get topmost of the remaining frames */
  121. if (findNext) {
  122. activateNextFrame(c);
  123. }
  124. }
  125. void activateNextFrame(Container c) {
  126. int i;
  127. JInternalFrame nextFrame = null;
  128. if (c == null) return;
  129. for (i = 0; i < c.getComponentCount(); i++) {
  130. if (c.getComponent(i) instanceof JInternalFrame) {
  131. nextFrame = (JInternalFrame) c.getComponent(i);
  132. break;
  133. }
  134. }
  135. if (nextFrame != null) {
  136. try { nextFrame.setSelected(true); }
  137. catch (PropertyVetoException e2) { }
  138. nextFrame.moveToFront();
  139. }
  140. }
  141. /** Removes the desktopIcon from its parent and adds its frame to the parent. */
  142. public void deiconifyFrame(JInternalFrame f) {
  143. JInternalFrame.JDesktopIcon desktopIcon;
  144. desktopIcon = f.getDesktopIcon();
  145. if(desktopIcon.getParent() != null) {
  146. desktopIcon.getParent().add(f);
  147. removeIconFor(f);
  148. try { f.setSelected(true); } catch (PropertyVetoException e2) { }
  149. }
  150. }
  151. /** This will activate <b>f</b> moving it to the front. It will
  152. * set the current active frame (if any) IS_SELECTED_PROPERTY to false.
  153. * There can be only one active frame across all Layers.
  154. */
  155. public void activateFrame(JInternalFrame f) {
  156. Container p = f.getParent();
  157. Component[] c;
  158. JDesktopPane d = f.getDesktopPane();
  159. JInternalFrame currentlyActiveFrame =
  160. (d == null) ? null : d.getSelectedFrame();
  161. // fix for bug: 4162443
  162. if(p == null) {
  163. // If the frame is not in parent, its icon maybe, check it
  164. p = f.getDesktopIcon().getParent();
  165. if(p == null)
  166. return;
  167. }
  168. // we only need to keep track of the currentActive InternalFrame, if any
  169. if (currentlyActiveFrame == null){
  170. if (d != null) { d.setSelectedFrame(f);}
  171. } else if (currentlyActiveFrame != f) {
  172. // if not the same frame as the current active
  173. // we deactivate the current
  174. if (currentlyActiveFrame.isSelected()) {
  175. try {
  176. currentlyActiveFrame.setSelected(false);
  177. }
  178. catch(PropertyVetoException e2) {}
  179. }
  180. if (d != null) { d.setSelectedFrame(f);}
  181. }
  182. f.moveToFront();
  183. }
  184. // implements javax.swing.DesktopManager
  185. public void deactivateFrame(JInternalFrame f) {
  186. JDesktopPane d = f.getDesktopPane();
  187. JInternalFrame currentlyActiveFrame =
  188. (d == null) ? null : d.getSelectedFrame();
  189. if (currentlyActiveFrame == f)
  190. d.setSelectedFrame(null);
  191. }
  192. // implements javax.swing.DesktopManager
  193. public void beginDraggingFrame(JComponent f) {
  194. setupDragMode(f);
  195. if (dragMode == FASTER_DRAG_MODE) {
  196. floatingItems = findFloatingItems(f);
  197. currentBounds = f.getBounds();
  198. desktopBounds = f.getParent().getBounds();
  199. desktopBounds.x = 0;
  200. desktopBounds.y = 0;
  201. desktopGraphics = f.getParent().getGraphics();
  202. ((JInternalFrame)f).isDragging = true;
  203. }
  204. }
  205. private void setupDragMode(JComponent f) {
  206. JDesktopPane p = getDesktopPane(f);
  207. if (p != null){
  208. String mode = (String)p.getClientProperty("JDesktopPane.dragMode");
  209. if (mode != null && mode.equals("outline")) {
  210. dragMode = OUTLINE_DRAG_MODE;
  211. } else if (mode != null && mode.equals("faster") && f instanceof JInternalFrame) {
  212. dragMode = FASTER_DRAG_MODE;
  213. } else {
  214. if ( p.getDragMode() == JDesktopPane.OUTLINE_DRAG_MODE ) {
  215. dragMode = OUTLINE_DRAG_MODE;
  216. } else if ( p.getDragMode() == JDesktopPane.LIVE_DRAG_MODE && f instanceof JInternalFrame) {
  217. dragMode = FASTER_DRAG_MODE;
  218. } else {
  219. dragMode = DEFAULT_DRAG_MODE;
  220. }
  221. }
  222. }
  223. }
  224. private transient Point currentLoc = null;
  225. /**
  226. * Moves the visible location of the frame being dragged
  227. * to the location specified. The means by which this occurs can vary depending
  228. * on the dragging algorithm being used. The actual logical location of the frame
  229. * might not change until endDraggingFrame is called.
  230. */
  231. public void dragFrame(JComponent f, int newX, int newY) {
  232. if (dragMode == OUTLINE_DRAG_MODE) {
  233. JDesktopPane desktopPane = getDesktopPane(f);
  234. if (desktopPane != null){
  235. Graphics g = desktopPane.getGraphics();
  236. g.setXORMode(Color.white);
  237. if (currentLoc != null) {
  238. g.drawRect( currentLoc.x, currentLoc.y, f.getWidth()-1, f.getHeight()-1);
  239. }
  240. g.drawRect( newX, newY, f.getWidth()-1, f.getHeight()-1);
  241. currentLoc = new Point (newX, newY);
  242. g.dispose();
  243. }
  244. } else if (dragMode == FASTER_DRAG_MODE) {
  245. dragFrameFaster(f, newX, newY);
  246. } else {
  247. setBoundsForFrame(f, newX, newY, f.getWidth(), f.getHeight());
  248. }
  249. }
  250. // implements javax.swing.DesktopManager
  251. public void endDraggingFrame(JComponent f) {
  252. if ( dragMode == OUTLINE_DRAG_MODE && currentLoc != null) {
  253. setBoundsForFrame(f, currentLoc.x, currentLoc.y, f.getWidth(), f.getHeight() );
  254. currentLoc = null;
  255. } else if (dragMode == FASTER_DRAG_MODE) {
  256. currentBounds = null;
  257. if (desktopGraphics != null) {
  258. desktopGraphics.dispose();
  259. desktopGraphics = null;
  260. }
  261. desktopBounds = null;
  262. ((JInternalFrame)f).isDragging = false;
  263. }
  264. }
  265. // implements javax.swing.DesktopManager
  266. public void beginResizingFrame(JComponent f, int direction) {
  267. setupDragMode(f);
  268. }
  269. /** Calls setBoundsForFrame() with the new values. */
  270. public void resizeFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
  271. if ( dragMode == DEFAULT_DRAG_MODE || dragMode == FASTER_DRAG_MODE ) {
  272. setBoundsForFrame(f, newX, newY, newWidth, newHeight);
  273. } else {
  274. JDesktopPane desktopPane = getDesktopPane(f);
  275. if (desktopPane != null){
  276. Graphics g = desktopPane.getGraphics();
  277. g.setXORMode(Color.white);
  278. if (currentBounds != null) {
  279. g.drawRect( currentBounds.x, currentBounds.y, currentBounds.width-1, currentBounds.height-1);
  280. }
  281. g.drawRect( newX, newY, newWidth-1, newHeight-1);
  282. currentBounds = new Rectangle (newX, newY, newWidth, newHeight);
  283. g.setPaintMode();
  284. g.dispose();
  285. }
  286. }
  287. }
  288. // implements javax.swing.DesktopManager
  289. public void endResizingFrame(JComponent f) {
  290. if ( dragMode == OUTLINE_DRAG_MODE && currentBounds != null) {
  291. setBoundsForFrame(f, currentBounds.x, currentBounds.y, currentBounds.width, currentBounds.height );
  292. currentBounds = null;
  293. }
  294. }
  295. /** This moves the JComponent and repaints the damaged areas. */
  296. public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
  297. boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
  298. f.setBounds(newX, newY, newWidth, newHeight);
  299. if(didResize) {
  300. f.validate();
  301. }
  302. }
  303. /** Convience method to remove the desktopIcon of <b>f</b> is necessary. */
  304. protected void removeIconFor(JInternalFrame f) {
  305. JInternalFrame.JDesktopIcon di = f.getDesktopIcon();
  306. Container c = di.getParent();
  307. if(c != null) {
  308. c.remove(di);
  309. c.repaint(di.getX(), di.getY(), di.getWidth(), di.getHeight());
  310. }
  311. }
  312. /** The iconifyFrame() code calls this to determine the proper bounds
  313. * for the desktopIcon.
  314. */
  315. protected Rectangle getBoundsForIconOf(JInternalFrame f) {
  316. //
  317. // Get the icon for this internal frame and its preferred size
  318. //
  319. JInternalFrame.JDesktopIcon icon = f.getDesktopIcon();
  320. Dimension prefSize = icon.getPreferredSize();
  321. //
  322. // Get the parent bounds and child components.
  323. //
  324. Container c = f.getParent();
  325. if (c == null) {
  326. /* the frame has not yet been added to the parent; how about (0,0) ?*/
  327. return new Rectangle(0, 0, prefSize.width, prefSize.height);
  328. }
  329. Rectangle parentBounds = c.getBounds();
  330. Component [] components = c.getComponents();
  331. //
  332. // Iterate through valid default icon locations and return the
  333. // first one that does not intersect any other icons.
  334. //
  335. Rectangle availableRectangle = null;
  336. JInternalFrame.JDesktopIcon currentIcon = null;
  337. int x = 0;
  338. int y = parentBounds.height - prefSize.height;
  339. int w = prefSize.width;
  340. int h = prefSize.height;
  341. boolean found = false;
  342. while (!found) {
  343. availableRectangle = new Rectangle(x,y,w,h);
  344. found = true;
  345. for ( int i=0; i<components.length; i++ ) {
  346. //
  347. // Get the icon for this component
  348. //
  349. if ( components[i] instanceof JInternalFrame ) {
  350. currentIcon = ((JInternalFrame)components[i]).getDesktopIcon();
  351. }
  352. else if ( components[i] instanceof JInternalFrame.JDesktopIcon ){
  353. currentIcon = (JInternalFrame.JDesktopIcon)components[i];
  354. } else
  355. /* found a child that's neither an internal frame nor
  356. an icon. I don't believe this should happen, but at
  357. present it does and causes a null pointer exception.
  358. Even when that gets fixed, this code protects against
  359. the npe. hania */
  360. continue;
  361. //
  362. // If this icon intersects the current location, get next location.
  363. //
  364. if ( !currentIcon.equals(icon) ) {
  365. if ( availableRectangle.intersects(currentIcon.getBounds()) ) {
  366. found = false;
  367. break;
  368. }
  369. }
  370. }
  371. if (currentIcon == null)
  372. /* didn't find any useful children above. This probably shouldn't
  373. happen, but this check protects against an npe if it ever does
  374. (and it's happening now) */
  375. return availableRectangle;
  376. x += currentIcon.getBounds().width;
  377. if ( x + w > parentBounds.width ) {
  378. x = 0;
  379. y -= h;
  380. }
  381. }
  382. return(availableRectangle);
  383. }
  384. /** Stores the bounds of the component just before a maximize call. */
  385. protected void setPreviousBounds(JInternalFrame f, Rectangle r) {
  386. f.setNormalBounds(r);
  387. }
  388. protected Rectangle getPreviousBounds(JInternalFrame f) {
  389. return f.getNormalBounds();
  390. }
  391. /** Sets that the component has been iconized and the bounds of the
  392. * desktopIcon are valid.
  393. */
  394. protected void setWasIcon(JInternalFrame f, Boolean value) {
  395. if (value != null) {
  396. f.putClientProperty(HAS_BEEN_ICONIFIED_PROPERTY, value);
  397. }
  398. }
  399. protected boolean wasIcon(JInternalFrame f) {
  400. return (f.getClientProperty(HAS_BEEN_ICONIFIED_PROPERTY) == Boolean.TRUE);
  401. }
  402. JDesktopPane getDesktopPane( JComponent frame ) {
  403. JDesktopPane pane = null;
  404. Component c = frame.getParent();
  405. // Find the JDesktopPane
  406. while ( pane == null ) {
  407. if ( c instanceof JDesktopPane ) {
  408. pane = (JDesktopPane)c;
  409. }
  410. else if ( c == null ) {
  411. break;
  412. }
  413. else {
  414. c = c.getParent();
  415. }
  416. }
  417. return pane;
  418. }
  419. // =========== stuff for faster frame dragging ===================
  420. private void dragFrameFaster(JComponent f, int newX, int newY) {
  421. Rectangle previousBounds = new Rectangle(currentBounds.x,
  422. currentBounds.y,
  423. currentBounds.width,
  424. currentBounds.height);
  425. // move the frame
  426. currentBounds.x = newX;
  427. currentBounds.y = newY;
  428. emergencyCleanup(f);
  429. boolean floaterCollision = isFloaterCollision(previousBounds, currentBounds);
  430. // System.out.println(previousBounds);
  431. Rectangle visBounds = previousBounds.intersection(desktopBounds);
  432. // System.out.println(previousBounds);
  433. // System.out.println(visBounds);
  434. if(!floaterCollision) {
  435. // blit the frame to the new location
  436. // if we're under a floater we can't blit
  437. desktopGraphics.copyArea(visBounds.x,
  438. visBounds.y,
  439. visBounds.width,
  440. visBounds.height,
  441. newX - previousBounds.x,
  442. newY - previousBounds.y);
  443. }
  444. JComponent parent = (JComponent)f.getParent();
  445. f.setBounds(currentBounds);
  446. if(floaterCollision) {
  447. // since we couldn't blit we just redraw as fast as possible
  448. // the isDragging mucking is to avoid activating emergency cleanup
  449. ((JInternalFrame)f).isDragging = false;
  450. parent.paintImmediately(currentBounds);
  451. ((JInternalFrame)f).isDragging = true;
  452. }
  453. // fake out the repaint manager. We'll take care of everything
  454. RepaintManager currentManager = RepaintManager.currentManager(f);
  455. currentManager.markCompletelyClean(parent);
  456. currentManager.markCompletelyClean(f);
  457. // compute the minimal newly exposed area
  458. // if the rects intersect then we use computeDifference. Otherwise
  459. // we'll repaint the entire previous bounds
  460. Rectangle[] dirtyRects = null;
  461. if ( previousBounds.intersects(currentBounds) ) {
  462. dirtyRects = SwingUtilities.computeDifference(previousBounds, currentBounds);
  463. } else {
  464. dirtyRects = new Rectangle[1];
  465. dirtyRects[0] = previousBounds;
  466. // System.out.println("no intersection");
  467. };
  468. // Fix the damage
  469. for (int i = 0; i < dirtyRects.length; i++) {
  470. parent.paintImmediately(dirtyRects[i]);
  471. }
  472. // new areas of blit were exposed
  473. if ( !(visBounds.equals(previousBounds)) ) {
  474. dirtyRects = SwingUtilities.computeDifference(previousBounds, desktopBounds);
  475. for (int i = 0; i < dirtyRects.length; i++) {
  476. dirtyRects[i].x += newX - previousBounds.x;
  477. dirtyRects[i].y += newY - previousBounds.y;
  478. ((JInternalFrame)f).isDragging = false;
  479. parent.paintImmediately(dirtyRects[i]);
  480. ((JInternalFrame)f).isDragging = true;
  481. // System.out.println(dirtyRects[i]);
  482. }
  483. }
  484. }
  485. private boolean isFloaterCollision(Rectangle moveFrom, Rectangle moveTo) {
  486. if (floatingItems.length == 0) {
  487. // System.out.println("no floaters");
  488. return false;
  489. }
  490. for (int i = 0; i < floatingItems.length; i++) {
  491. boolean intersectsFrom = moveFrom.intersects(floatingItems[i]);
  492. if (intersectsFrom) {
  493. return true;
  494. }
  495. boolean intersectsTo = moveTo.intersects(floatingItems[i]);
  496. if (intersectsTo) {
  497. return true;
  498. }
  499. }
  500. return false;
  501. }
  502. private Rectangle[] findFloatingItems(JComponent f) {
  503. Container desktop = f.getParent();
  504. Component[] children = desktop.getComponents();
  505. int i = 0;
  506. for (i = 0; i < children.length; i++) {
  507. if (children[i] == f) {
  508. break;
  509. }
  510. }
  511. // System.out.println(i);
  512. Rectangle[] floaters = new Rectangle[i];
  513. for (i = 0; i < floaters.length; i++) {
  514. floaters[i] = children[i].getBounds();
  515. }
  516. return floaters;
  517. }
  518. /**
  519. * This method is here to clean up problems associated
  520. * with a race condition which can occur when the full contents
  521. * of a copyArea's source argument is not available onscreen.
  522. * This uses brute force to clean up in case of possible damage
  523. */
  524. private void emergencyCleanup(final JComponent f) {
  525. if ( ((JInternalFrame)f).danger ) {
  526. SwingUtilities.invokeLater( new Runnable(){
  527. public void run(){
  528. ((JInternalFrame)f).isDragging = false;
  529. f.paintImmediately(0,0,
  530. f.getWidth(),
  531. f.getHeight());
  532. //finalFrame.repaint();
  533. ((JInternalFrame)f).isDragging = true;
  534. // System.out.println("repair complete");
  535. }});
  536. ((JInternalFrame)f).danger = false;
  537. }
  538. }
  539. }