View Javadoc

1   /*
2    * WindowManager.java
3    *
4    * Copyright (c) 2004-2006 Gregory Kotsaftis
5    * gregkotsaftis@yahoo.com
6    * http://zeus-jscl.sourceforge.net/
7    *
8    * This library is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU Lesser General Public
10   * License as published by the Free Software Foundation; either
11   * version 2.1 of the License, or (at your option) any later version.
12   *
13   * This library is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   * Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public
19   * License along with this library; if not, write to the Free Software
20   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21   */
22  package ch.elca.el4j.services.gui.swing.mdi;
23  
24  import java.awt.Point;
25  import java.awt.event.ActionEvent;
26  import java.awt.event.ActionListener;
27  import java.awt.event.ContainerEvent;
28  import java.awt.event.ContainerListener;
29  import java.util.Arrays;
30  import java.util.Hashtable;
31  
32  import javax.swing.JDesktopPane;
33  import javax.swing.JInternalFrame;
34  import javax.swing.JMenu;
35  import javax.swing.JRadioButtonMenuItem;
36  import javax.swing.UIManager;
37  import javax.swing.WindowConstants;
38  import javax.swing.event.InternalFrameEvent;
39  import javax.swing.event.InternalFrameListener;
40  
41  import ch.elca.el4j.services.gui.swing.mdi.JInternalFrameComparator;
42  
43  /**
44   * A JDesktop window manager for MDI support.
45   * <p>
46   * Major functions implemented:
47   * <ul>
48   * <li>close()</li>
49   * <li>closeAll()</li>
50   * <li>minimize()</li>
51   * <li>minimizeAll()</li>
52   * <li>maximize()</li>
53   * <li>maximizeAll()</li>
54   * <li>restore()</li>
55   * <li>restoreAll()</li>
56   * <li>hide()</li>
57   * <li>hideAll()</li>
58   * <li>selectNext()</li>
59   * <li>selectPrevious()</li>
60   * <li>reset()</li>
61   * <li>resetAll()</li>
62   * <li>tileHorizontally()</li>
63   * <li>tileVertically()</li>
64   * <li>tile()</li>
65   * <li>cascade()</li>
66   * </ul>
67   * <p>
68   *
69   * Adapted from project http://zeus-jscl.sourceforge.net/
70   *
71   * @svnLink $Revision: 3884 $;$Date: 2009-08-04 15:48:31 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/swing/src/main/java/ch/elca/el4j/services/gui/swing/mdi/WindowManager.java $
72   *
73   * @author Gregory Kotsaftis
74   * @since 1.04
75   */
76  public final class WindowManager {
77  
78  	/**
79  	 * Number of menu items.
80  	 */
81  	private int m_lastMenuItemsCount = 0;
82  
83  	/**
84  	 * Stores 'radio button menu items' and 'internal frames' pairs.
85  	 */
86  	private final Hashtable<JRadioButtonMenuItem, JInternalFrame> m_radioMenuItemsAndFrames = new Hashtable<JRadioButtonMenuItem, JInternalFrame>();
87  
88  	/**
89  	 * Stores 'internal frames' and 'radio button menu items' pairs.
90  	 */
91  	private final Hashtable<JInternalFrame, JRadioButtonMenuItem> m_framesAndRadioMenuItems = new Hashtable<JInternalFrame, JRadioButtonMenuItem>();
92  
93  	/**
94  	 * Compares frames by title.
95  	 */
96  	private final JInternalFrameComparator m_frameComparator = new JInternalFrameComparator();
97  
98  	/**
99  	 * Container listener.
100 	 */
101 	private final FrameListener m_frameListener = new FrameListener();
102 
103 	/**
104 	 * Internal frame listener.
105 	 */
106 	private final SelectFrameListener m_selectFrameListener = new SelectFrameListener();
107 
108 	/**
109 	 * Radio button menu item listener.
110 	 */
111 	private final MenuItemActionListener m_radioMenuItemListener = new MenuItemActionListener();
112 
113 	/**
114 	 * JDesktopPane instance.
115 	 */
116 	private JDesktopPane m_desktop = null;
117 
118 	/**
119 	 * JMenu instance.
120 	 */
121 	private JMenu m_windowsMenu = null;
122 
123 	/**
124 	 * Check to see which drag mode should be used.
125 	 */
126 	private boolean m_outlineDragMode = false;
127 
128 	/**
129 	 * Used to set the de-iconifiable policy.
130 	 */
131 	private boolean m_deiconifiablePolicy = false;
132 
133 	/**
134 	 * Used to set the close policy.
135 	 */
136 	private boolean m_closePolicy = false;
137 
138 	/**
139 	 * Used to set the auto-position policy.
140 	 */
141 	private boolean m_autoPositionPolicy = true;
142 
143 	/**
144 	 * Position for next placed JInternalFrame within the desktop.
145 	 * Used in conjuction with <code>m_autoPositionPolicy</code>.
146 	 */
147 	private Point m_nextFramePos = new Point(0, 0);
148 
149 	/**
150 	 * Closes an internal frame if it is not already closed and is closable.
151 	 * <p>
152 	 * @param f     The <code>JInternalFrame</code> to close.
153 	 */
154 	private void close_frame(JInternalFrame f) {
155 		if (f != null && !f.isClosed() && f.isClosable()) {
156 			try {
157 				if (m_closePolicy) {
158 					f.setClosed(true);
159 				} else {
160 					f.doDefaultCloseAction();
161 				}
162 			} catch (Exception e) {
163 				e.printStackTrace();
164 			}
165 		}
166 	}
167 
168 	/**
169 	 * Iconifies a frame if it is iconifiable but not yet iconified.
170 	 * <p>
171 	 * @param f     The <code>JInternalFrame</code> to iconify.
172 	 */
173 	private void iconify_frame(JInternalFrame f) {
174 		if (f != null && !f.isIcon() && f.isIconifiable()) {
175 			try {
176 				f.setIcon(true);
177 			} catch (Exception e) {
178 				e.printStackTrace();
179 			}
180 		}
181 	}
182 
183 	/**
184 	 * De-iconifies a frame if it is iconified.
185 	 * <p>
186 	 * @param f     The <code>JInternalFrame</code> to de-iconify.
187 	 */
188 	private void deiconify_frame(JInternalFrame f) {
189 		if (f != null && f.isIcon() && f.isIconifiable()) {
190 			try {
191 				f.setIcon(false);
192 			} catch (Exception e) {
193 				e.printStackTrace();
194 			}
195 		}
196 	}
197 
198 	/**
199 	 * Restores a frame.
200 	 * <p>
201 	 * @param f     The <code>JInternalFrame</code> to restore.
202 	 */
203 	private void restore_frame(JInternalFrame f) {
204 		if (f != null) {
205 			try {
206 				f.setMaximum(false);
207 			} catch (Exception e) {
208 				e.printStackTrace();
209 			}
210 		}
211 	}
212 
213 	/**
214 	 * Maximizes a frame if it is can be maximized and is not already maximized.
215 	 * <p>
216 	 * @param f     The <code>JInternalFrame</code> to maximize.
217 	 */
218 	private void maximize_frame(JInternalFrame f) {
219 		if (f != null && !f.isMaximum() && f.isMaximizable()) {
220 			try {
221 				f.setMaximum(true);
222 			} catch (Exception e) {
223 				e.printStackTrace();
224 			}
225 		}
226 	}
227 
228 	/**
229 	 * Resets a frame to it's original size.
230 	 * <p>
231 	 * @param f     The <code>JInternalFrame</code> to reset to original size.
232 	 */
233 	private void reset_frame(JInternalFrame f) {
234 		if (f != null) {
235 			f.pack();
236 		}
237 	}
238 
239 	/**
240 	 * Selects a frame and brings it to front of it's layer.
241 	 * <p>
242 	 * @param f     The selected <code>JInternalFrame</code>.
243 	 */
244 	private void select_frame(JInternalFrame f) {
245 		if (f != null && !f.isSelected()) {
246 			try {
247 				f.setSelected(true);
248 				f.toFront();
249 			} catch (Exception e) {
250 				e.printStackTrace();
251 			}
252 		}
253 	}
254 
255 	/**
256 	 * Hides a frame if it is visible. Also rebuilds the windows menu.
257 	 * <p>
258 	 * @param f     The <code>JInternalFrame</code> to hide.
259 	 */
260 	private void hide_frame(JInternalFrame f) {
261 		if (f != null && f.isVisible()) {
262 			f.setVisible(false);
263 
264 			if (f.isSelected()) {
265 				try {
266 					f.setSelected(false);
267 				} catch (Exception e) {
268 					e.printStackTrace();
269 				}
270 			}
271 
272 			// this event will not be fired otherwise!
273 			m_frameListener.manualFireEvent();
274 		}
275 	}
276 
277 	/**
278 	 * Finds if an internal frame has our frame listener attached. In this case
279 	 * i should skip attaching it again. This method is needed because
280 	 * when i debuged the code around <code>addInternalFrameListener()</code>
281 	 * i saw, to my surprise, that the <b>SAME</b> listener was added in the
282 	 * array with the internal frame listeners over and over again! Perhaps
283 	 * this is a bug of swings, perhaps not. Until this issue is solved i use
284 	 * this approach.
285 	 * <p>
286 	 * @todo Check java forums about this!
287 	 */
288 	private boolean is_internal_frame_listener_attached(JInternalFrame jif) {
289 		boolean found = false;
290 
291 		InternalFrameListener[] all = jif.getInternalFrameListeners();
292 		for (int i = 0; i < all.length; i++) {
293 			if (all[i] == m_selectFrameListener) {
294 				found = true;
295 				break;
296 			}
297 		}
298 
299 		return (found);
300 	}
301 
302 	/**
303 	 * Constructor. Creates a new <code>WindowManager</code> and
304 	 * attaches it to a desktop pane.
305 	 * <p>
306 	 * @param d             The <code>JDesktopPane</code> instance.
307 	 * @param windowsMenu   The <code>JMenu</code> instance.
308 	 */
309 	public WindowManager(JDesktopPane d, JMenu windowsMenu) {
310 		m_desktop = d;
311 		m_windowsMenu = windowsMenu;
312 
313 		if (m_desktop == null) {
314 			throw new NullPointerException(
315 					"JDesktopPane instance provided to WindowManager is NULL!");
316 		}
317 
318 		if (m_windowsMenu == null) {
319 			throw new NullPointerException(
320 					"JMenu instance provided to WindowManager is NULL!");
321 		}
322 
323 		// prepare the windows menu to add our radio buttons
324 		
325 		m_lastMenuItemsCount = m_windowsMenu.getMenuComponentCount();
326 		
327 		// listener for new frames that will be added in the desktop
328 		m_desktop.addContainerListener(m_frameListener);
329 
330 		// find if there are any frames in the desktop and start monitoring them
331 		JInternalFrame[] frames = m_desktop.getAllFrames();
332 		for (int i = 0; frames != null && i < frames.length; i++) {
333 			frames[i].addInternalFrameListener(m_selectFrameListener);
334 		}
335 
336 		// rebuild the windows menu if there were any frames opened before we
337 		// attached our ContainerListener.
338 		m_frameListener.manualFireEvent();
339 	}
340 
341 	/**
342 	 * Selects frames' drawing strategy.
343 	 * <p>
344 	 * @param outline   <code>true</code> to enable
345 	 *                  <code>JDesktopPane.OUTLINE_DRAG_MODE</code>, or
346 	 *                  <code>false</code> to enable
347 	 *                  <code>JDesktopPane.LIVE_DRAG_MODE</code>.
348 	 */
349 	public void setOutlineDragMode(boolean outline) {
350 		m_outlineDragMode = outline;
351 
352 		if (m_desktop != null) {
353 			if (m_outlineDragMode) {
354 				m_desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
355 			} else {
356 				m_desktop.setDragMode(JDesktopPane.LIVE_DRAG_MODE);
357 			}
358 		}
359 	}
360 
361 	/**
362 	 * Gets frames' drawing strategy.
363 	 * <p>
364 	 * @return  <code>true</code> if desktop's <code>DragMode</code> is
365 	 *          <code>JDesktopPane.OUTLINE_DRAG_MODE</code> or
366 	 *          <code>false</code> if desktop's <code>DragMode</code> is
367 	 *          <code>JDesktopPane.LIVE_DRAG_MODE</code>.
368 	 */
369 	public boolean getOutlineDragMode() {
370 		return (m_outlineDragMode);
371 	}
372 
373 	/**
374 	 * Sets de-iconifiable policy. Should we force a frame to de-iconify
375 	 * if it is iconified, during cascade operations?
376 	 * <p>
377 	 * @param p     <code>true</code> if force, <code>false</code> otherwise.
378 	 */
379 	public void setDeiconifiablePolicy(boolean p) {
380 		m_deiconifiablePolicy = p;
381 	}
382 
383 	/**
384 	 * Gets the de-iconifiable policy.
385 	 * <p>
386 	 * @return  The de-iconifiable policy.
387 	 */
388 	public boolean getDeiconifiablePolicy() {
389 		return (m_deiconifiablePolicy);
390 	}
391 
392 	/**
393 	 * Sets the close policy. Should we do default close operation per frame
394 	 * or force close it?
395 	 * <p>
396 	 * @param p     <code>true</code> to force close,
397 	 *              <code>false</code> do default close operation.
398 	 */
399 	public void setClosePolicy(boolean p) {
400 		m_closePolicy = p;
401 	}
402 
403 	/**
404 	 * Gets the close policy.
405 	 * <p>
406 	 * @return  <code>true</code> for force close,
407 	 *          <code>false</code> for default close operation.
408 	 */
409 	public boolean getClosePolicy() {
410 		return (m_closePolicy);
411 	}
412 
413 	/**
414 	 * Sets the auto position frames policy. Should we auto position the
415 	 * new frames in the desktop or not?
416 	 * <p>
417 	 * @param p     <code>true</code> for auto-position,
418 	 *              <code>false</code> for none.
419 	 */
420 	public void setAutoPositionPolicy(boolean p) {
421 		m_autoPositionPolicy = p;
422 	}
423 
424 	/**
425 	 * Gets the auto position frames policy.
426 	 * <p>
427 	 * @return  The auto-position policy.
428 	 */
429 	public boolean getAutoPositionPolicy() {
430 		return (m_autoPositionPolicy);
431 	}
432 
433 	/**
434 	 * Counts all frames, even those that are closed with
435 	 * <code>DefaultCloseOperation = HIDE_ON_CLOSE</code>
436 	 * <p>
437 	 * <b>NOTE</b>Use this method in order to understand how many
438 	 * "ghost" frames remain within the desktop.
439 	 * <p>
440 	 * @return  The number of frames.
441 	 */
442 	public int countFrames() {
443 		if (m_desktop == null || m_desktop.getAllFrames() == null
444 				|| m_desktop.getAllFrames().length < 1) {
445 			return (0);
446 		}
447 
448 		JInternalFrame[] frames = m_desktop.getAllFrames();
449 
450 		return (frames.length );
451 	}
452 
453 	/**
454 	 * Counts only visible frames.
455 	 * <p>
456 	 * @return  The number of visible frames.
457 	 */
458 	public int countVisibleFrames() {
459 		if (m_desktop == null || m_desktop.getAllFrames() == null
460 				|| m_desktop.getAllFrames().length < 1) {
461 			return (0);
462 		}
463 
464 		JInternalFrame[] frames = m_desktop.getAllFrames();
465 
466 		int count = 0;
467 		for (int i = 0; i < frames.length; i++) {
468 			if (frames[i].isVisible()) {
469 				count++;
470 			}
471 		}
472 
473 		return (count);
474 	}
475 
476 	/**
477 	 * Closes the selected frame if it is closable.
478 	 */
479 	public void close() {
480 		if (m_desktop == null || m_desktop.getAllFrames() == null
481 				|| m_desktop.getAllFrames().length < 1) {
482 			return;
483 		}
484 
485 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
486 
487 		close_frame(activeframe);
488 	}
489 
490 	/**
491 	 * Closes all frames that are closable.
492 	 */
493 	public void closeAll() {
494 		if (m_desktop == null || m_desktop.getAllFrames() == null
495 				|| m_desktop.getAllFrames().length < 1) {
496 			return;
497 		}
498 
499 		JInternalFrame[] frames = m_desktop.getAllFrames();
500 
501 		for (int i = 0; i < frames.length; i++) {
502 			close_frame(frames[i]);
503 		}
504 	}
505 
506 	/**
507 	 * Minimizes the selected frame if it is iconifiable.
508 	 */
509 	public void minimize() {
510 		if (m_desktop == null || m_desktop.getAllFrames() == null
511 				|| m_desktop.getAllFrames().length < 1) {
512 			return;
513 		}
514 
515 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
516 
517 		iconify_frame(activeframe);
518 	}
519 
520 	/**
521 	 * Minimizes all frames that are iconifiable.
522 	 */
523 	public void minimizeAll() {
524 		if (m_desktop == null || m_desktop.getAllFrames() == null
525 				|| m_desktop.getAllFrames().length < 1) {
526 			return;
527 		}
528 
529 		JInternalFrame[] frames = m_desktop.getAllFrames();
530 
531 		for (int i = 0; i < frames.length; i++) {
532 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
533 			// this frame is invisible and so we should not minimize it!
534 			if (!frames[i].isVisible())
535 				continue;
536 
537 			iconify_frame(frames[i]);
538 		}
539 	}
540 
541 	/**
542 	 * Restores the selected frame from it's maximized state.
543 	 */
544 	public void restore() {
545 		if (m_desktop == null || m_desktop.getAllFrames() == null
546 				|| m_desktop.getAllFrames().length < 1) {
547 			return;
548 		}
549 
550 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
551 
552 		if (activeframe != null) {
553 			if (activeframe.isMaximum() || activeframe.isIcon()) {
554 				// if it is minimized, it must be deiconified
555 				deiconify_frame(activeframe);
556 
557 				restore_frame(activeframe);
558 			}
559 		}
560 	}
561 
562 	/**
563 	 * Restores all frames from their maximized state.
564 	 */
565 	public void restoreAll() {
566 		if (m_desktop == null || m_desktop.getAllFrames() == null
567 				|| m_desktop.getAllFrames().length < 1) {
568 			return;
569 		}
570 
571 		JInternalFrame[] frames = m_desktop.getAllFrames();
572 
573 		for (int i = 0; i < frames.length; i++) {
574 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
575 			// this frame is invisible and so we should not restore it!
576 			if (!frames[i].isVisible())
577 				continue;
578 
579 			if (frames[i].isMaximum() || frames[i].isIcon()) {
580 				// if it is minimized, it must be deiconified
581 				deiconify_frame(frames[i]);
582 
583 				restore_frame(frames[i]);
584 			}
585 		}
586 	}
587 
588 	/**
589 	 * Maximizes the selected frame if it is not already maximized.
590 	 */
591 	public void maximize() {
592 		if (m_desktop == null || m_desktop.getAllFrames() == null
593 				|| m_desktop.getAllFrames().length < 1) {
594 			return;
595 		}
596 
597 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
598 
599 		maximize_frame(activeframe);
600 	}
601 
602 	/**
603 	 * Maximizes all frames that are not already maximized.
604 	 */
605 	public void maximizeAll() {
606 		if (m_desktop == null || m_desktop.getAllFrames() == null
607 				|| m_desktop.getAllFrames().length < 1) {
608 			return;
609 		}
610 
611 		JInternalFrame[] frames = m_desktop.getAllFrames();
612 
613 		for (int i = 0; i < frames.length; i++) {
614 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
615 			// this frame is invisible and so we should not maximize it!
616 			if (!frames[i].isVisible())
617 				continue;
618 
619 			maximize_frame(frames[i]);
620 		}
621 	}
622 
623 	/**
624 	 * Resets the frame to it's original preferred size of its components.
625 	 */
626 	public void reset() {
627 		if (m_desktop == null || m_desktop.getAllFrames() == null
628 				|| m_desktop.getAllFrames().length < 1) {
629 			return;
630 		}
631 
632 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
633 
634 		reset_frame(activeframe);
635 	}
636 
637 	/**
638 	 * Resets all frames to their original preferred size of their components.
639 	 */
640 	public void resetAll() {
641 		if (m_desktop == null || m_desktop.getAllFrames() == null
642 				|| m_desktop.getAllFrames().length < 1) {
643 			return;
644 		}
645 
646 		JInternalFrame[] frames = m_desktop.getAllFrames();
647 
648 		for (int i = 0; i < frames.length; i++) {
649 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
650 			// this frame is invisible and so we should not pack() it!
651 			if (!frames[i].isVisible())
652 				continue;
653 
654 			reset_frame(frames[i]);
655 		}
656 	}
657 
658 	/**
659 	 * Hides a frame. The hidden frame is added to the menu with
660 	 * disabled-like color.
661 	 */
662 	public void hide() {
663 		if (m_desktop == null || m_desktop.getAllFrames() == null
664 				|| m_desktop.getAllFrames().length < 1) {
665 			return;
666 		}
667 
668 		if (m_windowsMenu == null) {
669 			return;
670 		}
671 
672 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
673 
674 		hide_frame(activeframe);
675 	}
676 
677 	/**
678 	 * Hides all frames. The hidden frames are added to the menu with
679 	 * disabled-like color.
680 	 */
681 	public void hideAll() {
682 		if (m_desktop == null || m_desktop.getAllFrames() == null
683 				|| m_desktop.getAllFrames().length < 1) {
684 			return;
685 		}
686 
687 		if (m_windowsMenu == null) {
688 			return;
689 		}
690 
691 		JInternalFrame[] frames = m_desktop.getAllFrames();
692 
693 		for (int i = 0; i < frames.length; i++) {
694 			// this frame is already hidden!
695 			if (!frames[i].isVisible())
696 				continue;
697 
698 			hide_frame(frames[i]);
699 		}
700 	}
701 
702 	/**
703 	 * Selects the next internal frame.
704 	 */
705 	public void selectNext() {
706 		if (m_desktop == null || m_desktop.getAllFrames() == null
707 				|| m_desktop.getAllFrames().length < 1) {
708 			return;
709 		}
710 
711 		JInternalFrame[] frames = m_desktop.getAllFrames();
712 
713 		// we need at least 2 frames to perform next window!
714 		if (frames.length < 2) {
715 			return;
716 		}
717 
718 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
719 
720 		if (activeframe == null) {
721 			return;
722 		}
723 
724 		// NOTE: IT SEEMS IF WE DO NOT SORT THE FRAMES WE CANNOT SELECT
725 		//       THE NEXT FRAME CORRECTLY!!!
726 		// sort frames by title
727 		Arrays.sort(frames, m_frameComparator);
728 
729 		int next_frame = -1;
730 		for (int i = 0; i < frames.length; i++) {
731 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
732 			// this frame is invisible and so we should not select it!
733 			if (!frames[i].isVisible())
734 				continue;
735 
736 			// find index of selected frame
737 			if (frames[i] != activeframe)
738 				continue;
739 
740 			// check all remaining frames ahead, if they can be selected
741 			for (int j = i + 1; j < frames.length; j++) {
742 				if (!frames[j].isIcon()) {
743 					next_frame = j;
744 					break;
745 				}
746 			}
747 
748 			// if no frame found, check from the start
749 			if (next_frame == -1) {
750 				for (int k = 0; k < i; k++) {
751 					if (!frames[k].isIcon()) {
752 						next_frame = k;
753 						break;
754 					}
755 				}
756 			}
757 
758 			if (next_frame != -1)
759 				break;
760 		}
761 
762 		if (next_frame != -1) {
763 			select_frame(frames[next_frame]);
764 		}
765 	}
766 
767 	/**
768 	 * Selects the previous internal frame.
769 	 */
770 	public void selectPrevious() {
771 		if (m_desktop == null || m_desktop.getAllFrames() == null
772 				|| m_desktop.getAllFrames().length < 1) {
773 			return;
774 		}
775 
776 		JInternalFrame[] frames = m_desktop.getAllFrames();
777 
778 		// we need at least 2 frames to perform previous window!
779 		if (frames.length < 2) {
780 			return;
781 		}
782 
783 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
784 
785 		if (activeframe == null) {
786 			return;
787 		}
788 
789 		// NOTE: IT SEEMS IF WE DO NOT SORT THE FRAMES WE CANNOT SELECT
790 		//       THE PREVIOUS FRAME CORRECTLY!!!
791 		// sort frames by title
792 		Arrays.sort(frames, m_frameComparator);
793 
794 		int previous_frame = -1;
795 		for (int i = 0; i < frames.length; i++) {
796 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
797 			// this frame is invisible and so we should not select it!
798 			if (!frames[i].isVisible())
799 				continue;
800 
801 			// find index of selected frame
802 			if (frames[i] != activeframe)
803 				continue;
804 
805 			// check all previous frames, if they can be selected
806 			for (int j = i - 1; j >= 0; j--) {
807 				if (!frames[j].isIcon()) {
808 					previous_frame = j;
809 					break;
810 				}
811 			}
812 
813 			// if no frame found, check from the end
814 			if (previous_frame == -1) {
815 				for (int k = frames.length - 1; k > i; k--) {
816 					if (!frames[k].isIcon()) {
817 						previous_frame = k;
818 						break;
819 					}
820 				}
821 			}
822 
823 			if (previous_frame != -1)
824 				break;
825 		}
826 
827 		if (previous_frame != -1) {
828 			select_frame(frames[previous_frame]);
829 		}
830 	}
831 
832 	/**
833 	 * Cascades all frames. If De-iconifiablePolicy is false, minimized frames
834 	 * stay minimized.During cascade, it also sorts frames based on their title.
835 	 */
836 	public void cascade() {
837 		if (m_desktop == null || m_desktop.getAllFrames() == null
838 				|| m_desktop.getAllFrames().length < 1) {
839 			return;
840 		}
841 
842 		JInternalFrame activeframe = m_desktop.getSelectedFrame();
843 		JInternalFrame[] frames = m_desktop.getAllFrames();
844 
845 		// sort frames by title
846 		Arrays.sort(frames, m_frameComparator);
847 
848 		int x = 0;
849 		int y = 0;
850 		int width = m_desktop.getWidth() / 2;
851 		int height = m_desktop.getHeight() / 2;
852 
853 		for (int i = 0; i < frames.length; i++) {
854 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
855 			// this frame is invisible and so we should not cascade it!
856 			if (!frames[i].isVisible())
857 				continue;
858 
859 			// should we cascade also the iconified frames?
860 			if (m_deiconifiablePolicy == false && frames[i].isIcon())
861 				continue;
862 
863 			deiconify_frame(frames[i]);
864 
865 			frames[i].reshape(x, y, width, height);
866 
867 			frames[i].moveToFront();
868 
869 			if (frames[i] != activeframe) {
870 				int next_pos = frames[i].getHeight()
871 						- frames[i].getContentPane().getHeight();
872 
873 				frames[i].setLocation(x, y);
874 				x += next_pos;
875 				y += next_pos;
876 			}
877 
878 			// wrap around at the desktop edge
879 			if ((x + width) > m_desktop.getWidth()) {
880 				x = 0;
881 			}
882 			if ((y + height) > m_desktop.getHeight()) {
883 				y = 0;
884 			}
885 		}
886 
887 		// last frame will be the selected frame, if any.
888 		if (activeframe != null) {
889 			activeframe.moveToFront();
890 			activeframe.setLocation(x, y);
891 		}
892 	}
893 
894 	/**
895 	 * Tiles all windows vertically.
896 	 */
897 	public void tileVertically() {
898 		if (m_desktop == null || m_desktop.getAllFrames() == null
899 				|| m_desktop.getAllFrames().length < 1) {
900 			return;
901 		}
902 
903 		JInternalFrame[] frames = m_desktop.getAllFrames();
904 
905 		int visibleFrames = 0;
906 		for (int i = 0; i < frames.length; i++) {
907 			if (!frames[i].isVisible())
908 				continue;
909 
910 			visibleFrames++;
911 		}
912 
913 		if (visibleFrames == 0)
914 			return;
915 
916 		// sort frames by title
917 		Arrays.sort(frames, m_frameComparator);
918 
919 		int width = m_desktop.getWidth() / visibleFrames;
920 		int height = m_desktop.getHeight();
921 		int x = 0;
922 
923 		for (int i = 0; i < frames.length; i++) {
924 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
925 			// this frame is invisible and so we should not tile it!
926 			if (!frames[i].isVisible())
927 				continue;
928 
929 			try {
930 				frames[i].setMaximum(false);
931 				frames[i].setIcon(false);
932 				frames[i].moveToFront();
933 			} catch (Exception ex) {
934 				ex.printStackTrace();
935 			}
936 
937 			frames[i].reshape(x, 0, width, height);
938 
939 			x += width;
940 		}
941 	}
942 
943 	/**
944 	 * Tiles all windows horizontally.
945 	 */
946 	public void tileHorizontally() {
947 		if (m_desktop == null || m_desktop.getAllFrames() == null
948 				|| m_desktop.getAllFrames().length < 1) {
949 			return;
950 		}
951 
952 		JInternalFrame[] frames = m_desktop.getAllFrames();
953 
954 		int visibleFrames = 0;
955 		for (int i = 0; i < frames.length; i++) {
956 			if (!frames[i].isVisible())
957 				continue;
958 
959 			visibleFrames++;
960 		}
961 
962 		if (visibleFrames == 0)
963 			return;
964 
965 		// sort frames by title
966 		Arrays.sort(frames, m_frameComparator);
967 
968 		int width = m_desktop.getWidth();
969 		int height = m_desktop.getHeight() / visibleFrames;
970 		int y = 0;
971 
972 		for (int i = 0; i < frames.length; i++) {
973 			// if DefaultCloseOperation for a JInternalFrame is HIDE_ON_CLOSE
974 			// this frame is invisible and so we should not tile it!
975 			if (!frames[i].isVisible())
976 				continue;
977 
978 			try {
979 				frames[i].setMaximum(false);
980 				frames[i].setIcon(false);
981 				frames[i].moveToFront();
982 			} catch (Exception ex) {
983 				ex.printStackTrace();
984 			}
985 
986 			frames[i].reshape(0, y, width, height);
987 
988 			y += height;
989 		}
990 	}
991 
992 	/**
993 	 * Tiles all windows equally.
994 	 */
995 	public void tile() {
996 		if (m_desktop == null || m_desktop.getAllFrames() == null
997 				|| m_desktop.getAllFrames().length < 1) {
998 			return;
999 		}
1000 
1001 		JInternalFrame[] frames = m_desktop.getAllFrames();
1002 
1003 		int visibleFrames = 0;
1004 		for (int i = 0; i < frames.length; i++) {
1005 			if (!frames[i].isVisible())
1006 				continue;
1007 
1008 			visibleFrames++;
1009 		}
1010 
1011 		if (visibleFrames == 0)
1012 			return;
1013 
1014 		// sort frames by title
1015 		Arrays.sort(frames, m_frameComparator);
1016 
1017 		// create a matrix
1018 		int sqrt = (int) Math.sqrt(frames.length);
1019 		int numRows = sqrt;
1020 		int numCols = sqrt;
1021 
1022 		// because of possible precision loss, fix the matrix size
1023 		if (numRows * numCols < frames.length) {
1024 			numCols++;
1025 			if (numRows * numCols < frames.length) {
1026 				numRows++;
1027 			}
1028 		}
1029 
1030 		int width = m_desktop.getWidth() / numCols;
1031 		int height = m_desktop.getHeight() / numRows;
1032 		int x = 0;
1033 		int y = 0;
1034 
1035 		for (int i = 0; i < numRows; i++) {
1036 			for (int j = 0; j < numCols; j++) {
1037 				int index = (i * numCols) + j;
1038 
1039 				if (index >= frames.length)
1040 					break;
1041 
1042 				// if DefaultCloseOperation for a JInternalFrame is
1043 				// HIDE_ON_CLOSE then this frame is invisible and so
1044 				// we should not tile it!
1045 				if (!frames[index].isVisible())
1046 					continue;
1047 
1048 				try {
1049 					frames[index].setMaximum(false);
1050 					frames[index].setIcon(false);
1051 					frames[index].moveToFront();
1052 				} catch (Exception ex) {
1053 					ex.printStackTrace();
1054 				}
1055 
1056 				frames[index].reshape(x, y, width, height);
1057 
1058 				x += width;
1059 			}
1060 
1061 			y += height;
1062 			x = 0;
1063 		}
1064 	}
1065 
1066 	/**
1067 	 * A ContainerListener for <code>JDesktopPane</code> whenever a frame is
1068 	 * added/removed from the desktop, the windows menu is being rebuilt to
1069 	 * add/remove the new frame as a radio button. Also has a
1070 	 * <code>manualFireEvent()</code> method to manually rebuild the menu.
1071 	 */
1072 	private final class FrameListener implements ContainerListener {
1073 
1074 		/**
1075 		 * Rebuilds the menu.
1076 		 */
1077 		private void rebuild_menu() {
1078 			if (m_desktop != null && m_windowsMenu != null) {
1079 				// remove old items
1080 				while (m_windowsMenu.getMenuComponentCount() > m_lastMenuItemsCount) {
1081 					m_windowsMenu
1082 							.remove(m_windowsMenu.getMenuComponentCount() - 1);
1083 				}
1084 
1085 				m_radioMenuItemsAndFrames.clear();
1086 				m_framesAndRadioMenuItems.clear();
1087 
1088 				// create a radio button for each frame in desktop
1089 				JInternalFrame[] frames = m_desktop.getAllFrames();
1090 
1091 				// sort frames by title
1092 				Arrays.sort(frames, m_frameComparator);
1093 				
1094 				// added by SWI
1095 				if (frames.length > 0) {
1096 					m_windowsMenu.addSeparator();
1097 				}
1098 
1099 				for (int i = 0; i < frames.length; i++) {
1100 					JRadioButtonMenuItem item = new JRadioButtonMenuItem(""
1101 							+ (i + 1) + ": " + frames[i].getTitle(), frames[i]
1102 							.isSelected());
1103 					
1104 					// added by SWI
1105 					item.setMnemonic(Integer.toString(i + 1).toCharArray()[0]);
1106 
1107 					// change color for hidden frames
1108 					if (!frames[i].isVisible()) {
1109 						item
1110 								.setForeground(UIManager
1111 										.getColor("RadioButtonMenuItem.disabledForeground"));
1112 					}
1113 					/*else
1114 					{
1115 						item.setForeground( UIManager.getColor(
1116 							"RadioButtonMenuItem.foreground") );
1117 					}*/
1118 
1119 					item.addActionListener(m_radioMenuItemListener);
1120 					m_windowsMenu.add(item);
1121 
1122 					m_radioMenuItemsAndFrames.put(item, frames[i]);
1123 					m_framesAndRadioMenuItems.put(frames[i], item);
1124 				}
1125 			}
1126 		}
1127 
1128 		/**
1129 		 * Manually rebuilds the menu.
1130 		 */
1131 		public void manualFireEvent() {
1132 			rebuild_menu();
1133 		}
1134 
1135 		/**
1136 		 * Component added <code>Container</code> event.
1137 		 * <p>
1138 		 * @param ce    The <code>ContainerEvent</code>.
1139 		 */
1140 		public void componentAdded(ContainerEvent ce) {
1141 			// auto position new frames?
1142 			if (m_desktop != null && m_autoPositionPolicy == true
1143 					&& ce.getChild() != null
1144 					&& ce.getChild() instanceof JInternalFrame) {
1145 				JInternalFrame jif = (JInternalFrame) ce.getChild();
1146 				int w = jif.getWidth();
1147 				int h = jif.getHeight();
1148 
1149 				//---
1150 				// fix suggested by Kostas Filippaios
1151 				// if this is the first JInternalFrame added,
1152 				// reset the position to 0,0
1153 				if (countFrames() == 1) {
1154 					m_nextFramePos.setLocation(0, 0);
1155 				}
1156 				//---
1157 
1158 				jif.setLocation(m_nextFramePos);
1159 
1160 				int next_pos = h - jif.getContentPane().getHeight();
1161 				m_nextFramePos.x += next_pos;
1162 				m_nextFramePos.y += next_pos;
1163 
1164 				// wrap around at the desktop edge
1165 				if ((m_nextFramePos.x + w) > m_desktop.getWidth()) {
1166 					m_nextFramePos.x = 0;
1167 				}
1168 				if ((m_nextFramePos.y + h) > m_desktop.getHeight()) {
1169 					m_nextFramePos.y = 0;
1170 				}
1171 			}
1172 
1173 			// attach listener to new frame
1174 			if (m_desktop != null) {
1175 				JInternalFrame[] frames = m_desktop.getAllFrames();
1176 				for (int i = 0; frames != null && i < frames.length; i++) {
1177 					// skip if frame already attached!
1178 					if (is_internal_frame_listener_attached(frames[i]))
1179 						continue;
1180 
1181 					frames[i].addInternalFrameListener(m_selectFrameListener);
1182 				}
1183 			}
1184 
1185 			rebuild_menu();
1186 		}
1187 
1188 		/**
1189 		 * Component removed <code>Container</code> event.
1190 		 * <p>
1191 		 * @param ce    The <code>ContainerEvent</code>.
1192 		 */
1193 		public void componentRemoved(ContainerEvent ce) {
1194 			rebuild_menu();
1195 		}
1196 
1197 	}
1198 
1199 	/**
1200 	 * Whenever a radio button is selected, the related frame
1201 	 * will be made visible and selected too.
1202 	 */
1203 	private final class MenuItemActionListener implements ActionListener {
1204 
1205 		/**
1206 		 * Selects the related frame and makes it visible.
1207 		 * <p>
1208 		 * @param ae    The <code>ActionEvent</code>.
1209 		 */
1210 		public void actionPerformed(ActionEvent ae) {
1211 			Object o = ae.getSource();
1212 
1213 			if (o instanceof JRadioButtonMenuItem) {
1214 				JRadioButtonMenuItem item = (JRadioButtonMenuItem) o;
1215 
1216 				JInternalFrame frame = (JInternalFrame) m_radioMenuItemsAndFrames
1217 						.get(item);
1218 
1219 				if (item.isSelected()) {
1220 					try {
1221 						if (!frame.isVisible()) {
1222 							frame.setVisible(true);
1223 							frame.toFront();
1224 						}
1225 
1226 						if (!frame.isSelected()) {
1227 							frame.setSelected(true);
1228 						}
1229 
1230 						if (frame.isIcon()) {
1231 							frame.setIcon(false);
1232 						}
1233 					} catch (Exception e) {
1234 						e.printStackTrace();
1235 					}
1236 				} else {
1237 					// we cannot deselect the radio button, makes no sense...
1238 					item.setSelected(true);
1239 				}
1240 
1241 				// rebuild the windows menu
1242 				m_frameListener.manualFireEvent();
1243 			}
1244 		}
1245 
1246 	}
1247 
1248 	/**
1249 	 * Internal frame listener.
1250 	 */
1251 	private final class SelectFrameListener implements InternalFrameListener {
1252 
1253 		/**
1254 		 * Internal frame activated event method.
1255 		 * <p>
1256 		 * @param ife   The <code>InternalFrameEvent</code>.
1257 		 */
1258 		public void internalFrameActivated(InternalFrameEvent ife) {
1259 			JInternalFrame jif = ife.getInternalFrame();
1260 
1261 			JRadioButtonMenuItem item = (JRadioButtonMenuItem) m_framesAndRadioMenuItems
1262 					.get(jif);
1263 
1264 			item.setSelected(true);
1265 		}
1266 
1267 		/**
1268 		 * Internal frame closed event method.
1269 		 * <p>
1270 		 * @param ife   The <code>InternalFrameEvent</code>.
1271 		 */
1272 		public void internalFrameClosed(InternalFrameEvent ife) {
1273 		}
1274 
1275 		/**
1276 		 * Internal frame closing event method.
1277 		 * <p>
1278 		 * @param ife   The <code>InternalFrameEvent</code>.
1279 		 */
1280 		public void internalFrameClosing(InternalFrameEvent ife) {
1281 		}
1282 
1283 		/**
1284 		 * Internal frame deactivated event method.
1285 		 * <p>
1286 		 * @param ife   The <code>InternalFrameEvent</code>.
1287 		 */
1288 		public void internalFrameDeactivated(InternalFrameEvent ife) {
1289 			JInternalFrame jif = ife.getInternalFrame();
1290 
1291 			JRadioButtonMenuItem item = (JRadioButtonMenuItem) m_framesAndRadioMenuItems
1292 					.get(jif);
1293 
1294 			item.setSelected(false);
1295 
1296 			// if this is a normal frame e.g. DISPOSE_ON_CLOSE and we close
1297 			// it from X then a ContainerListener componentRemoved() will fire
1298 			// so we will rebuild the windows menu BUT if this is a
1299 			// HIDE_ON_CLOSE frame componentRemoved() is never invoked so we
1300 			// need to call this in order or apply the disabled color to this
1301 			// hidden frame...
1302 			if (jif.getDefaultCloseOperation() == WindowConstants.HIDE_ON_CLOSE) {
1303 				// rebuild the windows menu
1304 				m_frameListener.manualFireEvent();
1305 			}
1306 		}
1307 
1308 		/**
1309 		 * Internal frame deiconified event method.
1310 		 * <p>
1311 		 * @param ife   The <code>InternalFrameEvent</code>.
1312 		 */
1313 		public void internalFrameDeiconified(InternalFrameEvent ife) {
1314 		}
1315 
1316 		/**
1317 		 * Internal frame iconified event method.
1318 		 * <p>
1319 		 * @param ife   The <code>InternalFrameEvent</code>.
1320 		 */
1321 		public void internalFrameIconified(InternalFrameEvent ife) {
1322 		}
1323 
1324 		/**
1325 		 * Internal frame opened event method.
1326 		 * <p>
1327 		 * @param ife   The <code>InternalFrameEvent</code>.
1328 		 */
1329 		public void internalFrameOpened(InternalFrameEvent ife) {
1330 		}
1331 
1332 	}
1333 
1334 }