Source code: com/imagero/gui/flowin/FloatingWindowManager.java
1 /*
2 * Copyright (c) imagero Andrey Kuznetsov. All Rights Reserved.
3 * http://jgui.imagero.com
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 package com.imagero.gui.flowin;
21
22 import com.imagero.gui.swing.SCheckBoxMenuItem;
23 import com.imagero.gui.swing.SelectableAction;
24
25 import javax.swing.*;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.awt.*;
29 import java.beans.PropertyChangeEvent;
30 import java.beans.PropertyChangeListener;
31 import java.util.ArrayList;
32 import java.util.Hashtable;
33
34 /**
35 * <pre>
36 * FloatinWindowManager creates and keeps up to date menu
37 * (for JMenuBar) and popup menu (for FloatingWindow's title bar)
38 * and helps also to manage properties of FloatingWindow.
39 * </pre>
40 *
41 * @author Andrei Kouznetsov
42 * Date: 17.08.2004
43 * Time: 08:38:10
44 */
45 public class FloatingWindowManager {
46
47 ArrayList windows = new ArrayList();
48 JPopupMenu popup;
49 JMenu menu;
50
51 JCheckBoxMenuItem magnetic;
52 JCheckBoxMenuItem followsOwner;
53 JCheckBoxMenuItem staysOnScreen;
54 final JSeparator separator = new JSeparator();
55
56 boolean showMagnetic = true;
57 boolean showFollowOwner = true;
58 boolean showRestrictToScreen = true;
59
60 Hashtable popupItems = new Hashtable();
61
62 FloatingWindow active, drag;
63
64 PropertyChangeListener focusListener;
65 Timer t;
66
67
68 /**
69 * create FloatingWindowManager
70 */
71 public FloatingWindowManager() {
72 this(null);
73 }
74
75 /**
76 * create FloatingWindowManager
77 * @param label menu label
78 * @see #getMenu
79 */
80 public FloatingWindowManager(String label) {
81 popup = new JPopupMenu(label);
82 magnetic = new JCheckBoxMenuItem("magnetic");
83 followsOwner = new JCheckBoxMenuItem("follow owner frame");
84 staysOnScreen = new JCheckBoxMenuItem("stay on screen");
85
86 popup.add(magnetic);
87 popup.add(followsOwner);
88 popup.add(staysOnScreen);
89 popup.add(separator);
90
91 menu = new JMenu(label);
92
93 magnetic.addActionListener(new ActionListener() {
94 public void actionPerformed(ActionEvent e) {
95 setMagneticImpl();
96 }
97 });
98 followsOwner.addActionListener(new ActionListener() {
99 public void actionPerformed(ActionEvent e) {
100 setFollowImpl();
101 }
102 });
103 staysOnScreen.addActionListener(new ActionListener() {
104 public void actionPerformed(ActionEvent e) {
105 setRectrictedImpl();
106 }
107 });
108
109 t = new Timer(50, new ActionListener() {
110 public void actionPerformed(ActionEvent e) {
111 if (drag != null) {
112 setActiveWindow(drag);
113 return;
114 }
115
116 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
117 Component component = kfm.getFocusOwner();
118 if (lastDrag != null) {
119 setActiveWindow(lastDrag);
120 lastDrag = null;
121 }
122 else {
123 if (component != null) {
124 Container parent = component.getParent();
125 while (parent != null) {
126 if (parent instanceof ContentPane) {
127 ContentPane contentPane = (ContentPane) parent;
128 contentPane.getContainer().firePropertyChange(ContentPane.GOT_FOCUS, false, true);
129 contentPane.setLastFocusOwner(component);
130 break;
131 }
132 parent = parent.getParent();
133 }
134 if (parent == null) {
135 if (active != null) {
136 setActiveWindow(null);
137 }
138 }
139 }
140 }
141 }
142 });
143 t.setRepeats(false);
144
145 focusListener = new PropertyChangeListener() {
146 public void propertyChange(PropertyChangeEvent e) {
147 t.restart();
148 }
149 };
150 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
151 kfm.addPropertyChangeListener(focusListener);
152
153 UIManager.addPropertyChangeListener(new PropertyChangeListener() {
154 public void propertyChange(PropertyChangeEvent evt) {
155 SwingUtilities.updateComponentTreeUI(popup);
156 }
157 });
158 }
159
160 private void setRectrictedImpl() {
161 for (int i = 0; i < windows.size(); i++) {
162 FloatingWindow fw = (FloatingWindow) windows.get(i);
163 fw.setStaysOnScreen(staysOnScreen.isSelected());
164 }
165 }
166
167 private void setFollowImpl() {
168 for (int i = 0; i < windows.size(); i++) {
169 FloatingWindow fw = (FloatingWindow) windows.get(i);
170 fw.setFollowOwner(followsOwner.isSelected());
171 }
172 }
173
174 private void setMagneticImpl() {
175 for (int i = 0; i < windows.size(); i++) {
176 FloatingWindow fw = (FloatingWindow) windows.get(i);
177 fw.setMagnetic(magnetic.isSelected());
178 }
179 }
180
181 public int getDragOverAction() {
182 return ((FloatingWindow) windows.get(0)).getDragOverAction();
183 }
184
185 public void setDragOverAction(int dragOverAction) {
186 for (int i = 0; i < windows.size(); i++) {
187 FloatingWindow fw = (FloatingWindow) windows.get(i);
188 fw.setDragOverAction(dragOverAction);
189 }
190 }
191
192 public int getDockDelay() {
193 return ((FloatingWindow) windows.get(0)).getDockDelay();
194 }
195
196 public void setDockDelay(int dockDelay) {
197 for (int i = 0; i < windows.size(); i++) {
198 FloatingWindow fw = (FloatingWindow) windows.get(i);
199 fw.setDockDelay(dockDelay);
200 }
201 }
202
203 /**
204 * add FloatingWindow to FloatingWindowManager's list
205 * @param fw FloatingWindow
206 */
207 public void add(final FloatingWindow fw) {
208 if (fw != null && !windows.contains(fw)) {
209 windows.add(fw);
210
211 final SelectableAction action = new SelectableAction(fw.getTitle()) {
212 public void actionPerformed(ActionEvent e) {
213 fw.setVisible(isSelected());
214 }
215 };
216 SCheckBoxMenuItem popupItem = new SCheckBoxMenuItem(action);
217 SCheckBoxMenuItem menuItem = new SCheckBoxMenuItem(action);
218
219 Helper helper = new Helper();
220 helper.popupItem = popupItem;
221 helper.menuItem = menuItem;
222 helper.action = action;
223 helper.savedShowPopupMenu = fw.isShowPopupMenu();
224
225 popupItems.put(fw, helper);
226 popup.add(popupItem);
227 action.setSelected(fw.isVisible());
228 menu.add(menuItem);
229 fw.setShowPopupMenu(true);
230 fw.setPopup(popup);
231 fw.propertyChangeManager.addPropertyChangeListener(new PropertyChangeListener() {
232 public void propertyChange(PropertyChangeEvent evt) {
233 if (evt.getPropertyName().equals("isActive")) {
234 if (evt.getNewValue() == Boolean.TRUE) {
235 setActiveWindow(fw);
236 }
237 }
238 else if (evt.getPropertyName() == (FloatingWindow.PROPERTY_NAME)) {
239 if (fw.isDragging()) {
240 drag = fw;
241 }
242 else {
243 if (drag == fw) {
244 lastDrag = drag;
245 drag = null;
246 }
247 }
248 updateActions(fw);
249 }
250 }
251 });
252 }
253 }
254
255 FloatingWindow lastDrag;
256
257 /**
258 * update state of Action(s)
259 * @param fw FloatingWindow
260 */
261 private void updateActions(FloatingWindow fw) {
262 Helper helper = (Helper) popupItems.get(fw);
263 if (helper != null) {
264 helper.action.setSelected(fw.isVisible());
265 }
266 }
267
268 /**
269 * removes FloatingWindow from FloatingWindowManager's list
270 * @param fw FloatingWindow
271 * @return true if FloatingWindow was successfully removed
272 */
273 public boolean remove(FloatingWindow fw) {
274 if (fw != null) {
275 Helper helper = (Helper) popupItems.remove(fw);
276 if (helper != null) {
277 popup.remove(helper.popupItem);
278 menu.remove(helper.menuItem);
279 fw.setShowPopupMenu(helper.savedShowPopupMenu);
280 }
281 fw.setPopup(null);
282 return windows.remove(fw);
283 }
284 return false;
285 }
286
287 /**
288 * get JMenu created by FloatingWindowManager
289 * @return JMenu
290 */
291 public JMenu getMenu() {
292 return menu;
293 }
294
295 /**
296 * @return true if 'magnetic' menu item is shown (popup menu only)
297 */
298 public boolean isShowMagnetic() {
299 return showMagnetic;
300 }
301
302 /**
303 * @param showMagnetic if true then 'magnetic' menu item is shown
304 */
305 public void setShowMagnetic(boolean showMagnetic) {
306 this.showMagnetic = showMagnetic;
307 magnetic.setVisible(showMagnetic);
308 checkSeparator();
309 }
310
311 /**
312 * @return if true then 'follow owner' menu item is shown
313 */
314 public boolean isShowFollowOwner() {
315 return showFollowOwner;
316 }
317
318 /**
319 * @param showFollowOwner boolean - if true then 'follow owner' menu item is shown
320 */
321 public void setShowFollowOwner(boolean showFollowOwner) {
322 this.showFollowOwner = showFollowOwner;
323 followsOwner.setVisible(showFollowOwner);
324 checkSeparator();
325 }
326
327 /**
328 * @return boolean - if true then 'restrict to screen' menu item is shown
329 */
330 public boolean isShowStayOnScreen() {
331 return showRestrictToScreen;
332 }
333
334 /**
335 * @param showRestrictToScreen - if true then 'restrict to screen' menu item is shown
336 */
337 public void setShowStayOnScreen(boolean showRestrictToScreen) {
338 this.showRestrictToScreen = showRestrictToScreen;
339 staysOnScreen.setVisible(showRestrictToScreen);
340 checkSeparator();
341 }
342
343 public boolean isMagnetic() {
344 return magnetic.isSelected();
345 }
346
347 public void setMagnetic(boolean magnetic) {
348 this.magnetic.setSelected(magnetic);
349 setMagneticImpl();
350 }
351
352 public boolean isFollowsOwner() {
353 return followsOwner.isSelected();
354 }
355
356 public void setFollowsOwner(boolean followsOwner) {
357 this.followsOwner.setSelected(followsOwner);
358 setFollowImpl();
359 }
360
361 public boolean isStaysOnScreen() {
362 return staysOnScreen.isSelected();
363 }
364
365 public void setStaysOnScreen(boolean stayOnScreen) {
366 this.staysOnScreen.setSelected(stayOnScreen);
367 setRectrictedImpl();
368 }
369
370 /**
371 * separator should be visible if one of build in menu items (follow owner/magnetic/restrict to screen) is shown
372 */
373 protected void checkSeparator() {
374 if (showFollowOwner | showMagnetic | showRestrictToScreen) {
375 separator.setVisible(true);
376 }
377 else {
378 separator.setVisible(false);
379 }
380 }
381
382 public void setActiveWindow(FloatingWindow fw) {
383 if (active != fw) {
384 if (active != null) {
385 active.setActive(false);
386 }
387 if (fw != null) {
388 fw.setActive(true);
389 }
390 this.active = fw;
391 }
392 }
393
394 public FloatingWindow getActiveWindow() {
395 return active;
396 }
397
398 private static class Helper {
399 SCheckBoxMenuItem menuItem, popupItem;
400 SelectableAction action;
401 boolean savedShowPopupMenu;
402 }
403 }