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