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