001: /*
002: * DockPanel.java
003: *
004: * Created on January 9, 2007, 6:22 AM
005: *
006: * Re-implementation of the dock to use a grid-bag-laid-out panel instead
007: * of the very complicated code I was developing.
008: *
009: * Copyright 2006-2007 Nigel Hughes
010: *
011: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
012: * in compliance with the License. You may obtain a copy of the License at http://www.apache.org/
013: * licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
015: * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
016: * governing permissions and limitations under the License.
017: */
018:
019: package com.blogofbug.swing.components;
020:
021: import com.blogofbug.swing.SwingBugUtilities;
022: import com.blogofbug.swing.components.effects.ComponentEffect;
023: import com.blogofbug.swing.components.effects.ComponentTextEffect;
024: import com.blogofbug.swing.components.effects.ContainerEffectManager;
025: import com.blogofbug.swing.components.effects.EffectContainer;
026: import com.blogofbug.swing.delegates.MouseTracker;
027: import com.blogofbug.swing.delegates.MouseTrackerListener;
028: import java.awt.Color;
029: import java.awt.Component;
030: import java.awt.Container;
031: import java.awt.Dimension;
032: import java.awt.Graphics;
033: import java.awt.GridBagConstraints;
034: import java.awt.GridBagLayout;
035: import java.awt.Point;
036: import java.awt.Rectangle;
037: import java.awt.event.ActionEvent;
038: import java.awt.event.ActionListener;
039: import java.awt.event.ComponentEvent;
040: import java.awt.event.ComponentListener;
041: import java.util.Hashtable;
042: import javax.swing.JComponent;
043: import javax.swing.JPanel;
044: import javax.swing.Timer;
045: import java.awt.Insets;
046:
047: /**
048: * A dock that can be added to the glass pane of a frame
049: * @author nigel
050: */
051: public class DockPanel extends JPanel implements MouseTrackerListener,
052: ActionListener, ContainerEffectManager.ComponentEffectSupplier {
053: /**
054: * Enumeration object to contain the side of the screen/window the dock is against
055: *
056: */
057: public enum Side {
058: /**
059: * Lock dock to top of the window
060: */
061: NORTH,
062: /**
063: * Lock the dock to the bottom of the window
064: */
065: SOUTH,
066: /**
067: * Locks the dock to the right of the dock
068: */
069: EAST,
070: /**
071: * Lock the dock to the left of the window
072: */
073: WEST
074: };
075:
076: //The enlarged size of the icons
077: /**
078: * The "mouse over" size of a dock entry
079: */
080: private int enlargedSize = 96;
081:
082: //The normal size of the icons
083: /**
084: * The "mouse out" size
085: */
086: private int normalSize = 32;
087:
088: //Insets around each dock icon
089: /**
090: * The insets around the dock
091: */
092: private int insets = 2;
093:
094: //The side the dock is docked to
095: /**
096: * The frame the dock is associated with
097: */
098: private Side dockedTo = Side.SOUTH;
099:
100: //Autohide?
101: /**
102: * Should the dock automatically hide when the mouse is outside of the area
103: */
104: private boolean autoHide = false;
105:
106: //Auto-hiding!
107: /**
108: * True if the dock is currently being hidden
109: */
110: private boolean hiding = false;
111:
112: //Layout and standard constraints
113: /**
114: * The layout being used for the container
115: */
116: private GridBagLayout layout = new GridBagLayout();
117: /**
118: * The constraint for the filler objects
119: */
120: private GridBagConstraints filler = new GridBagConstraints();
121: /**
122: * The constraints for the title object
123: */
124: private GridBagConstraints title = new GridBagConstraints();
125: /**
126: * The constraints for an icon in the dock...
127: */
128: private GridBagConstraints icon = new GridBagConstraints();
129:
130: /**
131: * Spacer object above the dock
132: */
133: protected JSpacer spacer;;
134: /**
135: * Spacer on the "left" of the dock
136: */
137: protected JSpacer firstSpacer;;
138: /**
139: * Spacer on the right of the dock
140: */
141: protected JSpacer lastSpacer;
142:
143: /**
144: * Controls whether or not autohiding is already on a trigger
145: */
146: protected boolean delayedAutoHide = false;
147:
148: /**
149: * Determines current in/out status
150: */
151: protected boolean couldAutoHide = false;
152:
153: /**
154: * Hold index of components and their titles
155: */
156: private Hashtable<Component, String> iconLabels = new Hashtable<Component, String>();
157:
158: /**
159: * Track mouse movements (will be over the entire component)
160: */
161: private MouseTracker mouseTracker;
162:
163: /**
164: * Animation timer
165: */
166: //private Timer timer = new Timer(0,this);
167: /**
168: * Records in an animation is currently planning
169: */
170: private boolean animating = false;
171:
172: private int minHidingSize = 16;
173:
174: //Mouse location (last known)
175: /**
176: * Last known location of the mouse
177: */
178: private Point lastKnownMouse = new Point(0, 0);
179:
180: private boolean returnToNormal = true;
181: private boolean overDockComponent = false;
182: private boolean normalTimerRunning = false;
183:
184: /**
185: * Effects for the dock components
186: */
187: protected ContainerEffectManager effects = new ContainerEffectManager(
188: this , this , true);
189:
190: /**
191: * Debug mode
192: */
193: private static final boolean DEBUG_MODE = true;
194:
195: /**
196: * Creates a new instance of DockPanel
197: * @param normalSize The normal size of the dock icons
198: * @param enlargedSize The "mouse over" size of the dock icons
199: * @param dockedTo The side the dock is attached to
200: */
201: public DockPanel(int normalSize, int enlargedSize, Side dockedTo) {
202: //Set up the basics
203: this .dockedTo = dockedTo;
204: this .enlargedSize = enlargedSize;
205: this .normalSize = normalSize;
206:
207: //Prepare the timer
208: // timer.setCoalesce(true);
209: // timer.setDelay(20);
210: // timer.setRepeats(true);
211: // timer.start();
212: startTimer();
213: addComponentListener(new ComponentListener() {
214: public void componentHidden(ComponentEvent componentEvent) {
215: stopTimer();
216: }
217:
218: public void componentMoved(ComponentEvent componentEvent) {
219: }
220:
221: public void componentResized(ComponentEvent componentEvent) {
222: }
223:
224: public void componentShown(ComponentEvent componentEvent) {
225: startTimer();
226: }
227: });
228:
229: //make me transparent
230: setBackground(null);
231: setOpaque(false);
232:
233: //Set up the mouse tracker
234: mouseTracker = new MouseTracker(this , this );
235:
236: //Set up the spacers
237: spacer = new JSpacer();
238: firstSpacer = new JSpacer();
239: lastSpacer = new JSpacer();
240:
241: //Initial setup
242: setLayout(layout);
243: initializeLayout();
244: add(spacer, filler);
245: add(firstSpacer, icon);
246: add(lastSpacer, icon);
247:
248: //Perform initial layout
249: updateLayout(null);
250: }
251:
252: private void startTimer() {
253: SwingBugUtilities.addTimerListener(this );
254: }
255:
256: private void stopTimer() {
257: SwingBugUtilities.removeTimerListener(this );
258: }
259:
260: /**
261: * Determines if the dock is currently hiding (or hidden)
262: * @return True if it is auto-hiding...
263: */
264: public boolean isAutoHiding() {
265: return autoHide;
266: }
267:
268: /**
269: * Turns auto-hiding on or off
270: * @param autoHide True to turn it on, false to turn it off
271: */
272: public void setAutoHiding(boolean autoHide) {
273: this .autoHide = autoHide;
274: mouseMoved(lastKnownMouse);
275: }
276:
277: /**
278: * Creates a new instance of the dock which will attach to the south of the frame
279: * @param normalSize The normal size of the icon
280: * @param enlargedSize The mouse-over size of icons in the dock
281: */
282: public DockPanel(int normalSize, int enlargedSize) {
283: this (normalSize, enlargedSize, Side.SOUTH);
284: }
285:
286: /**
287: * Sets the normal size of an icon in the dock
288: * @param normalSize The normal size.
289: */
290: public void setNormalSize(int normalSize) {
291: this .normalSize = normalSize;
292: updateLayout(null);
293: layout.layoutContainer(this );
294: }
295:
296: /**
297: * Sets the minimum dimension of the hidden component
298: * @param minSize The minimum dimension.
299: */
300: public void setMinHidingSize(int minSize) {
301: minHidingSize = minSize;
302: }
303:
304: /**
305: * Sets the mouse-over size of an icon in the dock
306: * @param enlargedSize The enlarged size
307: */
308: public void setEnlargedSize(int enlargedSize) {
309: this .enlargedSize = enlargedSize;
310: updateLayout(null);
311: layout.layoutContainer(this );
312: }
313:
314: /**
315: * Sets up the basic layout and then calls updateLayout to finalize the
316: * individual dock elements
317: */
318: private void initializeLayout() {
319: //Standard settings for the filler panel
320: filler.fill = GridBagConstraints.BOTH;
321: filler.weightx = 1.0;
322: filler.weighty = 1.0;
323:
324: //Standard settings for the title components
325: title.anchor = GridBagConstraints.CENTER;
326:
327: //Standard settings for the icon components
328: icon.anchor = GridBagConstraints.CENTER;
329: icon.weightx = 0.0;
330: icon.weighty = 0.0;
331: icon.insets = new Insets(insets / 2, insets / 2, insets / 2,
332: insets / 2);
333:
334: //Do various bits of specific constraints layout
335: switch (dockedTo) {
336: case SOUTH:
337: filler.gridx = 0;
338: filler.gridy = 0;
339: filler.gridheight = 1;
340: filler.gridwidth = GridBagConstraints.REMAINDER;
341: title.gridx = 0;
342: title.gridy = 1;
343: icon.gridx = 0;
344: icon.gridy = 2;
345: icon.anchor = GridBagConstraints.SOUTH;
346: break;
347: case NORTH:
348: filler.gridx = 0;
349: filler.gridy = 2;
350: filler.gridheight = 1;
351: filler.gridwidth = GridBagConstraints.REMAINDER;
352: title.gridx = 0;
353: title.gridy = 1;
354: icon.gridx = 0;
355: icon.gridy = 0;
356: icon.anchor = GridBagConstraints.NORTH;
357: break;
358: case WEST:
359: filler.gridx = 2;
360: filler.gridy = 0;
361: filler.gridheight = GridBagConstraints.REMAINDER;
362: filler.gridwidth = 1;
363: title.gridx = 1;
364: title.gridy = 0;
365: icon.gridx = 0;
366: icon.gridy = 0;
367: icon.anchor = GridBagConstraints.WEST;
368: break;
369: case EAST:
370: filler.gridx = 0;
371: filler.gridy = 0;
372: filler.gridheight = GridBagConstraints.REMAINDER;
373: filler.gridwidth = 1;
374: title.gridx = 1;
375: title.gridy = 0;
376: icon.gridx = 2;
377: icon.gridy = 0;
378: icon.anchor = GridBagConstraints.EAST;
379: break;
380: }
381: }
382:
383: /**
384: * Changes the side of the panel the dock is attached to
385: * @param dockedTo Determines the side of the frame the dock is connected to
386: */
387: public void setDockedTo(DockPanel.Side dockedTo) {
388: this .dockedTo = dockedTo;
389: initializeLayout();
390: layout.setConstraints(spacer, filler);
391: updateLayout(null);
392: layout.layoutContainer(this );
393: }
394:
395: /**
396: * Determines the appropriate gridbag weight for the "book-end" spacers
397: * at either end of the dock
398: *
399: * @return The weight
400: */
401: private double spacerWeightX() {
402: switch (dockedTo) {
403: case NORTH:
404: case SOUTH:
405: return 1.0;
406: case EAST:
407: case WEST:
408: return 0.0;
409: }
410: return 0.0;
411: }
412:
413: /**
414: * Determines the appropriate gridbag weight for the "book-end" spacers
415: * at either end of the dock
416: *
417: * @return The weight
418: */
419: private double spacerWeightY() {
420: return 1.0 - spacerWeightX();
421: }
422:
423: /**
424: * Determines the appropriate size for the component based on the current "mouse over"
425: * component, the location in the dock area relative to the overall dock panel, and a given component
426: * This is protected so it can be over-ridden to apply a better alogrithm .
427: *
428: * @param p The location of the mouse relative to the overall dock
429: * @param component The component to be sized
430: * @param highlightedComponent The current component with the mouse over it
431: * @return A Dimension object with the recommened size of the component
432: */
433: protected Dimension getComponentPreferedSize(Point p,
434: Component component, Component highlightedComponent) {
435: //If we are hiding it should be small
436: if (hiding) {
437: int size = Math.max(insets * 3, minHidingSize);
438: return new Dimension(size, size);
439: }
440:
441: //determine it's distance based on half the size of the biggest component
442: double compSize = component.getSize().height;
443: double distanceFromCenter = 0;
444: double delta = (double) (enlargedSize - normalSize);
445:
446: if ((highlightedComponent != null)) {
447: switch (dockedTo) {
448: case NORTH:
449: case SOUTH:
450: distanceFromCenter = Math.abs((double) ((component
451: .getLocation().x + compSize / 2) - p.x));
452: break;
453: case EAST:
454: case WEST:
455: distanceFromCenter = Math.abs((double) ((component
456: .getLocation().y + compSize / 2) - p.y));
457: break;
458: }
459:
460: // if (highlightedComponent==component){
461: if (distanceFromCenter < enlargedSize / 2) {
462: return new Dimension(enlargedSize, enlargedSize);
463: }
464:
465: double cdy = 1.0 - distanceFromCenter
466: / (double) (enlargedSize * 2);
467: double tSize = Math.max((double) normalSize + cdy * delta,
468: (double) normalSize);
469: int size = (int) tSize;
470:
471: return new Dimension(size, size);
472: } else {
473: if (returnToNormal) {
474: return new Dimension(normalSize, normalSize);
475: } else {
476: return component.getPreferredSize();
477: }
478: }
479: }
480:
481: /**
482: * Essentially removes the spacer that forces the dock to the bottom of the
483: * panel, neat if you would like to use the panel as something like a
484: * special tab top.
485: *
486: * @param fillSpace true if the panel should fill up any empty space above the dock,
487: * false if it should not
488: */
489: public void fillSpace(boolean fillSpace) {
490: spacer.setVisible(fillSpace);
491: }
492:
493: /**
494: * Determines a value between the current and target including easing. Can
495: * be over-ridden if desired to have different sizing behavior. Implementers
496: * should note that if the size is not clipped to ensure that the icons will
497: * all fit in the dock, gridbag does Bad Things (tm).
498: *
499: * @param current The current value
500: * @param target The target value
501: * @return A value equal to or between target and current
502: */
503: protected double tweenValue(double current, double target) {
504: //Just make sure we do nothing if they are the same
505: if (current == target) {
506: return target;
507: }
508:
509: //Determine the difference
510: double delta = (target - current) / 8.0;
511:
512: //Tween in the right direction, but always by at least one pixel
513: if (delta > 0) {
514: delta = Math.max(1.0, delta);
515: } else if (delta < 0) {
516: delta = Math.min(-1.0, delta);
517: }
518:
519: switch (dockedTo) {
520: case SOUTH:
521: case NORTH:
522: return Math.min(current + delta, (getWidth() - insets
523: * iconLabels.size() * 2)
524: / iconLabels.size() - 10);
525: default:
526: return Math.min(current + delta, (getHeight() - insets
527: * iconLabels.size() * 2)
528: / iconLabels.size());
529: }
530: }
531:
532: /**
533: * Determines if a component is in the dock
534: * @param component The component you wish to test to see if its in the dock
535: * @return True if it does, false if it doesn't
536: */
537: public boolean dockContains(Component component) {
538: if (iconLabels.get(component) != null) {
539: return true;
540: }
541: return false;
542: }
543:
544: /**
545: * Takes a current size, and determines a new size setting up an animation
546: * if needed.
547: *
548: * @param currentSize The current size of the component
549: * @param newSize The new size of the component
550: * @return The in-between size
551: */
552: private Dimension tweenSize(Dimension currentSize, Dimension newSize) {
553: if ((newSize.width != currentSize.width)
554: || (newSize.height != currentSize.height)) {
555: animating = true;
556: newSize.width = (int) tweenValue(currentSize.width,
557: newSize.width);
558: newSize.height = (int) tweenValue(currentSize.height,
559: newSize.height);
560: }
561: return newSize;
562: }
563:
564: /**
565: *
566: *
567: */
568: private void setReturnToNormal() {
569: if (returnToNormal) {
570: return;
571: }
572: normalTimerRunning = true;
573: SwingBugUtilities.invokeAfter(new Runnable() {
574: public void run() {
575: if (!overDockComponent) {
576: returnToNormal = true;
577: normalTimerRunning = false;
578: validate();
579: }
580: }
581: }, 100);
582: }
583:
584: /**
585: * Debug method
586: *
587: * @param component The component
588: */
589: protected void showComponentLabel(JComponent component, String text) {
590: //Debugging information
591: if (DEBUG_MODE) {
592: ComponentEffect ce = effects
593: .getEffectFor((JComponent) component);
594: if ((ce != null) && (ce instanceof ComponentTextEffect)) {
595: ((ComponentTextEffect) ce).setText(text);
596: }
597: }
598: }
599:
600: /**
601: * Determines the new settings for the gridbag layout, and then applies them
602: * to all components
603: * @param p The point the mouse is at
604: */
605: private void updateLayout(Point p) {
606: Component highlightedComponent = null;
607: animating = false;
608: if (p != null) {
609: highlightedComponent = getComponentAt(p);
610: //Make sure it's at least in the dock zone
611: switch (dockedTo) {
612: case NORTH:
613: if (p.y > spacer.getY()) {
614: highlightedComponent = null;
615: }
616: break;
617: case SOUTH:
618: if (p.y < spacer.getHeight()) {
619: highlightedComponent = null;
620: }
621: break;
622: case WEST:
623: if (p.x > spacer.getX()) {
624: highlightedComponent = null;
625: }
626: break;
627: case EAST:
628: if (p.x < spacer.getWidth()) {
629: highlightedComponent = null;
630: }
631: break;
632: }
633: if ((highlightedComponent == firstSpacer)
634: || (highlightedComponent == lastSpacer)) {
635: highlightedComponent = null;
636: }
637: }
638: //Variables used for incrementing grid-x's and grid-y's
639: int dx = 0, dy = 0;
640: switch (dockedTo) {
641: case NORTH:
642: case SOUTH:
643: dx = 1;
644: break;
645: case EAST:
646: case WEST:
647: dy = 1;
648: break;
649: }
650:
651: //Set everything up as it should be
652: initializeLayout();
653:
654: //Add first spacer and move to next position
655: icon.weightx = spacerWeightX();
656: icon.weighty = spacerWeightY();
657: icon.fill = GridBagConstraints.BOTH;
658: layout.setConstraints(firstSpacer, icon);
659: icon.fill = GridBagConstraints.NONE;
660: icon.gridx += dx;
661: icon.gridy += dy;
662: title.gridx += dx;
663: title.gridy += dy;
664:
665: if (highlightedComponent == null) {
666: setReturnToNormal();
667: overDockComponent = false;
668: } else {
669: overDockComponent = true;
670: returnToNormal = false;
671: }
672:
673: //Iterate through the components updating everything
674: Component[] components = getComponents();
675: for (Component component : components) {
676: //Make sure it's not a filler or a title
677: if (!((component == spacer)
678: || (component instanceof DockLabel)
679: || (component == firstSpacer) || (component == lastSpacer))) {
680: //Set the weight
681: icon.weightx = 0.0;
682: icon.weighty = 0.0;
683: //Icon first
684: layout.setConstraints(component, icon);
685:
686: //Set the prefered size of the component
687: component.setPreferredSize(tweenSize(component
688: .getSize(), getComponentPreferedSize(p,
689: component, highlightedComponent)));
690:
691: //Set the visibility of its label
692: if (highlightedComponent == component) {
693: showComponentLabel((JComponent) component,
694: iconLabels.get(component));
695: } else {
696: showComponentLabel((JComponent) component, "");
697: }
698:
699: //Move to next position
700: icon.gridx += dx;
701: icon.gridy += dy;
702: title.gridx += dx;
703: title.gridy += dy;
704: }
705: }
706:
707: //Add the last spacer
708: icon.weightx = spacerWeightX();
709: icon.weighty = spacerWeightY();
710: icon.fill = GridBagConstraints.BOTH;
711: layout.setConstraints(lastSpacer, icon);
712: }
713:
714: /**
715: * Adds a new item to the dock
716: * @param component The component to add
717: * @param label The label to display when the mouse is over the dock item
718: */
719: public void addDockElement(Component component, String label) {
720: if (iconLabels.get(component) != null) {
721: return;
722: }
723:
724: //We would like it to do something funcky as it adds them and make them grow
725: component.setPreferredSize(new Dimension(0, 0));
726: iconLabels.put(component, label);
727: add(component);
728: updateLayout(mouseTracker.getPosition());
729: }
730:
731: /**
732: * Not interested
733: * @param mouseEntered True if the mouse came in
734: */
735: public void mouseCrossThreshold(boolean mouseEntered) {
736: }
737:
738: /**
739: * Fires an auto-hide check after a delay to ensure the mouse didn't just
740: * stray out of the dock on the way from one place to another.
741: */
742: protected void delayedAutoHide() {
743: if (delayedAutoHide) {
744: return;
745: }
746: delayedAutoHide = true;
747: SwingBugUtilities.invokeAfter(new Runnable() {
748: public void run() {
749: hiding = couldAutoHide;
750: if (hiding) {
751: validate();
752: }
753: delayedAutoHide = false;
754: }
755: }, 1000);
756: }
757:
758: /**
759: * When the mouse moves, update the layout. Should be optimized when the
760: * highlighted component is the spacer to not do anything
761: * @param position The location of the mouse
762: */
763: public void mouseMoved(Point position) {
764: if (autoHide) {
765: Component mouseOver = getComponentAt(position);
766: final Component self = this ;
767: if ((mouseOver == firstSpacer) || (mouseOver == lastSpacer)
768: || (spacer == mouseOver) || (mouseOver == this )) {
769: couldAutoHide = true;
770: if (hiding == false) {
771: delayedAutoHide();
772: }
773: } else {
774: hiding = false;
775: couldAutoHide = false;
776: }
777: } else {
778: hiding = false;
779: couldAutoHide = false;
780: }
781: lastKnownMouse = position;
782: updateLayout(position);
783: layout.layoutContainer(this );
784: }
785:
786: /**
787: * Returns true if the mouse is over a dock component, but false at all other times. This will cause
788: * swing to pass on the event to the next layer down.
789: * @param x The frame relative dock location x-cordination
790: * @param y The frame relative dock location y-coordinate
791: * @return True if the mouse is over a dock icon, false otherwise
792: */
793: public boolean contains(int x, int y) {
794: Rectangle rect = new Rectangle();
795: //Have to implement our own componentAt here
796: for (Component comp : getComponents()) {
797: rect = comp.getBounds(rect);
798: if (rect.contains(x, y)) {
799: if (comp instanceof JSpacer) {
800: // System.out.println("Out");
801: mouseMoved(new Point(-1, -1));
802: return false;
803: }
804: // System.out.println("In");
805: return true;
806: }
807: }
808: //A little non-intuitive. If it's not over a normal component or a spacer, it's in the dock area
809: return true;
810: }
811:
812: protected void paintChildren(Graphics graphics) {
813: super .paintChildren(graphics);
814: if (DEBUG_MODE) {
815: effects.paintEffects(graphics);
816: }
817: }
818:
819: /**
820: * Paints the component.
821: *
822: * @param graphics The graphics context
823: */
824: public void paintComponent(Graphics graphics) {
825: graphics.setColor(getBackground());
826: Rectangle dockSize = null;
827: int oldNormalSize = normalSize;
828: if (hiding) {
829: normalSize = Math
830: .min(
831: normalSize,
832: ((dockedTo == Side.NORTH) || (dockedTo == Side.SOUTH)) ? firstSpacer
833: .getHeight()
834: : firstSpacer.getWidth());
835: normalSize = Math.max(normalSize, minHidingSize);
836: }
837: //Draw the main dock background
838: switch (dockedTo) {
839: case NORTH:
840: dockSize = new Rectangle(firstSpacer.getX()
841: + firstSpacer.getWidth(), 0, getWidth()
842: - (lastSpacer.getWidth() + firstSpacer.getWidth()),
843: normalSize);
844: break;
845: case SOUTH:
846: dockSize = new Rectangle(firstSpacer.getX()
847: + firstSpacer.getWidth(), getHeight() - normalSize,
848: getWidth()
849: - (lastSpacer.getWidth() + firstSpacer
850: .getWidth()), normalSize);
851: break;
852: case WEST:
853: dockSize = new Rectangle(0, firstSpacer.getHeight(),
854: normalSize, getHeight()
855: - (lastSpacer.getHeight() + firstSpacer
856: .getHeight()));
857: break;
858: case EAST:
859: dockSize = new Rectangle(getWidth() - normalSize,
860: firstSpacer.getHeight(), normalSize, getHeight()
861: - (lastSpacer.getHeight() + firstSpacer
862: .getHeight()));
863: break;
864: }
865: if (hiding) {
866: normalSize = oldNormalSize;
867: }
868:
869: dockSize.x -= insets;
870: dockSize.y -= insets;
871: dockSize.width += insets * 2;
872: dockSize.height += insets * 2;
873: graphics.fillRect(dockSize.x, dockSize.y, dockSize.width,
874: dockSize.height);
875:
876: //Draw the outline
877: graphics.setColor(new Color(255, 255, 255, 127));
878: switch (dockedTo) {
879: case SOUTH:
880: graphics.drawRect(dockSize.x, dockSize.y,
881: dockSize.width - 1, dockSize.height);
882: break;
883: }
884: super .paintComponent(graphics);
885: }
886:
887: /**
888: * Called when the animation timer fires
889: * @param actionEvent The action event
890: */
891: public void actionPerformed(ActionEvent actionEvent) {
892: if (animating) {
893: updateLayout(lastKnownMouse);
894: layout.layoutContainer(this );
895: }
896: }
897:
898: public ComponentEffect getEffect(JComponent forComponent,
899: Container inComponent, EffectContainer effectEngine) {
900: if (this .DEBUG_MODE) {
901: if (forComponent instanceof JSpacer) {
902: return null;
903: } else {
904: return new ComponentTextEffect(inComponent,
905: forComponent, effectEngine, "",
906: ComponentTextEffect.TextLocation.NORTH);
907: }
908: }
909: return null;
910: }
911:
912: //For convenience
913: /**
914: * A utilitity class to fill the gaps around the dock contents
915: */
916: public class JSpacer extends JComponent {
917: /**
918: * Creates a new instance of the dock spacer
919: */
920: public JSpacer() {
921: setBackground(null);
922: setOpaque(false);
923: }
924:
925: }
926:
927: //Temporary until I do something else
928: /**
929: * A utiliity class to represent the label of the currently mouse-over dock item
930: */
931: public class DockLabel extends StrokedLabel {
932: /**
933: * Creats a new instance of the dock label
934: * @param title The text of the label
935: */
936: public DockLabel(String title) {
937: super(title);
938: }
939:
940: };
941: }
|