001: package net.suberic.util.swing;
002:
003: import javax.swing.*;
004: import java.awt.event.*;
005: import java.lang.reflect.Method;
006:
007: /**
008: * <p>
009: * This is a DesktopManager for a JDesktopPane which dynamically resizes
010: * when a JInternalFrame is moved out of the visible portion of the
011: * desktop. This means that all parts of your JInteralFrames will be
012: * available via the ScrollBars at all time.
013: * </p>
014: *
015: * <p>
016: * Currently, to use this class you need to set it as the DesktopManager
017: * of your JDesktopPane, and also register the JDesktopPane and its
018: * JScrollPane with this ScrollingDesktopManager:
019: * </p>
020: *
021: * <pre>
022: * JDesktopPane desktopPane = new JDesktopPane();
023: * JScrollPane scrollPane = new JScrollPane(desktopPane,
024: * JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED,
025: * JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
026: * ScrollingDesktopManager manager = new ScrollingDesktopManager(desktopPane,
027: * scrollPane);
028: * desktopPane.setDesktopManager(manager);
029: * </pre>
030: *
031: * <p>
032: * Note that it only makes sense to use this class with the
033: * SCROLLBAR_AS_NEEDED and SCROLLBAR_ALWAYS options on the JScrollPane.
034: * </p>
035: *
036: * @see javax.swing.JDesktopPane
037: * @see javax.swing.JScrollBar
038: * @see javax.swing.DefaultDesktopManager
039: * @version 1.0 6/28/2000
040: * @author Allen Petersen
041: */
042:
043: public class ScrollingDesktopManager extends DefaultDesktopManager
044: implements ContainerListener, AdjustmentListener {
045:
046: private JScrollPane scrollPane = null;
047:
048: private JDesktopPane pane = null;
049:
050: private boolean updating = false;
051:
052: //private Method getScrollModeMethod = null;
053:
054: //private Method setScrollModeMethod = null;
055:
056: private static Integer SIMPLE_SCROLL_MODE;
057:
058: /**
059: * <p>
060: * This creates a ScrollingDesktopManager for JDesktopPane pane
061: * in JScrollPane scrollPane.
062: * </p>
063: */
064: public ScrollingDesktopManager(JDesktopPane pane,
065: JScrollPane scrollPane) {
066: super ();
067: setDesktopPane(pane);
068: setScrollPane(scrollPane);
069: }
070:
071: /**
072: * <p>This extends the behaviour of DefaultDesktopManager by
073: * calling <code>updateDesktopSize()</code> after
074: * completing its action.
075: *
076: * Overrides
077: * <code>javax.swing.DefaultDesktopManager.closeFrame(JInternalFrame f).
078: * </code>
079: */
080: public void closeFrame(JInternalFrame f) {
081: super .closeFrame(f);
082: updateDesktopSize();
083:
084: // workaround for bug in jdk 1.5 (sigh)
085: SwingUtilities.invokeLater(new Runnable() {
086: public void run() {
087:
088: java.awt.KeyboardFocusManager mgr = java.awt.KeyboardFocusManager
089: .getCurrentKeyboardFocusManager();
090: pane.requestFocusInWindow();
091: }
092: });
093: }
094:
095: /**
096: * <p>This extends the behaviour of DefaultDesktopManager by
097: * calling <code>updateDesktopSize()</code> after
098: * completing its action.
099: *
100: * Overrides
101: * <code>javax.swing.DefaultDesktopManager.minimizeFrame(JInternalFrame f).
102: * </code>
103: */
104: public void minimizeFrame(JInternalFrame f) {
105: super .minimizeFrame(f);
106: updateDesktopSize();
107:
108: }
109:
110: /**
111: * <p>This extends the behaviour of DefaultDesktopManager by
112: * calling <code>updateDesktopSize()</code> after
113: * completing its action.
114: *
115: * Overrides
116: * <code>javax.swing.DefaultDesktopManager.iconifyFrame(JInternalFrame f).
117: * </code>
118: */
119: public void iconifyFrame(JInternalFrame f) {
120: super .iconifyFrame(f);
121: updateDesktopSize();
122: }
123:
124: /**
125: * <p>This extends the behaviour of DefaultDesktopManager by
126: * calling <code>updateDesktopSize()</code> after
127: * completing its action.
128: *
129: * Overrides
130: * <code>javax.swing.DefaultDesktopManager.deiconifyFrame(JInternalFrame f).
131: * </code>
132: */
133: public void deiconifyFrame(JInternalFrame f) {
134: super .deiconifyFrame(f);
135: updateDesktopSize();
136: }
137:
138: /**
139: * <p>This extends the behaviour of DefaultDesktopManager by
140: * calling <code>updateDesktopSize()</code> after
141: * completing its action.
142: *
143: * Overrides
144: * <code>javax.swing.DefaultDesktopManager.endDraggingFrame(JComponent f).
145: * </code>
146: */
147: public void endDraggingFrame(JComponent f) {
148: super .endDraggingFrame(f);
149:
150: updateDesktopSize();
151: }
152:
153: /**
154: * <p>This extends the behaviour of DefaultDesktopManager by
155: * calling <code>updateDesktopSize()</code> after
156: * completing its action.
157: *
158: * Overrides
159: * <code>javax.swing.DefaultDesktopManager.endResizingFrame(JComponent f).
160: * </code>
161: */
162:
163: public void endResizingFrame(JComponent f) {
164: super .endResizingFrame(f);
165: updateDesktopSize();
166: }
167:
168: /**
169: * <p>This overrides maximizeFrame() such that it only maximizes to the
170: * size of the Viewport, rather than to the entire size of the
171: * JDesktopPane.</p>
172: *
173: * Overrides
174: * <code>javax.swing.DefaultDesktopManager.maximizeFrame(JInternalFrame f)
175: * </code>
176: */
177: public void maximizeFrame(JInternalFrame f) {
178: if (scrollPane != null) {
179: java.awt.Dimension newSize = scrollPane.getViewport()
180: .getSize();
181: pane.setSize(newSize);
182: pane.setPreferredSize(newSize);
183: }
184:
185: super .maximizeFrame(f);
186:
187: }
188:
189: /**
190: * Implements componentRemoved() as an empty method. This is necessary
191: * because, in order to move components between layers, JLayeredPane
192: * removes the component from one layer and then adds it to the other
193: * layer. This can lead to problems, as the desktop may resize and
194: * remove the area where the pane was going to be replaced.
195: *
196: * Fortunately, closeFrame() is called when a JInternalFrame is
197: * actually removed from the JDesktopPane, so it should be safe to
198: * ignore the componentRemoved events.
199: */
200: public void componentRemoved(java.awt.event.ContainerEvent e) {
201: //updateDesktopSize();
202: }
203:
204: /**
205: * Implements componentAdded() to call updateDesktopSize().
206: */
207: public void componentAdded(java.awt.event.ContainerEvent e) {
208: updateDesktopSize();
209: }
210:
211: /**
212: * Implements adjustmentValueChanged() to call updateDesktopSize().
213: */
214: public void adjustmentValueChanged(java.awt.event.AdjustmentEvent e) {
215: updateDesktopSize();
216: }
217:
218: /**
219: * This actually does the updating of the parent JDesktopPane.
220: */
221: public void updateDesktopSize() {
222: if (!updating && scrollPane != null && scrollPane.isShowing()) {
223: updating = true;
224: //int oldValue = saveScrollMode();
225:
226: JScrollBar hsb = scrollPane.getHorizontalScrollBar();
227: JScrollBar vsb = scrollPane.getVerticalScrollBar();
228:
229: // calculate the min and max locations for all the frames.
230: JInternalFrame[] allFrames = pane.getAllFrames();
231: int min_x = 0, min_y = 0, max_x = 0, max_y = 0;
232: java.awt.Rectangle bounds = null;
233: // add to this the current viewable area.
234:
235: if (allFrames.length > 0) {
236: bounds = allFrames[0].getBounds(bounds);
237: min_x = bounds.x;
238: min_y = bounds.y;
239: max_x = bounds.width + bounds.x;
240: max_y = bounds.height + bounds.y;
241: for (int i = 1; i < allFrames.length; i++) {
242: bounds = allFrames[i].getBounds(bounds);
243: min_x = Math.min(min_x, bounds.x);
244: min_y = Math.min(min_y, bounds.y);
245: max_x = Math.max(max_x, bounds.width + bounds.x);
246: max_y = Math.max(max_y, bounds.height + bounds.y);
247: }
248: }
249:
250: int windowsWidth = max_x;
251: int windowsHeight = max_y;
252:
253: bounds = scrollPane.getViewport().getViewRect();
254: min_x = Math.min(min_x, bounds.x);
255: min_y = Math.min(min_y, bounds.y);
256: max_x = Math.max(max_x, bounds.width + bounds.x);
257: max_y = Math.max(max_y, bounds.height + bounds.y);
258:
259: if (min_x != 0 || min_y != 0) {
260: for (int i = 0; i < allFrames.length; i++) {
261: allFrames[i].setLocation(allFrames[i].getX()
262: - min_x, allFrames[i].getY() - min_y);
263:
264: }
265:
266: windowsWidth = windowsWidth - min_x;
267: windowsHeight = windowsHeight - min_y;
268: }
269:
270: int hval = hsb.getValue();
271: int vval = vsb.getValue();
272:
273: bounds = scrollPane.getViewport().getViewRect();
274: int oldViewWidth = bounds.width + hval;
275: int oldViewHeight = bounds.height + vval;
276:
277: int portWidth = scrollPane.getViewport().getSize().width;
278: int portHeight = scrollPane.getViewport().getSize().height;
279:
280: java.awt.Dimension dim = pane.getSize();
281: int oldWidth = dim.width;
282: int oldHeight = dim.height;
283:
284: pane.setSize(max_x - min_x, max_y - min_y);
285:
286: /*********************************/
287:
288: int prefWidth = max_x - min_x;
289: int prefHeight = max_y - min_y;
290:
291: boolean hasVsb = false, needsVsb = false, hasHsb = false, needsHsb = false;
292: // if a scrollbar is added, check to see if the space covered
293: // by the scrollbar is whitespace or not. if not, remove that
294: // whitespace from the preferredsize.
295:
296: if (vsb.isVisible()) {
297: hasVsb = true;
298: } else {
299: hasVsb = false;
300: }
301:
302: if (hsb.isVisible()) {
303: hasHsb = true;
304: } else {
305: hasHsb = false;
306: }
307:
308: if (max_x - min_x > scrollPane.getViewport().getViewRect().width)
309: needsHsb = true;
310: else
311: needsHsb = false;
312:
313: if (max_y - min_y > scrollPane.getViewport().getViewRect().height)
314: needsVsb = true;
315: else
316: needsVsb = false;
317:
318: if (hasVsb == false && needsVsb == true) {
319: if (windowsWidth < bounds.width + bounds.x
320: - vsb.getPreferredSize().width) {
321: prefWidth -= vsb.getPreferredSize().width;
322: }
323: } else if (hasVsb == true && needsVsb == false) {
324: if (windowsWidth <= bounds.width + bounds.x) {
325: prefWidth += vsb.getPreferredSize().width;
326: }
327: }
328:
329: if (hasHsb == false && needsHsb == true) {
330: if (windowsHeight < bounds.height + bounds.y
331: - hsb.getPreferredSize().height) {
332: prefHeight -= hsb.getPreferredSize().height;
333: }
334: } else if (hasHsb == true && needsHsb == false) {
335: if (windowsHeight <= bounds.height + bounds.y) {
336: prefHeight += hsb.getPreferredSize().height;
337: }
338: }
339:
340: /**************************************/
341:
342: pane.setPreferredSize(new java.awt.Dimension(prefWidth,
343: prefHeight));
344: scrollPane.validate();
345:
346: hsb = scrollPane.getHorizontalScrollBar();
347: vsb = scrollPane.getVerticalScrollBar();
348:
349: if (min_x != 0
350: && hval - min_x + hsb.getModel().getExtent() > hsb
351: .getMaximum()) {
352: hsb.setMaximum(hval - min_x
353: + hsb.getModel().getExtent());
354: }
355:
356: if (min_y != 0
357: && vval - min_y + vsb.getModel().getExtent() > vsb
358: .getMaximum()) {
359: vsb.setMaximum(vval - min_y
360: + vsb.getModel().getExtent());
361: }
362:
363: hsb.setValue(hval - min_x);
364:
365: vsb.setValue(vval - min_y);
366:
367: //resetScrollMode(oldValue);
368:
369: updating = false;
370: }
371: }
372:
373: /*
374:
375: public int saveScrollMode() {
376: JViewport viewport = scrollPane.getViewport();
377: if (getScrollModeMethod == null) {
378: configureScrollMethods();
379: }
380:
381: if (getScrollModeMethod.getName().equals("isBackingStoreEnabled")) {
382: if (viewport.isBackingStoreEnabled()) {
383: viewport .setBackingStoreEnabled(false);
384: return 1;
385: } else {
386: return 0;
387: }
388: } else {
389: try {
390: Integer retVal = (Integer) getScrollModeMethod.invoke(viewport, new Object[] { null });
391: setScrollModeMethod.invoke(scrollPane.getViewport(), new Object[] { SIMPLE_SCROLL_MODE });
392: return retVal.intValue();
393: } catch (Exception e) {
394: return 0;
395: }
396: }
397:
398: }
399:
400: public void resetScrollMode(int oldValue) {
401: JViewport viewport = scrollPane.getViewport();
402: if (setScrollModeMethod == null)
403: configureScrollMethods();
404:
405: if (setScrollModeMethod.getName().equals("setBackingStoreEnabled")) {
406: if (oldValue == 1)
407: viewport.setBackingStoreEnabled(true);
408: } else {
409: try {
410: setScrollModeMethod.invoke(viewport, new Object[] { new Integer(oldValue) });
411: } catch (Exception e) {
412: // dunno... ?
413: }
414: }
415:
416: }
417: private void configureScrollMethods() {
418: // heh. this will be lots and lots of fun.
419: Class viewportClass = scrollPane.getViewport().getClass();
420:
421: try {
422: getScrollModeMethod = viewportClass.getMethod("getScrollMode", null);
423: Class[] args = new Class[] {
424: Integer.TYPE
425: };
426: setScrollModeMethod = viewportClass.getMethod("setScrollMode", args);
427: SIMPLE_SCROLL_MODE = (Integer) viewportClass.getField("SIMPLE_SCROLL_MODE").get(null);
428: } catch (Exception e) {
429: try {
430: getScrollModeMethod = viewportClass.getMethod("isBackingStoreEnabled", null);
431: setScrollModeMethod = viewportClass.getMethod("setBackingStoreEnabled", new Class[] { Boolean.TYPE } );
432: } catch (Exception ex) {
433: // this would be bad. :)
434: }
435: }
436:
437:
438: }
439:
440: */
441:
442: /**
443: * This sets the scrollPane object. It also removes this as a
444: * listener on the previous scrollPane object (if any), and then sets
445: * it as an adjustmentListener on the scrollPane's JScrollBars.
446: */
447: public void setScrollPane(JScrollPane newScrollPane) {
448: if (scrollPane != null) {
449: scrollPane.getHorizontalScrollBar()
450: .removeAdjustmentListener(this );
451: scrollPane.getVerticalScrollBar().removeAdjustmentListener(
452: this );
453: }
454: scrollPane = newScrollPane;
455: scrollPane.getHorizontalScrollBar().addAdjustmentListener(this );
456: scrollPane.getVerticalScrollBar().addAdjustmentListener(this );
457: }
458:
459: public JScrollPane getScrollPane() {
460: return scrollPane;
461: }
462:
463: /**
464: * This sets the desktopPane object. It also removes this as a
465: * listener on the previous desktopPane object (if any), and then sets
466: * itself as a ContainerListener on the new JDesktopPane.
467: */
468: public void setDesktopPane(JDesktopPane newDesktopPane) {
469: if (pane != null)
470: pane.removeContainerListener(this );
471: pane = newDesktopPane;
472: pane.addContainerListener(this );
473: }
474:
475: public JDesktopPane getDesktopPane() {
476: return pane;
477: }
478: }
|