001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance;
031:
032: import java.awt.*;
033: import java.awt.event.MouseEvent;
034: import java.beans.PropertyChangeEvent;
035: import java.beans.PropertyChangeListener;
036:
037: import javax.swing.*;
038: import javax.swing.border.EmptyBorder;
039: import javax.swing.plaf.basic.BasicSplitPaneDivider;
040: import javax.swing.plaf.basic.BasicSplitPaneUI;
041:
042: import org.jvnet.lafwidget.animation.*;
043: import org.jvnet.substance.theme.SubstanceTheme;
044: import org.jvnet.substance.utils.*;
045: import org.jvnet.substance.utils.icon.TransitionAwareIcon;
046:
047: /**
048: * Split pane divider in <code>Substance</code> look and feel.
049: *
050: * @author Kirill Grouchnikov
051: */
052: public class SubstanceSplitPaneDivider extends BasicSplitPaneDivider
053: implements Trackable {
054: /**
055: * Listener for fade animations.
056: */
057: private RolloverControlListener substanceRolloverListener;
058: /**
059: * Listener for fade animations.
060: */
061: protected FadeStateListener substanceFadeStateListener;
062: /**
063: * Listener on property change events.
064: */
065: private PropertyChangeListener substancePropertyChangeListener;
066: /**
067: * Surrogate button model for tracking the thumb transitions.
068: */
069: private ButtonModel gripModel;
070:
071: /**
072: * Simple constructor.
073: *
074: * @param ui
075: * Associated UI.
076: */
077: public SubstanceSplitPaneDivider(SubstanceSplitPaneUI ui) {
078: super (ui);
079: }
080:
081: @Override
082: public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) {
083: if (newUI != null) {
084: // installing
085: this .splitPane = newUI.getSplitPane();
086:
087: this .gripModel = new DefaultButtonModel();
088: this .gripModel.setArmed(false);
089: this .gripModel.setSelected(false);
090: this .gripModel.setPressed(false);
091: this .gripModel.setRollover(false);
092: this .gripModel.setEnabled(this .splitPane.isEnabled());
093:
094: // fix for defect 109 - memory leak on changing theme
095: this .substanceRolloverListener = new RolloverControlListener(
096: this , this .gripModel);
097: this .addMouseListener(this .substanceRolloverListener);
098: this .addMouseMotionListener(this .substanceRolloverListener);
099:
100: this .substancePropertyChangeListener = new PropertyChangeListener() {
101: public void propertyChange(PropertyChangeEvent evt) {
102: if ("enabled".equals(evt.getPropertyName())) {
103: gripModel.setEnabled(splitPane.isEnabled());
104: }
105: }
106: };
107: this .splitPane
108: .addPropertyChangeListener(this .substancePropertyChangeListener);
109:
110: this .substanceFadeStateListener = new FadeStateListener(
111: this , this .gripModel, SubstanceCoreUtilities
112: .getFadeCallback(this .splitPane,
113: this .gripModel, false, false, this ));
114: this .substanceFadeStateListener.registerListeners(false);
115: } else {
116: // uninstalling
117: // fix for defect 109 - memory leak on changing theme
118: this .removeMouseListener(this .substanceRolloverListener);
119: this
120: .removeMouseMotionListener(this .substanceRolloverListener);
121: this .substanceRolloverListener = null;
122:
123: this .splitPane
124: .removePropertyChangeListener(this .substancePropertyChangeListener);
125: this .substancePropertyChangeListener = null;
126:
127: this .substanceFadeStateListener.unregisterListeners();
128: this .substanceFadeStateListener = null;
129: }
130: super .setBasicSplitPaneUI(newUI);
131: }
132:
133: /*
134: * (non-Javadoc)
135: *
136: * @see java.awt.Component#paint(java.awt.Graphics)
137: */
138: @Override
139: public void paint(Graphics g) {
140: if (SubstanceCoreUtilities.hasFlatAppearance(this .splitPane,
141: true)) {
142: SubstanceFillBackgroundDelegate.GLOBAL_INSTANCE
143: .updateIfOpaque(g, this .splitPane);
144: }
145:
146: Graphics2D graphics = (Graphics2D) g.create();
147:
148: ComponentState currState = ComponentState.getState(
149: this .gripModel, this .splitPane);
150: ComponentState prevState = SubstanceCoreUtilities
151: .getPrevComponentState(this .splitPane);
152: FadeState fadeState = SubstanceFadeUtilities.getFadeState(this ,
153: FadeKind.ROLLOVER, FadeKind.SELECTION, FadeKind.PRESS);
154: float cyclePos = 10.0f;
155: if (fadeState != null) {
156: cyclePos = fadeState.getFadePosition();
157: if (fadeState.isFadingIn())
158: cyclePos = 10.0f - cyclePos;
159: }
160:
161: // compute the grip handle dimension
162: int minSizeForGripPresence = SubstanceSizeUtils
163: .getAdjustedSize(SubstanceSizeUtils
164: .getComponentFontSize(this ), 30, 1, 2, false);
165: int maxGripSize = SubstanceSizeUtils.getAdjustedSize(
166: SubstanceSizeUtils.getComponentFontSize(this ), 40, 1,
167: 3, false);
168: if (this .splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
169: int thumbHeight = this .getHeight();
170: if (thumbHeight >= minSizeForGripPresence) {
171: int gripHeight = thumbHeight / 4;
172: if (gripHeight > maxGripSize)
173: gripHeight = maxGripSize;
174:
175: int thumbWidth = this .getWidth();
176:
177: int gripX = 0;
178: int gripY = (thumbHeight - gripHeight) / 2;
179:
180: // draw the grip bumps
181: graphics.drawImage(SubstanceImageCreator
182: .getSplitDividerBumpImage(this , thumbWidth,
183: gripHeight, false, currState,
184: SubstanceThemeUtilities.getTheme(this ,
185: currState),
186: SubstanceThemeUtilities.getTheme(this ,
187: prevState), cyclePos), gripX,
188: gripY, null);
189: }
190: } else {
191: int thumbWidth = this .getWidth();
192: if (thumbWidth >= minSizeForGripPresence) {
193: int gripWidth = thumbWidth / 4;
194: if (gripWidth > maxGripSize)
195: gripWidth = maxGripSize;
196:
197: int thumbHeight = this .getHeight();
198: // int gripHeight = thumbHeight * 2 / 3;
199:
200: int gripX = (thumbWidth - gripWidth) / 2;
201: int gripY = 1;
202: // draw the grip bumps
203: graphics.drawImage(SubstanceImageCreator
204: .getSplitDividerBumpImage(this , gripWidth,
205: thumbHeight, true, currState,
206: SubstanceThemeUtilities.getTheme(this ,
207: currState),
208: SubstanceThemeUtilities.getTheme(this ,
209: prevState), cyclePos), gripX,
210: gripY, null);
211: }
212: }
213:
214: graphics.dispose();
215:
216: super .paint(g);
217: }
218:
219: /*
220: * (non-Javadoc)
221: *
222: * @see javax.swing.plaf.basic.BasicSplitPaneDivider#createLeftOneTouchButton()
223: */
224: @Override
225: protected JButton createLeftOneTouchButton() {
226: JButton oneTouchButton = new JButton() {
227: // Don't want the button to participate in focus traversable.
228: @Override
229: public boolean isFocusable() {
230: return false;
231: }
232: };
233: Icon verticalSplit = new TransitionAwareIcon(oneTouchButton,
234: new TransitionAwareIcon.Delegate() {
235: public Icon getThemeIcon(SubstanceTheme theme) {
236: return SubstanceImageCreator
237: .getArrowIcon(
238: SubstanceSizeUtils
239: .getComponentFontSize(splitPane),
240: -2, -1, 0,
241: SwingConstants.NORTH, theme);
242: }
243: });
244: Icon horizontalSplit = new TransitionAwareIcon(oneTouchButton,
245: new TransitionAwareIcon.Delegate() {
246: public Icon getThemeIcon(SubstanceTheme theme) {
247: return SubstanceImageCreator
248: .getArrowIcon(
249: SubstanceSizeUtils
250: .getComponentFontSize(splitPane),
251: -2, -1, 0, SwingConstants.WEST,
252: theme);
253: }
254: });
255: oneTouchButton
256: .setIcon(this .splitPane.getOrientation() == JSplitPane.VERTICAL_SPLIT ? verticalSplit
257: : horizontalSplit);
258:
259: oneTouchButton.putClientProperty(
260: SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY,
261: Boolean.TRUE);
262: // fix for issue 281 - set empty border so that the arrow
263: // icon is not cropped
264: oneTouchButton.setBorder(new EmptyBorder(0, 0, 0, 0));
265:
266: oneTouchButton.setRequestFocusEnabled(false);
267: oneTouchButton.setCursor(Cursor
268: .getPredefinedCursor(Cursor.HAND_CURSOR));
269: oneTouchButton.setFocusPainted(false);
270: oneTouchButton.setBorderPainted(false);
271: return oneTouchButton;
272: }
273:
274: /*
275: * (non-Javadoc)
276: *
277: * @see javax.swing.plaf.basic.BasicSplitPaneDivider#createRightOneTouchButton()
278: */
279: @Override
280: protected JButton createRightOneTouchButton() {
281: JButton oneTouchButton = new JButton() {
282: // Don't want the button to participate in focus traversable.
283: @Override
284: public boolean isFocusable() {
285: return false;
286: }
287: };
288: Icon verticalSplit = new TransitionAwareIcon(oneTouchButton,
289: new TransitionAwareIcon.Delegate() {
290: public Icon getThemeIcon(SubstanceTheme theme) {
291: return SubstanceImageCreator
292: .getArrowIcon(
293: SubstanceSizeUtils
294: .getComponentFontSize(splitPane),
295: -2, -1, 0,
296: SwingConstants.SOUTH, theme);
297: }
298: });
299: Icon horizontalSplit = new TransitionAwareIcon(oneTouchButton,
300: new TransitionAwareIcon.Delegate() {
301: public Icon getThemeIcon(SubstanceTheme theme) {
302: return SubstanceImageCreator
303: .getArrowIcon(
304: SubstanceSizeUtils
305: .getComponentFontSize(splitPane),
306: -2, -1, 0, SwingConstants.EAST,
307: theme);
308: }
309: });
310: oneTouchButton
311: .setIcon(this .splitPane.getOrientation() == JSplitPane.VERTICAL_SPLIT ? verticalSplit
312: : horizontalSplit);
313:
314: oneTouchButton.putClientProperty(
315: SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY,
316: Boolean.TRUE);
317: // fix for issue 281 - set empty border so that the arrow
318: // icon is not cropped
319: oneTouchButton.setBorder(new EmptyBorder(0, 0, 0, 0));
320:
321: oneTouchButton.setCursor(Cursor
322: .getPredefinedCursor(Cursor.HAND_CURSOR));
323: oneTouchButton.setFocusPainted(false);
324: oneTouchButton.setBorderPainted(false);
325: oneTouchButton.setRequestFocusEnabled(false);
326: // b.setOpaque(false);
327: return oneTouchButton;
328: }
329:
330: /**
331: * Updates the one-touch buttons.
332: *
333: * @param orientation
334: * Split pane orientation.
335: */
336: public void updateOneTouchButtons(int orientation) {
337: if (orientation == JSplitPane.VERTICAL_SPLIT) {
338: if (this .leftButton != null) {
339: this .leftButton.setIcon(new TransitionAwareIcon(
340: this .leftButton,
341: new TransitionAwareIcon.Delegate() {
342: public Icon getThemeIcon(
343: SubstanceTheme theme) {
344: return SubstanceImageCreator
345: .getArrowIcon(
346: SubstanceSizeUtils
347: .getComponentFontSize(splitPane),
348: -2, -1, 0,
349: SwingConstants.NORTH,
350: theme);
351: }
352: }));
353: }
354: if (this .rightButton != null) {
355: this .rightButton.setIcon(new TransitionAwareIcon(
356: this .rightButton,
357: new TransitionAwareIcon.Delegate() {
358: public Icon getThemeIcon(
359: SubstanceTheme theme) {
360: return SubstanceImageCreator
361: .getArrowIcon(
362: SubstanceSizeUtils
363: .getComponentFontSize(splitPane),
364: -2, -1, 0,
365: SwingConstants.SOUTH,
366: theme);
367: }
368: }));
369: }
370: } else {
371: if (this .leftButton != null) {
372: this .leftButton.setIcon(new TransitionAwareIcon(
373: this .leftButton,
374: new TransitionAwareIcon.Delegate() {
375: public Icon getThemeIcon(
376: SubstanceTheme theme) {
377: return SubstanceImageCreator
378: .getArrowIcon(
379: SubstanceSizeUtils
380: .getComponentFontSize(splitPane),
381: -2, -1, 0,
382: SwingConstants.WEST,
383: theme);
384: }
385: }));
386: }
387: if (this .rightButton != null) {
388: this .rightButton.setIcon(new TransitionAwareIcon(
389: this .rightButton,
390: new TransitionAwareIcon.Delegate() {
391: public Icon getThemeIcon(
392: SubstanceTheme theme) {
393: return SubstanceImageCreator
394: .getArrowIcon(
395: SubstanceSizeUtils
396: .getComponentFontSize(splitPane),
397: -2, -1, 0,
398: SwingConstants.EAST,
399: theme);
400: }
401: }));
402: }
403: }
404: }
405:
406: /*
407: * (non-Javadoc)
408: *
409: * @see org.jvnet.substance.utils.Trackable#isInside(java.awt.event.MouseEvent)
410: */
411: public boolean isInside(MouseEvent me) {
412: // entire area is sensitive
413: return true;
414: }
415: }
|