build.properties0000644000175000017500000000032411330361560013034 0ustar tonytonyjdk.home=/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home javac.source=1.6 javac.target=1.6 javac.encoding=ISO-8859-1 javac.debug=on javac.generate.no.warnings=off javac.args= javac.max.memory=128mbuild.xml0000644000175000017500000002200211430023550011430 0ustar tonytony src/0000755000175000017500000000000011415741022010406 5ustar tonytonysrc/org/0000755000175000017500000000000011427105010011167 5ustar tonytonysrc/org/pushingpixels/0000755000175000017500000000000011427105010014071 5ustar tonytonysrc/org/pushingpixels/flamingo/0000755000175000017500000000000011427105010015665 5ustar tonytonysrc/org/pushingpixels/flamingo/internal/0000755000175000017500000000000011401230444017503 5ustar tonytonysrc/org/pushingpixels/flamingo/internal/ui/0000755000175000017500000000000011401230444020120 5ustar tonytonysrc/org/pushingpixels/flamingo/internal/ui/common/0000755000175000017500000000000011407771362021427 5ustar tonytonysrc/org/pushingpixels/flamingo/internal/ui/common/BasicCommandToggleMenuButtonUI.java0000644000175000017500000000761511401230444030225 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.awt.geom.GeneralPath; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.JCommandToggleMenuButton; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Basic UI delegate for the {@link JCommandToggleMenuButton} component. * * @author Kirill Grouchnikov */ public class BasicCommandToggleMenuButtonUI extends BasicCommandToggleButtonUI { public static ComponentUI createUI(JComponent c) { return new BasicCommandToggleMenuButtonUI(); } @Override protected void paintButtonIcon(Graphics g, Rectangle iconRect) { boolean isSelected = this.commandButton.getActionModel().isSelected(); if (isSelected) { Color selectionColor = FlamingoUtilities.getColor(Color.blue .darker(), "Table.selectionBackground", "textHighlight"); Rectangle extended = new Rectangle(iconRect.x - 1, iconRect.y - 1, iconRect.width + 1, iconRect.height + 1); g.setColor(selectionColor); g.fillRect(extended.x, extended.y, extended.width, extended.height); g.setColor(selectionColor.darker()); g.drawRect(extended.x, extended.y, extended.width, extended.height); } super.paintButtonIcon(g, iconRect); // does it actually have an icon? Icon iconToPaint = this.getIconToPaint(); if (isSelected && (iconToPaint == null)) { // draw a checkmark Graphics2D g2d = (Graphics2D) g.create(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(getForegroundColor(this.commandButton.getActionModel() .isEnabled())); int iw = iconRect.width; int ih = iconRect.height; GeneralPath path = new GeneralPath(); path.moveTo(0.2f * iw, 0.5f * ih); path.lineTo(0.42f * iw, 0.8f * ih); path.lineTo(0.8f * iw, 0.2f * ih); g2d.translate(iconRect.x, iconRect.y); Stroke stroke = new BasicStroke((float) 0.1 * iw, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); g2d.setStroke(stroke); g2d.draw(path); g2d.dispose(); } } @Override protected boolean isPaintingBackground() { boolean isActionRollover = this.commandButton.getActionModel() .isRollover(); return (isActionRollover || !this.commandButton.isFlat()); } } src/org/pushingpixels/flamingo/internal/ui/common/RichTooltipPanelUI.java0000644000175000017500000000354611401230444025741 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import javax.swing.plaf.PanelUI; /** * UI for rich tooltip panel ({@link JRichTooltipPanel}). * * @author Kirill Grouchnikov */ public abstract class RichTooltipPanelUI extends PanelUI { } src/org/pushingpixels/flamingo/internal/ui/common/CommandButtonLayoutManagerSmall.java0000644000175000017500000002637611401230444030525 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.beans.PropertyChangeEvent; import javax.swing.JSeparator; import javax.swing.SwingConstants; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerSmall implements CommandButtonLayoutManager { @Override public int getPreferredIconSize() { return 16; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = commandButton.getInsets(); // int bx = borderInsets.left + borderInsets.right; int by = borderInsets.top + borderInsets.bottom; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); boolean hasIcon = (commandButton.getIcon() != null); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); int prefIconSize = hasIcon ? this.getPreferredIconSize() : 0; // start with the left insets int width = borderInsets.left; // icon? if (hasIcon) { // padding before the icon width += layoutHGap; // icon width width += prefIconSize; // padding after the icon width += layoutHGap; } // popup icon? if (hasPopupIcon) { // padding before the popup icon width += 2 * layoutHGap; // text width width += 1 + fm.getHeight() / 2; // padding after the popup icon width += 2 * layoutHGap; } if (commandButton instanceof JCommandButton) { JCommandButton jcb = (JCommandButton) commandButton; CommandButtonKind buttonKind = jcb.getCommandButtonKind(); if (hasIcon && buttonKind.hasAction() && buttonKind.hasPopup()) { // space for a vertical separator width += new JSeparator(JSeparator.VERTICAL).getPreferredSize().width; } } // right insets width += borderInsets.right; // and remove the padding before the first and after the last elements width -= 2 * layoutHGap; return new Dimension(width, by + Math.max(prefIconSize, fm.getAscent() + fm.getDescent())); } @Override public void propertyChange(PropertyChangeEvent evt) { } @Override public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) { Insets ins = commandButton.getInsets(); int height = commandButton.getHeight(); ResizableIcon buttonIcon = commandButton.getIcon(); if (buttonIcon != null) { // bottom-right corner of the icon area return new Point(ins.left + buttonIcon.getIconWidth(), (height + buttonIcon.getIconHeight()) / 2); } else { // bottom-left corner of the button return new Point(ins.left, 3 * height / 4); } } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, 0, 0); Insets ins = commandButton.getInsets(); result.iconRect = new Rectangle(); result.popupActionRect = new Rectangle(); int width = commandButton.getWidth(); int height = commandButton.getHeight(); int prefWidth = this.getPreferredSize(commandButton).width; int shiftX = 0; if (commandButton.getHorizontalAlignment() == SwingConstants.CENTER) { if (width > prefWidth) { shiftX = (width - prefWidth) / 2; } } ResizableIcon buttonIcon = commandButton.getIcon(); boolean hasIcon = (buttonIcon != null); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); boolean ltr = commandButton.getComponentOrientation().isLeftToRight(); FontMetrics fm = g.getFontMetrics(); int labelHeight = fm.getAscent() + fm.getDescent(); JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton) .getCommandButtonKind() : JCommandButton.CommandButtonKind.ACTION_ONLY; int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); if (ltr) { int x = ins.left + shiftX - layoutHGap; // icon if (hasIcon) { x += layoutHGap; int iconHeight = buttonIcon.getIconHeight(); int iconWidth = buttonIcon.getIconWidth(); result.iconRect.x = x; result.iconRect.y = (height - iconHeight) / 2; result.iconRect.width = iconWidth; result.iconRect.height = iconHeight; x += (iconWidth + layoutHGap); } if (hasPopupIcon) { x += 2 * layoutHGap; result.popupActionRect.x = x; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.height = labelHeight + 2; x += result.popupActionRect.width; x += 2 * layoutHGap; } int xBorderBetweenActionAndPopup = 0; int verticalSeparatorWidth = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; // compute the action and popup click areas switch (buttonKind) { case ACTION_ONLY: result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; break; case POPUP_ONLY: result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; break; case ACTION_AND_POPUP_MAIN_ACTION: case ACTION_AND_POPUP_MAIN_POPUP: // 1. break after icon if button has icon // 2. no break (all popup) if button has no icon if (hasIcon) { // shift popup action rectangle to the right // to accomodate the vertical separator result.popupActionRect.x += verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.iconRect.x + result.iconRect.width + layoutHGap; result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = xBorderBetweenActionAndPopup; result.popupClickArea.y = 0; result.popupClickArea.width = width - xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = true; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = true; } break; } } else { int x = width - ins.right - shiftX + layoutHGap; // icon if (hasIcon) { x -= layoutHGap; int iconHeight = buttonIcon.getIconHeight(); int iconWidth = buttonIcon.getIconWidth(); result.iconRect.x = x - iconWidth; result.iconRect.y = (height - iconHeight) / 2; result.iconRect.width = iconWidth; result.iconRect.height = iconHeight; x -= (iconWidth + layoutHGap); } if (hasPopupIcon) { x -= 2 * layoutHGap; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.x = x - result.popupActionRect.width; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.height = labelHeight + 2; x -= result.popupActionRect.width; x -= 2 * layoutHGap; } int xBorderBetweenActionAndPopup = 0; int verticalSeparatorWidth = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; // compute the action and popup click areas switch (buttonKind) { case ACTION_ONLY: result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; break; case POPUP_ONLY: result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; break; case ACTION_AND_POPUP_MAIN_ACTION: case ACTION_AND_POPUP_MAIN_POPUP: // 1. break after icon if button has icon // 2. no break (all popup) if button has no icon if (hasIcon) { // shift popup action rectangle to the left // to accomodate the vertical separator result.popupActionRect.x -= verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.iconRect.x - layoutHGap; result.actionClickArea.x = xBorderBetweenActionAndPopup; result.actionClickArea.y = 0; result.actionClickArea.width = width - xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = true; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = true; } break; } } return result; } } src/org/pushingpixels/flamingo/internal/ui/common/CommandButtonLayoutManagerTile.java0000644000175000017500000004473411401230444030350 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import javax.swing.JSeparator; import javax.swing.SwingConstants; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerTile implements CommandButtonLayoutManager { @Override public int getPreferredIconSize() { return 32; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = commandButton.getInsets(); int by = borderInsets.top + borderInsets.bottom; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); String buttonText = commandButton.getText(); int titleWidth = (buttonText == null) ? 0 : fm .stringWidth(commandButton.getText()); String extraText = commandButton.getExtraText(); int extraWidth = (extraText == null) ? 0 : fm.stringWidth(extraText); double textWidth = Math.max(titleWidth, extraWidth); int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); boolean hasIcon = (commandButton.getIcon() != null); boolean hasText = (textWidth > 0); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); int prefIconSize = hasIcon ? this.getPreferredIconSize() : 0; // start with the left insets int width = borderInsets.left; // icon? if (hasIcon) { // padding before the icon width += layoutHGap; // icon width width += prefIconSize; // padding after the icon width += layoutHGap; } // text? if (hasText) { // padding before the text width += layoutHGap; // text width width += textWidth; // padding after the text width += layoutHGap; } // popup icon? if (hasPopupIcon) { // padding before the popup icon width += 2 * layoutHGap; // text width width += 1 + fm.getHeight() / 2; // padding after the popup icon width += 2 * layoutHGap; } if (commandButton instanceof JCommandButton) { JCommandButton jcb = (JCommandButton) commandButton; CommandButtonKind buttonKind = jcb.getCommandButtonKind(); boolean hasSeparator = false; if (buttonKind == CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION && (hasIcon || hasText)) { hasSeparator = true; } if (buttonKind == CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP && hasIcon) { hasSeparator = true; } if (hasSeparator) { // space for a vertical separator width += new JSeparator(JSeparator.VERTICAL).getPreferredSize().width; } } // right insets width += borderInsets.right; // and remove the padding before the first and after the last elements width -= 2 * layoutHGap; return new Dimension(width, by + Math .max(prefIconSize, 2 * (fm.getAscent() + fm .getDescent()))); } @Override public void propertyChange(PropertyChangeEvent evt) { } @Override public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) { Insets ins = commandButton.getInsets(); int height = commandButton.getHeight(); ResizableIcon buttonIcon = commandButton.getIcon(); if (buttonIcon != null) { // bottom-right corner of the icon area return new Point(ins.left + buttonIcon.getIconWidth(), (height + buttonIcon.getIconHeight()) / 2); } else { // bottom-left corner of the button return new Point(ins.left, 3 * height / 4); } } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, 0, 0); Insets ins = commandButton.getInsets(); result.iconRect = new Rectangle(); result.popupActionRect = new Rectangle(); int width = commandButton.getWidth(); int height = commandButton.getHeight(); int prefWidth = this.getPreferredSize(commandButton).width; int shiftX = 0; if (commandButton.getHorizontalAlignment() == SwingConstants.CENTER) { if (width > prefWidth) { shiftX = (width - prefWidth) / 2; } } ResizableIcon buttonIcon = commandButton.getIcon(); String buttonText = commandButton.getText(); String buttonExtraText = commandButton.getExtraText(); boolean hasIcon = (buttonIcon != null); boolean hasText = (buttonText != null) || (buttonExtraText != null); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); boolean ltr = commandButton.getComponentOrientation().isLeftToRight(); FontMetrics fm = g.getFontMetrics(); int labelHeight = fm.getAscent() + fm.getDescent(); JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton) .getCommandButtonKind() : JCommandButton.CommandButtonKind.ACTION_ONLY; int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); if (ltr) { int x = ins.left + shiftX - layoutHGap; // icon if (hasIcon) { x += layoutHGap; int iconHeight = buttonIcon.getIconHeight(); int iconWidth = buttonIcon.getIconWidth(); result.iconRect.x = x; result.iconRect.y = (height - iconHeight) / 2; result.iconRect.width = iconWidth; result.iconRect.height = iconHeight; x += (iconWidth + layoutHGap); } // text if (hasText) { x += layoutHGap; TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); lineLayoutInfo.textRect.x = x; lineLayoutInfo.textRect.y = (height - 2 * labelHeight) / 2; lineLayoutInfo.textRect.width = (buttonText == null) ? 0 : (int) fm.getStringBounds(buttonText, g).getWidth(); lineLayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); String extraText = commandButton.getExtraText(); TextLayoutInfo extraLineLayoutInfo = new TextLayoutInfo(); extraLineLayoutInfo.text = extraText; extraLineLayoutInfo.textRect = new Rectangle(); extraLineLayoutInfo.textRect.x = x; extraLineLayoutInfo.textRect.y = lineLayoutInfo.textRect.y + labelHeight; extraLineLayoutInfo.textRect.width = (extraText == null) ? 0 : (int) fm.getStringBounds(extraText, g).getWidth(); extraLineLayoutInfo.textRect.height = labelHeight; result.extraTextLayoutInfoList = new ArrayList(); result.extraTextLayoutInfoList.add(extraLineLayoutInfo); x += Math.max(lineLayoutInfo.textRect.width, extraLineLayoutInfo.textRect.width); x += layoutHGap; } if (hasPopupIcon) { x += 2 * layoutHGap; result.popupActionRect.x = x; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.height = labelHeight + 2; x += result.popupActionRect.width; x += 2 * layoutHGap; } int xBorderBetweenActionAndPopup = 0; int verticalSeparatorWidth = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; // compute the action and popup click areas switch (buttonKind) { case ACTION_ONLY: result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; break; case POPUP_ONLY: result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; break; case ACTION_AND_POPUP_MAIN_ACTION: // 1. break before popup icon if button has text or icon // 2. no break (all popup) if button has no text and no icon if (hasText || hasIcon) { // shift popup action rectangle to the right to // accomodate the vertical separator result.popupActionRect.x += verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.popupActionRect.x - 2 * layoutHGap; result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = xBorderBetweenActionAndPopup; result.popupClickArea.y = 0; result.popupClickArea.width = width - xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = true; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = true; } break; case ACTION_AND_POPUP_MAIN_POPUP: // 1. break after icon if button has icon // 2. no break (all popup) if button has no icon if (hasIcon) { // shift text rectangles and popup action rectangle to the // right // to accomodate the vertical separator if (result.textLayoutInfoList != null) { for (TextLayoutInfo textLayoutInfo : result.textLayoutInfoList) { textLayoutInfo.textRect.x += verticalSeparatorWidth; } } if (result.extraTextLayoutInfoList != null) { for (TextLayoutInfo extraTextLayoutInfo : result.extraTextLayoutInfoList) { extraTextLayoutInfo.textRect.x += verticalSeparatorWidth; } } result.popupActionRect.x += verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.iconRect.x + result.iconRect.width + layoutHGap; result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = xBorderBetweenActionAndPopup; result.popupClickArea.y = 0; result.popupClickArea.width = width - xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = true; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = true; } break; } } else { int x = width - ins.right - shiftX + layoutHGap; // icon if (hasIcon) { x -= layoutHGap; int iconHeight = buttonIcon.getIconHeight(); int iconWidth = buttonIcon.getIconWidth(); result.iconRect.x = x - iconWidth; result.iconRect.y = (height - iconHeight) / 2; result.iconRect.width = iconWidth; result.iconRect.height = iconHeight; x -= (iconWidth + layoutHGap); } // text if (hasText) { x -= layoutHGap; TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); lineLayoutInfo.textRect.width = (buttonText == null) ? 0 : (int) fm.getStringBounds(buttonText, g).getWidth(); lineLayoutInfo.textRect.x = x - lineLayoutInfo.textRect.width; lineLayoutInfo.textRect.y = (height - 2 * labelHeight) / 2; lineLayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); String extraText = commandButton.getExtraText(); TextLayoutInfo extraLineLayoutInfo = new TextLayoutInfo(); extraLineLayoutInfo.text = extraText; extraLineLayoutInfo.textRect = new Rectangle(); extraLineLayoutInfo.textRect.width = (extraText == null) ? 0 : (int) fm.getStringBounds(extraText, g).getWidth(); extraLineLayoutInfo.textRect.x = x - extraLineLayoutInfo.textRect.width; extraLineLayoutInfo.textRect.y = lineLayoutInfo.textRect.y + labelHeight; extraLineLayoutInfo.textRect.height = labelHeight; result.extraTextLayoutInfoList = new ArrayList(); result.extraTextLayoutInfoList.add(extraLineLayoutInfo); x -= Math.max(lineLayoutInfo.textRect.width, extraLineLayoutInfo.textRect.width); x -= layoutHGap; } if (hasPopupIcon) { x -= 2 * layoutHGap; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.x = x - result.popupActionRect.width; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.height = labelHeight + 2; x -= result.popupActionRect.width; x -= 2 * layoutHGap; } int xBorderBetweenActionAndPopup = 0; int verticalSeparatorWidth = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; // compute the action and popup click areas switch (buttonKind) { case ACTION_ONLY: result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; break; case POPUP_ONLY: result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; break; case ACTION_AND_POPUP_MAIN_ACTION: // 1. break before popup icon if button has text or icon // 2. no break (all popup) if button has no text and no icon if (hasText || hasIcon) { // shift popup action rectangle to the left to // accomodate the vertical separator result.popupActionRect.x -= verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.popupActionRect.x + result.popupActionRect.width + 2 * layoutHGap; result.actionClickArea.x = xBorderBetweenActionAndPopup; result.actionClickArea.y = 0; result.actionClickArea.width = width - xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = true; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = true; } break; case ACTION_AND_POPUP_MAIN_POPUP: // 1. break after icon if button has icon // 2. no break (all popup) if button has no icon if (hasIcon) { // shift text rectangles and popup action rectangle to the // left to accomodate the vertical separator if (result.textLayoutInfoList != null) { for (TextLayoutInfo textLayoutInfo : result.textLayoutInfoList) { textLayoutInfo.textRect.x -= verticalSeparatorWidth; } } if (result.extraTextLayoutInfoList != null) { for (TextLayoutInfo extraTextLayoutInfo : result.extraTextLayoutInfoList) { extraTextLayoutInfo.textRect.x -= verticalSeparatorWidth; } } result.popupActionRect.x -= verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.iconRect.x - layoutHGap; result.actionClickArea.x = xBorderBetweenActionAndPopup; result.actionClickArea.y = 0; result.actionClickArea.width = width - xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = true; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = true; } break; } } return result; } } src/org/pushingpixels/flamingo/internal/ui/common/CommandButtonLayoutManagerCustom.java0000644000175000017500000001062711401230444030717 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import javax.swing.JSeparator; import org.pushingpixels.flamingo.api.common.AbstractCommandButton; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerCustom extends CommandButtonLayoutManagerBig { public CommandButtonLayoutManagerCustom(AbstractCommandButton commandButton) { super(commandButton); } @Override public int getPreferredIconSize() { return -1; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = commandButton.getInsets(); int bx = borderInsets.left + borderInsets.right; int by = borderInsets.top + borderInsets.bottom; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); JSeparator jsep = new JSeparator(JSeparator.HORIZONTAL); int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); int layoutVGap = FlamingoUtilities.getVLayoutGap(commandButton); int title1Width = (this.titlePart1 == null) ? 0 : fm .stringWidth(this.titlePart1); int title2Width = (this.titlePart2 == null) ? 0 : fm .stringWidth(this.titlePart2); ResizableIcon icon = commandButton.getIcon(); int iconWidth = (icon == null) ? 0 : icon.getIconWidth(); int width = Math.max(iconWidth, Math.max(title1Width, title2Width + 4 * layoutHGap + jsep.getPreferredSize().width + (FlamingoUtilities.hasPopupAction(commandButton) ? 1 + fm .getHeight() / 2 : 0))); boolean hasIcon = (commandButton.getIcon() != null); boolean hasText = (this.titlePart1 != null); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); // start height with the top inset int height = borderInsets.top; // icon? if (hasIcon) { // padding above the icon height += layoutVGap; // icon height height += icon.getIconHeight(); // padding below the icon height += layoutVGap; } // text? if (hasText) { // padding above the text height += layoutVGap; // text height - two lines height += 2 * (fm.getAscent() + fm.getDescent()); // padding below the text height += layoutVGap; } // popup icon (no text)? if (!hasText && hasPopupIcon) { // padding above the popup icon height += layoutVGap; // popup icon height - one line of text height += fm.getHeight(); // padding below the popup icon height += layoutVGap; } if (hasPopupIcon) { // space for a horizontal separator height += new JSeparator(JSeparator.HORIZONTAL).getPreferredSize().height; } // bottom insets height += borderInsets.bottom; // and remove the padding above the first and below the last elements height -= 2 * layoutVGap; return new Dimension(bx + width, height); } } src/org/pushingpixels/flamingo/internal/ui/common/popup/0000755000175000017500000000000011401230444022553 5ustar tonytonysrc/org/pushingpixels/flamingo/internal/ui/common/popup/PopupPanelUI.java0000644000175000017500000000363211401230444025743 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import javax.swing.plaf.PanelUI; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; /** * UI for popup panel ({@link JPopupPanel}). * * @author Kirill Grouchnikov */ public abstract class PopupPanelUI extends PanelUI { } src/org/pushingpixels/flamingo/internal/ui/common/popup/ColorSelectorPanelUI.java0000644000175000017500000000356411401230444027423 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import javax.swing.plaf.ComponentUI; /** * UI for command button ({@link JColorSelectorPanel}). * * @author Kirill Grouchnikov */ public abstract class ColorSelectorPanelUI extends ComponentUI { } src/org/pushingpixels/flamingo/internal/ui/common/popup/BasicPopupPanelUI.java0000644000175000017500000004671411401230444026715 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import java.applet.Applet; import java.awt.*; import java.awt.event.*; import java.util.List; import javax.swing.*; import javax.swing.border.*; import javax.swing.plaf.*; import javax.swing.plaf.basic.ComboPopup; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager.PopupEvent; import org.pushingpixels.flamingo.api.ribbon.JRibbon; import org.pushingpixels.flamingo.internal.ui.ribbon.JRibbonTaskToggleButton; import org.pushingpixels.flamingo.internal.ui.ribbon.appmenu.JRibbonApplicationMenuPopupPanel; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; import org.pushingpixels.flamingo.internal.utils.KeyTipManager; /** * Basic UI for popup panel {@link JPopupPanel}. * * @author Kirill Grouchnikov */ public class BasicPopupPanelUI extends PopupPanelUI { /** * The associated popup panel. */ protected JPopupPanel popupPanel; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicPopupPanelUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.popupPanel = (JPopupPanel) c; super.installUI(this.popupPanel); installDefaults(); installComponents(); installListeners(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); super.uninstallUI(this.popupPanel); } /** * Installs default settings for the associated command popup menu. */ protected void installDefaults() { Color bg = this.popupPanel.getBackground(); if (bg == null || bg instanceof UIResource) { this.popupPanel.setBackground(FlamingoUtilities.getColor( Color.lightGray, "PopupPanel.background", "Panel.background")); } Border b = this.popupPanel.getBorder(); if (b == null || b instanceof UIResource) { Border toSet = UIManager.getBorder("PopupPanel.border"); if (toSet == null) toSet = new BorderUIResource.CompoundBorderUIResource( new LineBorder(FlamingoUtilities.getBorderColor()), new EmptyBorder(1, 1, 1, 1)); this.popupPanel.setBorder(toSet); } LookAndFeel.installProperty(this.popupPanel, "opaque", Boolean.TRUE); } /** * Installs listeners on the associated command popup menu. */ protected void installListeners() { initiliazeGlobalListeners(); } /** * Installs components on the associated command popup menu. */ protected void installComponents() { } /** * Uninstalls default settings from the associated command popup menu. */ protected void uninstallDefaults() { LookAndFeel.uninstallBorder(this.popupPanel); } /** * Uninstalls listeners from the associated command popup menu. */ protected void uninstallListeners() { } /** * Uninstalls subcomponents from the associated command popup menu. */ protected void uninstallComponents() { } /** * The global listener that tracks the ESC key action on the root panes of * windows that show popup panels. */ static PopupPanelManager.PopupListener popupPanelManagerListener; /** * Initializes the global listeners. */ protected static synchronized void initiliazeGlobalListeners() { if (popupPanelManagerListener != null) { return; } popupPanelManagerListener = new PopupPanelEscapeDismisser(); PopupPanelManager.defaultManager().addPopupListener( popupPanelManagerListener); new WindowTracker(); } /** * This class is used to trace the changes in the shown popup panels and * install ESC key listener on the matching root pane so that the popup * panels can be dismissed with the ESC key. * * @author Kirill Grouchnikov */ protected static class PopupPanelEscapeDismisser implements PopupPanelManager.PopupListener { /** * The currently installed action map on the {@link #tracedRootPane}. */ private ActionMap newActionMap; /** * The currently installed input map on the {@link #tracedRootPane}. */ private InputMap newInputMap; /** * The last shown popup panel sequence. */ List lastPathSelected; /** * Currently traced root pane. It is the root pane of the originating * component of the first popup panel in the currently shown sequence of * {@link PopupPanelManager}. */ private JRootPane tracedRootPane; /** * Creates a new tracer for popup panels to be dismissed with ESC key. */ public PopupPanelEscapeDismisser() { PopupPanelManager popupPanelManager = PopupPanelManager .defaultManager(); this.lastPathSelected = popupPanelManager.getShownPath(); if (this.lastPathSelected.size() != 0) { traceRootPane(this.lastPathSelected); } } @Override public void popupHidden(PopupEvent event) { PopupPanelManager msm = PopupPanelManager.defaultManager(); List p = msm.getShownPath(); if (lastPathSelected.size() != 0 && p.size() == 0) { // if it is the last popup panel to be dismissed, untrace the // root pane untraceRootPane(); } lastPathSelected = p; } /** * Removes the installed maps on the currently traced root pane. */ private void untraceRootPane() { if (this.tracedRootPane != null) { removeUIActionMap(this.tracedRootPane, this.newActionMap); removeUIInputMap(this.tracedRootPane, this.newInputMap); } } @Override public void popupShown(PopupEvent event) { PopupPanelManager msm = PopupPanelManager.defaultManager(); List p = msm.getShownPath(); if (lastPathSelected.size() == 0 && p.size() != 0) { // if it is the first popup panel to be shown, trace the root // panel traceRootPane(p); } lastPathSelected = p; } /** * Installs the maps on the root pane of the originating component of * the first popup panel of the specified sequence to trace the ESC key * and dismiss the shown popup panels. * * @param shownPath * Popup panel sequence. */ private void traceRootPane(List shownPath) { JComponent originator = shownPath.get(0).getPopupOriginator(); this.tracedRootPane = SwingUtilities.getRootPane(originator); if (this.tracedRootPane != null) { newInputMap = new ComponentInputMapUIResource(tracedRootPane); newInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "hidePopupPanel"); newActionMap = new ActionMapUIResource(); newActionMap.put("hidePopupPanel", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { // Hide the last sequence popup for every ESC keystroke. // There is special case - if the keytips are shown // for the *second* panel of the app menu popup panel, // do not dismiss the popup List popups = PopupPanelManager .defaultManager().getShownPath(); if (popups.size() > 0) { PopupPanelManager.PopupInfo lastPopup = popups .get(popups.size() - 1); if (lastPopup.getPopupPanel() instanceof JRibbonApplicationMenuPopupPanel) { JRibbonApplicationMenuPopupPanel appMenuPopupPanel = (JRibbonApplicationMenuPopupPanel) lastPopup .getPopupPanel(); KeyTipManager.KeyTipChain currentlyShownKeyTipChain = KeyTipManager .defaultManager() .getCurrentlyShownKeyTipChain(); if ((currentlyShownKeyTipChain != null) && (currentlyShownKeyTipChain.chainParentComponent == appMenuPopupPanel .getPanelLevel2())) return; } } PopupPanelManager.defaultManager().hideLastPopup(); } }); addUIInputMap(tracedRootPane, newInputMap); addUIActionMap(tracedRootPane, newActionMap); } } /** * Adds the specified input map to the specified component. * * @param c * Component. * @param map * Input map to add. */ void addUIInputMap(JComponent c, InputMap map) { InputMap lastNonUI = null; InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); while (parent != null && !(parent instanceof UIResource)) { lastNonUI = parent; parent = parent.getParent(); } if (lastNonUI == null) { c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map); } else { lastNonUI.setParent(map); } map.setParent(parent); } /** * Adds the specified action map to the specified component. * * @param c * Component. * @param map * Action map to add. */ void addUIActionMap(JComponent c, ActionMap map) { ActionMap lastNonUI = null; ActionMap parent = c.getActionMap(); while (parent != null && !(parent instanceof UIResource)) { lastNonUI = parent; parent = parent.getParent(); } if (lastNonUI == null) { c.setActionMap(map); } else { lastNonUI.setParent(map); } map.setParent(parent); } /** * Removes the specified input map from the specified component. * * @param c * Component. * @param map * Input map to remove. */ void removeUIInputMap(JComponent c, InputMap map) { InputMap im = null; InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); while (parent != null) { if (parent == map) { if (im == null) { c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map .getParent()); } else { im.setParent(map.getParent()); } break; } im = parent; parent = parent.getParent(); } } /** * Removes the specified action map from the specified component. * * @param c * Component. * @param map * Action map to remove. */ void removeUIActionMap(JComponent c, ActionMap map) { ActionMap im = null; ActionMap parent = c.getActionMap(); while (parent != null) { if (parent == map) { if (im == null) { c.setActionMap(map.getParent()); } else { im.setParent(map.getParent()); } break; } im = parent; parent = parent.getParent(); } } } /** * This class is used to dismiss popup panels on the following events: * *
    *
  • Mouse click outside any shown popup panel.
  • *
  • Closing, iconifying or deactivation of a top-level window.
  • *
  • Any change in the component hierarchy of a top-level window.
  • *
* * Only one top-level window is tracked at any time. The assumption is that * the {@link PopupPanelManager} only shows popup panels originating from * one top-level window. * * @author Kirill Grouchnikov */ protected static class WindowTracker implements PopupPanelManager.PopupListener, AWTEventListener, ComponentListener, WindowListener { /** * The currently tracked window. It is the window of the originating * component of the first popup panel in the currently shown sequence of * {@link PopupPanelManager}. */ Window grabbedWindow; /** * Last selected path in the {@link PopupPanelManager}. */ List lastPathSelected; /** * Creates the new window tracker. */ public WindowTracker() { PopupPanelManager popupPanelManager = PopupPanelManager .defaultManager(); popupPanelManager.addPopupListener(this); this.lastPathSelected = popupPanelManager.getShownPath(); if (this.lastPathSelected.size() != 0) { grabWindow(this.lastPathSelected); } } /** * Grabs the window of the first popup panel in the specified popup * panel sequence. * * @param shownPath * Sequence of the currently shown popup panels. */ void grabWindow(List shownPath) { final Toolkit tk = Toolkit.getDefaultToolkit(); java.security.AccessController .doPrivileged(new java.security.PrivilegedAction() { public Object run() { tk.addAWTEventListener(WindowTracker.this, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK | AWTEvent.WINDOW_EVENT_MASK); return null; } }); Component invoker = shownPath.get(0).getPopupOriginator(); grabbedWindow = invoker instanceof Window ? (Window) invoker : SwingUtilities.getWindowAncestor(invoker); if (grabbedWindow != null) { grabbedWindow.addComponentListener(this); grabbedWindow.addWindowListener(this); } } /** * Ungrabs the currently tracked window. */ void ungrabWindow() { final Toolkit tk = Toolkit.getDefaultToolkit(); // The grab should be removed java.security.AccessController .doPrivileged(new java.security.PrivilegedAction() { public Object run() { tk.removeAWTEventListener(WindowTracker.this); return null; } }); if (grabbedWindow != null) { grabbedWindow.removeComponentListener(this); grabbedWindow.removeWindowListener(this); grabbedWindow = null; } } @Override public void popupShown(PopupEvent event) { PopupPanelManager msm = PopupPanelManager.defaultManager(); List p = msm.getShownPath(); if (lastPathSelected.size() == 0 && p.size() != 0) { // if it is the first popup panel to be shown, grab its window grabWindow(p); } lastPathSelected = p; } @Override public void popupHidden(PopupEvent event) { PopupPanelManager msm = PopupPanelManager.defaultManager(); List p = msm.getShownPath(); if (lastPathSelected.size() != 0 && p.size() == 0) { // if it is the last popup panel to be hidden, ungrab its window ungrabWindow(); } lastPathSelected = p; } public void eventDispatched(AWTEvent ev) { if (!(ev instanceof MouseEvent)) { // We are interested in MouseEvents only return; } MouseEvent me = (MouseEvent) ev; final Component src = me.getComponent(); JPopupPanel popupPanelParent = (JPopupPanel) SwingUtilities .getAncestorOfClass(JPopupPanel.class, src); switch (me.getID()) { case MouseEvent.MOUSE_PRESSED: boolean wasCommandButtonPopupShowing = false; if (src instanceof JCommandButton) { wasCommandButtonPopupShowing = ((JCommandButton) src) .getPopupModel().isPopupShowing(); } if (!wasCommandButtonPopupShowing && (popupPanelParent != null)) { // close all popups until this parent and return PopupPanelManager.defaultManager().hidePopups( popupPanelParent); return; } if (src instanceof JRibbonTaskToggleButton) { JRibbon ribbon = (JRibbon) SwingUtilities .getAncestorOfClass(JRibbon.class, src); if ((ribbon != null) && FlamingoUtilities .isShowingMinimizedRibbonInPopup(ribbon)) { // This will be handled in the action listener installed // on ribbon task toggle buttons in BasicRibbonUI. // There the ribbon popup will be hidden. return; } } // if the popup of command button was showing, it will be hidden // in BasicCommandButtonUI.processPopupAction() - via // BasicCommandButtonUI.createPopupActionListener(). if (!wasCommandButtonPopupShowing) { // special case - ignore mouse press on an item in a combo popup if (SwingUtilities .getAncestorOfClass(ComboPopup.class, src) == null) { PopupPanelManager.defaultManager().hidePopups(src); } } // pass the event so that it gets processed by the controls break; case MouseEvent.MOUSE_RELEASED: // special case - mouse release on an item in a combo popup if (SwingUtilities.getAncestorOfClass(ComboPopup.class, src) != null) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { PopupPanelManager.defaultManager().hidePopups(src); } }); } // pass the event so that it gets processed by the controls break; case MouseEvent.MOUSE_WHEEL: if (popupPanelParent != null) { // close all popups until this parent and return PopupPanelManager.defaultManager().hidePopups( popupPanelParent); return; } PopupPanelManager.defaultManager().hidePopups(src); break; } } /** * Checks whether the specified component lies inside a * {@link JPopupPanel}. * * @param src * Component. * @return true if the specified component lies inside a * {@link JPopupPanel}. */ boolean isInPopupPanel(Component src) { for (Component c = src; c != null; c = c.getParent()) { if (c instanceof Applet || c instanceof Window) { break; } else if (c instanceof JPopupPanel) { return true; } } return false; } public void componentResized(ComponentEvent e) { PopupPanelManager.defaultManager().hidePopups(null); } public void componentMoved(ComponentEvent e) { PopupPanelManager.defaultManager().hidePopups(null); } public void componentShown(ComponentEvent e) { PopupPanelManager.defaultManager().hidePopups(null); } public void componentHidden(ComponentEvent e) { PopupPanelManager.defaultManager().hidePopups(null); } public void windowClosing(WindowEvent e) { PopupPanelManager.defaultManager().hidePopups(null); } public void windowClosed(WindowEvent e) { PopupPanelManager.defaultManager().hidePopups(null); } public void windowIconified(WindowEvent e) { PopupPanelManager.defaultManager().hidePopups(null); } public void windowDeactivated(WindowEvent e) { PopupPanelManager.defaultManager().hidePopups(null); } public void windowOpened(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowActivated(WindowEvent e) { } } } src/org/pushingpixels/flamingo/internal/ui/common/popup/BasicCommandPopupMenuUI.java0000644000175000017500000005137711401230444030062 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import java.awt.*; import java.awt.geom.AffineTransform; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.popup.*; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager.PopupEvent; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonPanelUI; import org.pushingpixels.flamingo.internal.ui.common.CommandButtonLayoutManagerMedium; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class BasicCommandPopupMenuUI extends BasicPopupPanelUI { /** * The associated popup menu */ protected JCommandPopupMenu popupMenu; protected ChangeListener popupMenuChangeListener; protected PopupPanelManager.PopupListener popupListener; protected ScrollableCommandButtonPanel commandButtonPanel; protected JScrollablePanel menuItemsPanel; public static final String FORCE_ICON = "flamingo.internal.commandButtonLayoutManagerMedium.forceIcon"; protected static final CommandButtonDisplayState POPUP_MENU = new CommandButtonDisplayState( "Popup menu", 16) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton commandButton) { return new CommandButtonLayoutManagerMedium() { @Override protected float getIconTextGapFactor() { return 2.0f; }; }; } }; /** * Popup panel that hosts groups of icons. * * @author Kirill Grouchnikov */ protected static class ScrollableCommandButtonPanel extends JComponent { /** * Maximum dimension of this popup gallery. */ protected Dimension maxDimension; /** * The internal panel that hosts the icon command buttons. Is hosted in * the {@link #scroll}. */ protected JCommandButtonPanel buttonPanel; /** * The maximum number of visible button rows. */ protected int maxVisibleButtonRows; /** * Scroll panel that hosts {@link #buttonPanel}. */ protected JScrollPane scroll; /** * Creates new a icon popup panel. * * @param iconPanel * The internal panel that hosts icon command buttons. * @param maxButtonColumns * The maximum number of button columns. * @param maxVisibleButtonRows * The maximum number of visible button rows. */ public ScrollableCommandButtonPanel(JCommandButtonPanel iconPanel, int maxButtonColumns, int maxVisibleButtonRows) { this.buttonPanel = iconPanel; this.buttonPanel.setMaxButtonColumns(maxButtonColumns); this.maxVisibleButtonRows = maxVisibleButtonRows; int maxButtonWidth = 0; int maxButtonHeight = 0; int groupCount = iconPanel.getGroupCount(); for (int i = 0; i < groupCount; i++) { for (AbstractCommandButton button : iconPanel .getGroupButtons(i)) { maxButtonWidth = Math.max(maxButtonWidth, button .getPreferredSize().width); maxButtonHeight = Math.max(maxButtonHeight, button .getPreferredSize().height); } } updateMaxDimension(); this.scroll = new JScrollPane(this.buttonPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); this.scroll.setBorder(new EmptyBorder(0, 0, 0, 0)); this.buttonPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); this.scroll.setOpaque(false); this.scroll.getViewport().setOpaque(false); this.setLayout(new IconPopupLayout()); this.add(this.scroll); this.setBorder(new Border() { @Override public Insets getBorderInsets(Component c) { return new Insets(0, 0, 1, 0); } @Override public boolean isBorderOpaque() { return true; } @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { g.setColor(FlamingoUtilities.getBorderColor()); g.drawLine(x, y + height - 1, x + width, y + height - 1); } }); } /** * Updates the max dimension of this panel. This method is for internal * use only. */ public void updateMaxDimension() { if (this.buttonPanel == null) return; this.buttonPanel.setPreferredSize(null); Dimension prefIconPanelDim = this.buttonPanel.getPreferredSize(); // fix for issue 13 - respect the gaps and insets BasicCommandButtonPanelUI panelUI = (BasicCommandButtonPanelUI) buttonPanel .getUI(); int titlePanelCount = buttonPanel.isToShowGroupLabels() ? 1 : 0; this.maxDimension = new Dimension(prefIconPanelDim.width, panelUI .getPreferredHeight(this.maxVisibleButtonRows, titlePanelCount)); this.setPreferredSize(null); } /** * Layout manager for this popup gallery. * * @author Kirill Grouchnikov */ protected class IconPopupLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component comp) { } /* * (non-Javadoc) * * @see * java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component comp) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container parent) { Insets insets = parent.getInsets(); int left = insets.left; int right = insets.right; int top = insets.top; int bottom = insets.bottom; scroll.setBounds(left, top, parent.getWidth() - left - right, parent.getHeight() - top - bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container parent) { return this.preferredLayoutSize(parent); } /* * (non-Javadoc) * * @see * java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container parent) { Insets insets = parent.getInsets(); int left = insets.left; int right = insets.right; int top = insets.top; int bottom = insets.bottom; Dimension controlPanelDim = buttonPanel.getPreferredSize(); if (controlPanelDim == null) controlPanelDim = new Dimension(0, 0); int w = Math.min(controlPanelDim.width, maxDimension.width) + left + right; int h = Math.min(controlPanelDim.height, maxDimension.height) + top + bottom; if (h == (maxDimension.height + top + bottom)) { int scrollBarWidth = UIManager.getInt("ScrollBar.width"); if (scrollBarWidth == 0) { // Nimbus scrollBarWidth = new JScrollBar(JScrollBar.VERTICAL) .getPreferredSize().width; } w += scrollBarWidth; // h += 5; } return new Dimension(w, h); } } } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicCommandPopupMenuUI(); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.ui.BasicPopupPanelUI#installUI(javax.swing. * JComponent) */ @Override public void installUI(JComponent c) { this.popupMenu = (JCommandPopupMenu) c; super.installUI(this.popupMenu); this.popupMenu.setLayout(this.createLayoutManager()); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.ui.BasicPopupPanelUI#installComponents() */ @Override protected void installComponents() { super.installComponents(); syncComponents(); } protected void syncComponents() { if (this.popupMenu.hasCommandButtonPanel()) { this.commandButtonPanel = createScrollableButtonPanel(); this.popupMenu.add(this.commandButtonPanel); } final JPanel menuPanel = this.createMenuPanel(); menuPanel.setLayout(new LayoutManager() { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension preferredLayoutSize(Container parent) { int height = 0; int width = 0; for (int i = 0; i < parent.getComponentCount(); i++) { Dimension pref = parent.getComponent(i).getPreferredSize(); height += pref.height; width = Math.max(width, pref.width); } Insets ins = parent.getInsets(); return new Dimension(width + ins.left + ins.right, height + ins.top + ins.bottom); } @Override public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); } @Override public void layoutContainer(Container parent) { Insets ins = parent.getInsets(); int topY = ins.top; for (int i = 0; i < parent.getComponentCount(); i++) { Component comp = parent.getComponent(i); Dimension pref = comp.getPreferredSize(); comp.setBounds(ins.left, topY, parent.getWidth() - ins.left - ins.right, pref.height); topY += pref.height; } } }); this.popupMenu.putClientProperty(BasicCommandPopupMenuUI.FORCE_ICON, null); java.util.List menuComponents = this.popupMenu .getMenuComponents(); if (menuComponents != null) { for (Component menuComponent : menuComponents) { menuPanel.add(menuComponent); } boolean atLeastOneButtonHasIcon = false; for (Component menuComponent : menuComponents) { if (menuComponent instanceof JCommandMenuButton) { JCommandMenuButton menuButton = (JCommandMenuButton) menuComponent; if (menuButton.getIcon() != null) { atLeastOneButtonHasIcon = true; } } if (menuComponent instanceof JCommandToggleMenuButton) { atLeastOneButtonHasIcon = true; } } this.popupMenu.putClientProperty( BasicCommandPopupMenuUI.FORCE_ICON, atLeastOneButtonHasIcon ? Boolean.TRUE : null); for (Component menuComponent : menuComponents) { if (menuComponent instanceof JCommandMenuButton) { JCommandMenuButton menuButton = (JCommandMenuButton) menuComponent; menuButton.putClientProperty( BasicCommandPopupMenuUI.FORCE_ICON, atLeastOneButtonHasIcon ? Boolean.TRUE : null); menuButton.setDisplayState(POPUP_MENU); } if (menuComponent instanceof JCommandToggleMenuButton) { JCommandToggleMenuButton menuButton = (JCommandToggleMenuButton) menuComponent; menuButton.putClientProperty( BasicCommandPopupMenuUI.FORCE_ICON, Boolean.TRUE); menuButton.setDisplayState(POPUP_MENU); } } } this.menuItemsPanel = new JScrollablePanel(menuPanel, JScrollablePanel.ScrollType.VERTICALLY); final LayoutManager scrollableLm = this.menuItemsPanel.getLayout(); this.menuItemsPanel.setLayout(new LayoutManager() { @Override public void addLayoutComponent(String name, Component comp) { scrollableLm.addLayoutComponent(name, comp); } @Override public void removeLayoutComponent(Component comp) { scrollableLm.removeLayoutComponent(comp); } @Override public Dimension preferredLayoutSize(Container parent) { Dimension result = menuPanel.getPreferredSize(); int maxMenuButtonCount = popupMenu.getMaxVisibleMenuButtons(); if ((maxMenuButtonCount < 0) || (maxMenuButtonCount >= menuPanel.getComponentCount())) { return result; } // the assumption is that all menu buttons have the // same height. int singleHeight = menuPanel.getComponent(0).getPreferredSize().height; int width = 0; for (int i = 0; i < menuPanel.getComponentCount(); i++) { width = Math.max(width, menuPanel.getComponent(i) .getPreferredSize().width); } Insets ins = parent.getInsets(); // add two for scroller buttons return new Dimension(width + ins.left + ins.right, singleHeight * (maxMenuButtonCount + 2) + ins.top + ins.bottom); } @Override public Dimension minimumLayoutSize(Container parent) { return this.preferredLayoutSize(parent); } @Override public void layoutContainer(Container parent) { scrollableLm.layoutContainer(parent); } }); this.popupMenu.add(this.menuItemsPanel); } protected ScrollableCommandButtonPanel createScrollableButtonPanel() { return new ScrollableCommandButtonPanel(this.popupMenu .getMainButtonPanel(), this.popupMenu.getMaxButtonColumns(), this.popupMenu.getMaxVisibleButtonRows()); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.ui.BasicPopupPanelUI#uninstallComponents() */ @Override protected void uninstallComponents() { this.popupMenu.removeAll(); super.uninstallComponents(); } @Override protected void installListeners() { super.installListeners(); this.popupMenuChangeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { popupMenu.removeAll(); syncComponents(); } }; this.popupMenu.addChangeListener(this.popupMenuChangeListener); this.popupListener = new PopupPanelManager.PopupListener() { @Override public void popupShown(PopupEvent event) { } @Override public void popupHidden(PopupEvent event) { if (event.getSource() instanceof JColorSelectorPopupMenu) { ((JColorSelectorPopupMenu) event.getSource()) .getColorSelectorCallback().onColorRollover(null); } } }; PopupPanelManager.defaultManager().addPopupListener(this.popupListener); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.ui.BasicPopupPanelUI#uninstallListeners() */ @Override protected void uninstallListeners() { this.popupMenu.removeChangeListener(this.popupMenuChangeListener); this.popupMenuChangeListener = null; PopupPanelManager.defaultManager().addPopupListener(this.popupListener); this.popupListener = null; super.uninstallListeners(); } protected JPanel createMenuPanel() { return new MenuPanel(); } protected LayoutManager createLayoutManager() { return new PopupMenuLayoutManager(); } protected class PopupMenuLayoutManager implements LayoutManager { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension minimumLayoutSize(Container parent) { return null; } @Override public Dimension preferredLayoutSize(Container parent) { int height = 0; int width = 0; if (commandButtonPanel != null) { width = commandButtonPanel.getPreferredSize().width; height = commandButtonPanel.getPreferredSize().height; } Dimension menuItemsPref = (popupMenu.getMaxVisibleMenuButtons() > 0) ? menuItemsPanel .getPreferredSize() : menuItemsPanel.getView().getPreferredSize(); width = Math.max(menuItemsPref.width, width); height += menuItemsPref.height; Insets ins = parent.getInsets(); return new Dimension(width + ins.left + ins.right, height + ins.top + ins.bottom); } @Override public void layoutContainer(Container parent) { Insets ins = parent.getInsets(); int bottomY = parent.getHeight() - ins.bottom; Dimension menuItemsPref = (popupMenu.getMaxVisibleMenuButtons() > 0) ? menuItemsPanel .getPreferredSize() : menuItemsPanel.getView().getPreferredSize(); menuItemsPanel.setBounds(ins.left, bottomY - menuItemsPref.height, parent.getWidth() - ins.left - ins.right, menuItemsPref.height); menuItemsPanel.doLayout(); bottomY -= menuItemsPref.height; if (commandButtonPanel != null) { commandButtonPanel.setBounds(ins.left, ins.top, parent .getWidth() - ins.left - ins.right, bottomY - ins.top); commandButtonPanel.invalidate(); commandButtonPanel.validate(); commandButtonPanel.doLayout(); } } } protected static class MenuPanel extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); JCommandPopupMenu menu = (JCommandPopupMenu) SwingUtilities .getAncestorOfClass(JCommandPopupMenu.class, this); if (Boolean.TRUE.equals(menu.getClientProperty(FORCE_ICON))) { this.paintIconGutterBackground(g); this.paintIconGutterSeparator(g); } } protected int getSeparatorX() { JCommandPopupMenu menu = (JCommandPopupMenu) SwingUtilities .getAncestorOfClass(JCommandPopupMenu.class, this); if (!Boolean.TRUE.equals(menu.getClientProperty(FORCE_ICON))) { return -1; } java.util.List menuComponents = menu.getMenuComponents(); if (menuComponents != null) { for (Component menuComponent : menuComponents) { if (menuComponent instanceof JCommandMenuButton || menuComponent instanceof JCommandToggleMenuButton) { AbstractCommandButton button = (AbstractCommandButton) menuComponent; if (!Boolean.TRUE.equals(button .getClientProperty(FORCE_ICON))) { continue; } boolean ltr = button.getComponentOrientation() .isLeftToRight(); CommandButtonLayoutManager.CommandButtonLayoutInfo layoutInfo = button .getUI().getLayoutInfo(); if (ltr) { int iconRight = layoutInfo.iconRect.x + layoutInfo.iconRect.width; int textLeft = button.getWidth(); for (CommandButtonLayoutManager.TextLayoutInfo tli : layoutInfo.textLayoutInfoList) { textLeft = Math.min(textLeft, tli.textRect.x); } return (iconRight + textLeft) / 2; } else { int iconLeft = layoutInfo.iconRect.x; int textRight = 0; for (CommandButtonLayoutManager.TextLayoutInfo tli : layoutInfo.textLayoutInfoList) { textRight = Math.max(textRight, tli.textRect.x + tli.textRect.width); } return (iconLeft + textRight) / 2; } } } } throw new IllegalStateException( "Menu marked to show icons but no menu buttons in it"); } protected void paintIconGutterSeparator(Graphics g) { CellRendererPane buttonRendererPane = new CellRendererPane(); JSeparator rendererSeparator = new JSeparator(JSeparator.VERTICAL); buttonRendererPane.setBounds(0, 0, this.getWidth(), this .getHeight()); int sepX = this.getSeparatorX(); if (this.getComponentOrientation().isLeftToRight()) { buttonRendererPane.paintComponent(g, rendererSeparator, this, sepX, 2, 2, this.getHeight() - 4, true); } else { buttonRendererPane.paintComponent(g, rendererSeparator, this, sepX, 2, 2, this.getHeight() - 4, true); } } protected void paintIconGutterBackground(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); g2d.setComposite(AlphaComposite.SrcOver.derive(0.7f)); int sepX = this.getSeparatorX(); if (this.getComponentOrientation().isLeftToRight()) { g2d.clipRect(0, 0, sepX + 2, this.getHeight()); AffineTransform at = AffineTransform.getTranslateInstance(0, this.getHeight()); at.rotate(-Math.PI / 2); g2d.transform(at); FlamingoUtilities.renderSurface(g2d, this, new Rectangle(0, 0, this.getHeight(), 50), false, false, false); } else { g2d.clipRect(this.getWidth() - sepX, 0, sepX + 2, this .getHeight()); AffineTransform at = AffineTransform.getTranslateInstance(0, this.getHeight()); at.rotate(-Math.PI / 2); g2d.transform(at); FlamingoUtilities.renderSurface(g2d, this, new Rectangle(0, sepX, this.getHeight(), this.getWidth() - sepX), false, false, false); } g2d.dispose(); } } }src/org/pushingpixels/flamingo/internal/ui/common/popup/JColorSelectorPanel.java0000644000175000017500000000555411401230444027300 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import javax.swing.JPanel; import javax.swing.UIManager; public class JColorSelectorPanel extends JPanel { private String caption; private JPanel colorSelectionContainer; private boolean isLastPanel; /** * The UI class ID string. */ public static final String uiClassID = "ColorSelectorPanelUI"; public JColorSelectorPanel(String caption, JPanel colorSelectionContainer) { this.caption = caption; this.colorSelectionContainer = colorSelectionContainer; this.updateUI(); } /* * (non-Javadoc) * * @see javax.swing.JButton#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((ColorSelectorPanelUI) UIManager.getUI(this)); } else { setUI(BasicColorSelectorPanelUI.createUI(this)); } } /* * (non-Javadoc) * * @see javax.swing.JButton#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } public String getCaption() { return this.caption; } public JPanel getColorSelectionContainer() { return this.colorSelectionContainer; } public void setLastPanel(boolean isLastPanel) { this.isLastPanel = isLastPanel; } public boolean isLastPanel() { return this.isLastPanel; } } src/org/pushingpixels/flamingo/internal/ui/common/popup/JColorSelectorComponent.java0000644000175000017500000000754011401230444030200 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import java.awt.Color; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.popup.JColorSelectorPopupMenu; public class JColorSelectorComponent extends JComponent { private Color color; private List colorChooserCallbacks; private boolean isTopOpen; private boolean isBottomOpen; /** * The UI class ID string. */ public static final String uiClassID = "ColorSelectorComponentUI"; public JColorSelectorComponent(Color color, JColorSelectorPopupMenu.ColorSelectorCallback colorChooserCallback) { this.setOpaque(true); this.color = color; this.colorChooserCallbacks = new ArrayList(); this.colorChooserCallbacks.add(colorChooserCallback); this.updateUI(); } /* * (non-Javadoc) * * @see javax.swing.JButton#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((ColorSelectorComponentUI) UIManager.getUI(this)); } else { setUI(BasicColorSelectorComponentUI.createUI(this)); } } /* * (non-Javadoc) * * @see javax.swing.JButton#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } public Color getColor() { return this.color; } public synchronized void addColorSelectorCallback( JColorSelectorPopupMenu.ColorSelectorCallback callback) { this.colorChooserCallbacks.add(callback); } public synchronized void onColorSelected(Color selected) { for (JColorSelectorPopupMenu.ColorSelectorCallback callback : this.colorChooserCallbacks) { callback.onColorSelected(selected); } } public synchronized void onColorRollover(Color rollover) { for (JColorSelectorPopupMenu.ColorSelectorCallback callback : this.colorChooserCallbacks) { callback.onColorRollover(rollover); } } public void setTopOpen(boolean isTopOpen) { this.isTopOpen = isTopOpen; } public void setBottomOpen(boolean isBottomOpen) { this.isBottomOpen = isBottomOpen; } public boolean isTopOpen() { return this.isTopOpen; } public boolean isBottomOpen() { return this.isBottomOpen; } } src/org/pushingpixels/flamingo/internal/ui/common/popup/BasicColorSelectorComponentUI.java0000644000175000017500000001576311401230444031274 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; import org.pushingpixels.trident.Timeline; import org.pushingpixels.trident.swing.SwingRepaintCallback; /** * Basic UI for color selector component {@link JColorSelectorComponent}. * * @author Kirill Grouchnikov */ public class BasicColorSelectorComponentUI extends ColorSelectorComponentUI { protected JColorSelectorComponent colorSelectorComponent; protected ButtonModel buttonModel; protected MouseListener mouseListener; protected ChangeListener modelChangeListener; protected ActionListener actionListener; protected Timeline rolloverTimeline; protected float rollover; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicColorSelectorComponentUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.colorSelectorComponent = (JColorSelectorComponent) c; this.buttonModel = new DefaultButtonModel(); installDefaults(); installComponents(); installListeners(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); c.setLayout(null); this.colorSelectorComponent = null; } /** * Installs listeners on the associated color selector component. */ protected void installListeners() { this.mouseListener = new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { if (!buttonModel.isRollover()) { colorSelectorComponent .onColorRollover(colorSelectorComponent.getColor()); rolloverTimeline.play(); } buttonModel.setRollover(true); } @Override public void mouseExited(MouseEvent e) { if (buttonModel.isRollover()) { colorSelectorComponent.onColorRollover(null); rolloverTimeline.playReverse(); } buttonModel.setRollover(false); } @Override public void mousePressed(MouseEvent e) { buttonModel.setArmed(true); buttonModel.setPressed(true); } @Override public void mouseReleased(MouseEvent e) { buttonModel.setPressed(false); buttonModel.setArmed(false); } }; this.colorSelectorComponent.addMouseListener(this.mouseListener); this.modelChangeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { colorSelectorComponent.repaint(); } }; this.buttonModel.addChangeListener(this.modelChangeListener); this.actionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { colorSelectorComponent.onColorSelected(colorSelectorComponent .getColor()); PopupPanelManager.defaultManager().hidePopups(null); } }; this.buttonModel.addActionListener(this.actionListener); } /** * Uninstalls listeners from the associated color selector component. */ protected void uninstallListeners() { this.buttonModel.removeActionListener(this.actionListener); this.actionListener = null; this.buttonModel.removeChangeListener(this.modelChangeListener); this.modelChangeListener = null; this.colorSelectorComponent.removeMouseListener(this.mouseListener); this.mouseListener = null; } /** * Installs defaults on the associated color selector component. */ protected void installDefaults() { this.rolloverTimeline = new Timeline(this); this.rolloverTimeline.addPropertyToInterpolate("rollover", 0.0f, 1.0f); this.rolloverTimeline.addCallback(new SwingRepaintCallback( this.colorSelectorComponent)); this.rolloverTimeline.setDuration(150); } /** * Uninstalls defaults from the associated color selector component. */ protected void uninstallDefaults() { } /** * Installs subcomponents on the associated color selector component. */ protected void installComponents() { } /** * Uninstalls subcomponents from the associated color selector component. */ protected void uninstallComponents() { } public void setRollover(float rollover) { this.rollover = rollover; } @Override public void update(Graphics g, JComponent c) { int w = this.colorSelectorComponent.getWidth(); int h = this.colorSelectorComponent.getHeight(); Graphics2D g2d = (Graphics2D) g.create(); Color fillColor = this.colorSelectorComponent.getColor(); g2d.setColor(fillColor); g2d.fillRect(0, 0, w, h); float[] hsb = new float[3]; Color.RGBtoHSB(fillColor.getRed(), fillColor.getGreen(), fillColor .getBlue(), hsb); float brightness = hsb[2] * 0.7f; g2d.setColor(new Color(brightness, brightness, brightness)); int ty = this.colorSelectorComponent.isTopOpen() ? 1 : 0; int by = this.colorSelectorComponent.isBottomOpen() ? 1 : 0; g2d.drawRect(0, -ty, w - 1, h - 1 + ty + by); if (this.rollover > 0.0f) { g2d.setComposite(AlphaComposite.SrcOver.derive(this.rollover)); g2d.setColor(new Color(207, 186, 115)); g2d.drawRect(0, 0, w - 1, h - 1); g2d.setColor(new Color(230, 212, 150)); g2d.drawRect(1, 1, w - 3, h - 3); } g2d.dispose(); } } src/org/pushingpixels/flamingo/internal/ui/common/popup/BasicColorSelectorPanelUI.java0000644000175000017500000001521011401230444030354 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import java.awt.*; import javax.swing.*; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Basic UI for color selector panel {@link JColorSelectorPanel}. * * @author Kirill Grouchnikov */ public class BasicColorSelectorPanelUI extends ColorSelectorPanelUI { protected JColorSelectorPanel colorSelectorPanel; protected JLabel captionLabel; protected JPanel colorSelectorContainer; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicColorSelectorPanelUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.colorSelectorPanel = (JColorSelectorPanel) c; installDefaults(); installComponents(); installListeners(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); c.setLayout(null); this.colorSelectorPanel = null; } /** * Installs listeners on the associated color selector panel. */ protected void installListeners() { } /** * Uninstalls listeners from the associated color selector panel. */ protected void uninstallListeners() { } /** * Installs defaults on the associated color selector panel. */ protected void installDefaults() { } /** * Uninstalls defaults from the associated color selector panel. */ protected void uninstallDefaults() { } /** * Installs subcomponents on the associated color selector panel. */ protected void installComponents() { this.captionLabel = new JLabel(this.colorSelectorPanel.getCaption()); this.captionLabel.setFont(this.captionLabel.getFont().deriveFont( Font.BOLD)); this.colorSelectorContainer = this.colorSelectorPanel .getColorSelectionContainer(); this.colorSelectorPanel.add(this.captionLabel); if (this.colorSelectorContainer != null) { this.colorSelectorPanel.add(this.colorSelectorContainer); } this.colorSelectorPanel.setLayout(new PanelLayout()); } /** * Uninstalls subcomponents from the associated color selector panel. */ protected void uninstallComponents() { this.colorSelectorPanel.remove(this.captionLabel); if (this.colorSelectorContainer != null) { this.colorSelectorPanel.remove(this.colorSelectorContainer); } } @Override public void paint(Graphics g, JComponent c) { Color bg = this.colorSelectorPanel.getBackground(); g.setColor(bg); int w = c.getWidth(); int h = c.getHeight(); g.fillRect(0, 0, w, h); Rectangle captionBackground = this.captionLabel.getBounds(); this.paintCaptionBackground(g, 0, 0, w, captionBackground.height + 2 * getLayoutGap()); if (this.colorSelectorPanel.isLastPanel()) { paintBottomDivider(g, 0, 0, w, h); } } protected void paintBottomDivider(Graphics g, int x, int y, int width, int height) { g.setColor(FlamingoUtilities.getBorderColor()); g.drawLine(x, y + height - 1, x + width - 1, y + height - 1); } protected void paintCaptionBackground(Graphics g, int x, int y, int width, int height) { FlamingoUtilities.renderSurface(g, this.colorSelectorPanel, new Rectangle(x, y, width, height), false, true, true); } /** * Returns the layout gap for button panel components. * * @return The layout gap for button panel components. */ protected int getLayoutGap() { return 4; } protected class PanelLayout implements LayoutManager { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension minimumLayoutSize(Container parent) { return new Dimension(20, 20); } @Override public Dimension preferredLayoutSize(Container parent) { int layoutGap = getLayoutGap(); Dimension labelPrefSize = captionLabel.getPreferredSize(); Dimension contPrefSize = colorSelectorContainer.getPreferredSize(); return new Dimension(Math.max(labelPrefSize.width, contPrefSize.width), 2 * layoutGap + labelPrefSize.height + contPrefSize.height + (colorSelectorPanel.isLastPanel() ? 1 : 0)); } @Override public void layoutContainer(Container parent) { int layoutGap = getLayoutGap(); Dimension labelPrefSize = captionLabel.getPreferredSize(); int labelWidth = labelPrefSize.width; int labelHeight = labelPrefSize.height; int y = layoutGap; if (captionLabel.getComponentOrientation().isLeftToRight()) { captionLabel.setBounds(layoutGap, y, labelWidth, labelHeight); } else { captionLabel.setBounds(parent.getWidth() - layoutGap - labelWidth, y, labelWidth, labelHeight); } y += labelHeight + layoutGap; colorSelectorContainer.setBounds(0, y, parent.getWidth(), parent .getHeight() - y - (colorSelectorPanel.isLastPanel() ? 1 : 0)); } } } src/org/pushingpixels/flamingo/internal/ui/common/popup/ColorSelectorComponentUI.java0000644000175000017500000000357411401230444030327 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common.popup; import javax.swing.plaf.ComponentUI; /** * UI for command button ({@link JColorSelectorComponent}). * * @author Kirill Grouchnikov */ public abstract class ColorSelectorComponentUI extends ComponentUI { } src/org/pushingpixels/flamingo/internal/ui/common/CommandButtonUI.java0000644000175000017500000000441711401230444025271 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.Point; import javax.swing.plaf.ButtonUI; import org.pushingpixels.flamingo.api.common.CommandButtonLayoutManager; import org.pushingpixels.flamingo.api.common.JCommandButton; /** * UI for command button ({@link JCommandButton}). * * @author Kirill Grouchnikov */ public abstract class CommandButtonUI extends ButtonUI { /** * Returns the layout information for the associated button. * * @return Layout information for the associated button. */ public abstract CommandButtonLayoutManager.CommandButtonLayoutInfo getLayoutInfo(); public abstract Point getKeyTipAnchorCenterPoint(); } src/org/pushingpixels/flamingo/internal/ui/common/BasicCommandButtonUI.java0000644000175000017500000011622411407771362026252 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.ColorConvertOp; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.*; import javax.swing.plaf.basic.BasicGraphicsUtils; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.CommandButtonLayoutManager.CommandButtonLayoutInfo; import org.pushingpixels.flamingo.api.common.CommandButtonLayoutManager.CommandButtonSeparatorOrientation; import org.pushingpixels.flamingo.api.common.JCommandButtonStrip.StripOrientation; import org.pushingpixels.flamingo.api.common.icon.FilteredResizableIcon; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.model.PopupButtonModel; import org.pushingpixels.flamingo.api.common.popup.*; import org.pushingpixels.flamingo.internal.utils.*; /** * Basic UI for command button {@link JCommandButton}. * * @author Kirill Grouchnikov */ public class BasicCommandButtonUI extends CommandButtonUI { /** * The associated command button. */ protected AbstractCommandButton commandButton; /** * Indication whether the mouse pointer is over the associated command * button. */ protected boolean isUnderMouse; /** * Property change listener. */ protected PropertyChangeListener propertyChangeListener; /** * Tracks user interaction with the command button (including keyboard and * mouse). */ protected BasicCommandButtonListener basicPopupButtonListener; /** * Layout information. */ protected CommandButtonLayoutManager.CommandButtonLayoutInfo layoutInfo; /** * Client property to mark the command button to have square corners. This * client property is for internal use only. */ public static final String EMULATE_SQUARE_BUTTON = "flamingo.internal.commandButton.ui.emulateSquare"; /** * Client property to mark the command button to not dispose the popups on * activation. * * @see #disposePopupsActionListener */ public static final String DONT_DISPOSE_POPUPS = "flamingo.internal.commandButton.ui.dontDisposePopups"; /** * This listener disposes all popup panels when button's action is * activated. An example of scenario would be a command button in the popup * panel of an in-ribbon gallery. When this command button is activated, the * associated popup panel is dismissed. * * @see #DONT_DISPOSE_POPUPS */ protected ActionListener disposePopupsActionListener; /** * Action listener on the popup area. */ protected PopupActionListener popupActionListener; /** * The "expand" action icon. */ protected ResizableIcon popupActionIcon; protected CommandButtonLayoutManager layoutManager; /** * Used to provide a LAF-consistent appearance under core LAFs. */ protected CellRendererPane buttonRendererPane; /** * Used to provide a LAF-consistent appearance under core LAFs. */ protected AbstractButton rendererButton; /** * Used to paint the separator between the action and popup areas. */ protected JSeparator rendererSeparator; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicCommandButtonUI(); } /** * Creates a new UI delegate. */ public BasicCommandButtonUI() { // this.toTakeSavedDimension = false; } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.commandButton = (AbstractCommandButton) c; installDefaults(); installComponents(); installListeners(); installKeyboardActions(); this.layoutManager = this.commandButton.getDisplayState() .createLayoutManager(this.commandButton); this.updateCustomDimension(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { c.setLayout(null); uninstallKeyboardActions(); uninstallListeners(); uninstallComponents(); uninstallDefaults(); this.commandButton = null; } /** * Installs defaults on the associated command button. */ protected void installDefaults() { configureRenderer(); this.updateBorder(); this.syncDisabledIcon(); } protected void configureRenderer() { this.buttonRendererPane = new CellRendererPane(); this.commandButton.add(buttonRendererPane); this.rendererButton = createRendererButton(); this.rendererButton.setOpaque(false); this.rendererSeparator = new JSeparator(); Font currFont = this.commandButton.getFont(); if ((currFont == null) || (currFont instanceof UIResource)) { this.commandButton.setFont(this.rendererButton.getFont()); } // special handling for Mac OS X native look-and-feel this.rendererButton.putClientProperty("JButton.buttonType", "square"); } protected void updateBorder() { Border currBorder = this.commandButton.getBorder(); if ((currBorder == null) || (currBorder instanceof UIResource)) { int tb = (int) (this.commandButton.getVGapScaleFactor() * 4); int lr = (int) (this.commandButton.getHGapScaleFactor() * 6); this.commandButton .setBorder(new BorderUIResource.EmptyBorderUIResource(tb, lr, tb, lr)); } } /** * Creates the renderer button. * * @return The renderer button. */ protected AbstractButton createRendererButton() { return new JButton(""); } /** * Installs subcomponents on the associated command button. */ protected void installComponents() { this.updatePopupActionIcon(); ResizableIcon buttonIcon = this.commandButton.getIcon(); if (buttonIcon instanceof AsynchronousLoading) { ((AsynchronousLoading) buttonIcon) .addAsynchronousLoadListener(new AsynchronousLoadListener() { public void completed(boolean success) { if (success && (commandButton != null)) commandButton.repaint(); } }); } if (this.commandButton instanceof JCommandButton) { this.popupActionIcon = this.createPopupActionIcon(); } } /** * Installs listeners on the associated command button. */ protected void installListeners() { this.basicPopupButtonListener = createButtonListener(this.commandButton); if (this.basicPopupButtonListener != null) { this.commandButton.addMouseListener(this.basicPopupButtonListener); this.commandButton .addMouseMotionListener(this.basicPopupButtonListener); this.commandButton.addFocusListener(this.basicPopupButtonListener); this.commandButton.addChangeListener(this.basicPopupButtonListener); } this.propertyChangeListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (AbstractButton.ICON_CHANGED_PROPERTY.equals(evt .getPropertyName())) { Icon newIcon = (Icon) evt.getNewValue(); if (newIcon instanceof AsynchronousLoading) { AsynchronousLoading async = (AsynchronousLoading) newIcon; async .addAsynchronousLoadListener(new AsynchronousLoadListener() { public void completed(boolean success) { if (success) { if (commandButton != null) { syncIconDimension(); syncDisabledIcon(); commandButton.repaint(); } } } }); if (!async.isLoading()) { syncIconDimension(); syncDisabledIcon(); commandButton.repaint(); } } else { syncIconDimension(); syncDisabledIcon(); commandButton.revalidate(); commandButton.repaint(); } } if ("commandButtonKind".equals(evt.getPropertyName())) { updatePopupActionIcon(); } if ("popupOrientationKind".equals(evt.getPropertyName())) { updatePopupActionIcon(); } if ("customDimension".equals(evt.getPropertyName())) { updateCustomDimension(); } if ("hgapScaleFactor".equals(evt.getPropertyName())) { updateBorder(); } if ("vgapScaleFactor".equals(evt.getPropertyName())) { updateBorder(); } if ("popupModel".equals(evt.getPropertyName())) { // rewire the popup action listener on the new popup model PopupButtonModel oldModel = (PopupButtonModel) evt .getOldValue(); PopupButtonModel newModel = (PopupButtonModel) evt .getNewValue(); if (oldModel != null) { oldModel.removePopupActionListener(popupActionListener); popupActionListener = null; } if (newModel != null) { popupActionListener = createPopupActionListener(); newModel.addPopupActionListener(popupActionListener); } } if ("displayState".equals(evt.getPropertyName())) { syncIconDimension(); syncDisabledIcon(); commandButton.invalidate(); commandButton.revalidate(); commandButton.doLayout(); } // pass the event to the layout manager if (layoutManager != null) { layoutManager.propertyChange(evt); } if ("componentOrientation".equals(evt.getPropertyName())) { updatePopupActionIcon(); commandButton.repaint(); } } }; this.commandButton .addPropertyChangeListener(this.propertyChangeListener); this.disposePopupsActionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { boolean toDismiss = !Boolean.TRUE.equals(commandButton .getClientProperty(DONT_DISPOSE_POPUPS)); if (toDismiss) { JCommandPopupMenu menu = (JCommandPopupMenu) SwingUtilities .getAncestorOfClass(JCommandPopupMenu.class, commandButton); if (menu != null) { toDismiss = menu.isToDismissOnChildClick(); } } if (toDismiss) { if (SwingUtilities.getAncestorOfClass(JPopupPanel.class, commandButton) != null) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // command button may be cleared if the // button click resulted in LAF switch if (commandButton != null) { // clear the active states commandButton.getActionModel().setPressed( false); commandButton.getActionModel().setRollover( false); commandButton.getActionModel().setArmed( false); } } }); } PopupPanelManager.defaultManager().hidePopups(null); } } }; this.commandButton.addActionListener(this.disposePopupsActionListener); if (this.commandButton instanceof JCommandButton) { this.popupActionListener = this.createPopupActionListener(); ((JCommandButton) this.commandButton).getPopupModel() .addPopupActionListener(this.popupActionListener); } } /** * Creates the icon for the popup area. * * @return The icon for the popup area. */ protected ResizableIcon createPopupActionIcon() { return FlamingoUtilities .getCommandButtonPopupActionIcon((JCommandButton) this.commandButton); } /** * Creates the button listener for the specified command button. * * @param b * Command button. * @return The button listener for the specified command button. */ protected BasicCommandButtonListener createButtonListener( AbstractCommandButton b) { return new BasicCommandButtonListener(); } /** * Installs the keyboard actions on the associated command button. */ protected void installKeyboardActions() { if (this.basicPopupButtonListener != null) { basicPopupButtonListener.installKeyboardActions(this.commandButton); } } /** * Uninstalls defaults from the associated command button. */ protected void uninstallDefaults() { unconfigureRenderer(); } protected void unconfigureRenderer() { if (this.buttonRendererPane != null) { this.commandButton.remove(this.buttonRendererPane); } this.buttonRendererPane = null; } /** * Uninstalls subcomponents from the associated command button. */ protected void uninstallComponents() { } /** * Uninstalls listeners from the associated command button. */ protected void uninstallListeners() { if (this.basicPopupButtonListener != null) { this.commandButton .removeMouseListener(this.basicPopupButtonListener); this.commandButton .removeMouseListener(this.basicPopupButtonListener); this.commandButton .removeMouseMotionListener(this.basicPopupButtonListener); this.commandButton .removeFocusListener(this.basicPopupButtonListener); this.commandButton .removeChangeListener(this.basicPopupButtonListener); } this.commandButton .removePropertyChangeListener(this.propertyChangeListener); this.propertyChangeListener = null; this.commandButton .removeActionListener(this.disposePopupsActionListener); this.disposePopupsActionListener = null; if (this.commandButton instanceof JCommandButton) { ((JCommandButton) this.commandButton).getPopupModel() .removePopupActionListener(this.popupActionListener); this.popupActionListener = null; } } /** * Uninstalls the keyboard actions from the associated command button. */ protected void uninstallKeyboardActions() { if (this.basicPopupButtonListener != null) { this.basicPopupButtonListener .uninstallKeyboardActions(this.commandButton); } } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void update(Graphics g, JComponent c) { Graphics2D g2d = (Graphics2D) g.create(); RenderingUtils.installDesktopHints(g2d); super.update(g2d, c); g2d.dispose(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void paint(Graphics g, JComponent c) { g.setFont(FlamingoUtilities.getFont(commandButton, "Ribbon.font", "Button.font", "Panel.font")); this.layoutInfo = this.layoutManager.getLayoutInfo(this.commandButton, g); commandButton.putClientProperty("icon.bounds", layoutInfo.iconRect); if (this.isPaintingBackground()) { this.paintButtonBackground(g, new Rectangle(0, 0, commandButton .getWidth(), commandButton.getHeight())); } // Graphics2D g2d = (Graphics2D) g.create(); // g2d.setColor(new Color(255, 0, 0, 64)); // if (getActionClickArea() != null) { // g2d.fill(getActionClickArea()); // } // g2d.setColor(new Color(0, 0, 255, 64)); // if (getPopupClickArea() != null) { // g2d.fill(getPopupClickArea()); // } // g2d.dispose(); if (layoutInfo.iconRect != null) { this.paintButtonIcon(g, layoutInfo.iconRect); } if (layoutInfo.popupActionRect.getWidth() > 0) { paintPopupActionIcon(g, layoutInfo.popupActionRect); } FontMetrics fm = g.getFontMetrics(); boolean isTextPaintedEnabled = commandButton.isEnabled(); if (commandButton instanceof JCommandButton) { JCommandButton jCommandButton = (JCommandButton) commandButton; isTextPaintedEnabled = layoutInfo.isTextInActionArea ? jCommandButton .getActionModel().isEnabled() : jCommandButton.getPopupModel().isEnabled(); } g.setColor(getForegroundColor(isTextPaintedEnabled)); if (layoutInfo.textLayoutInfoList != null) { for (CommandButtonLayoutManager.TextLayoutInfo mainTextLayoutInfo : layoutInfo.textLayoutInfoList) { if (mainTextLayoutInfo.text != null) { BasicGraphicsUtils.drawString(g, mainTextLayoutInfo.text, -1, mainTextLayoutInfo.textRect.x, mainTextLayoutInfo.textRect.y + fm.getAscent()); } } } if (isTextPaintedEnabled) { g.setColor(FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground")); } else { g.setColor(FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground").brighter()); } if (layoutInfo.extraTextLayoutInfoList != null) { for (CommandButtonLayoutManager.TextLayoutInfo extraTextLayoutInfo : layoutInfo.extraTextLayoutInfoList) { if (extraTextLayoutInfo.text != null) { BasicGraphicsUtils.drawString(g, extraTextLayoutInfo.text, -1, extraTextLayoutInfo.textRect.x, extraTextLayoutInfo.textRect.y + fm.getAscent()); } } } if (this.isPaintingSeparators() && (layoutInfo.separatorArea != null)) { if (layoutInfo.separatorOrientation == CommandButtonSeparatorOrientation.HORIZONTAL) { this .paintButtonHorizontalSeparator(g, layoutInfo.separatorArea); } else { this.paintButtonVerticalSeparator(g, layoutInfo.separatorArea); } } // Graphics2D g2d = (Graphics2D) g.create(); // // g2d.setColor(Color.red); // g2d.draw(layoutInfo.iconRect); // g2d.setColor(Color.blue); // if (layoutInfo.textLayoutInfoList != null) { // for (CommandButtonLayoutManager.TextLayoutInfo mainTextLayoutInfo : // layoutInfo.textLayoutInfoList) { // if (mainTextLayoutInfo.text != null) { // g2d.draw(mainTextLayoutInfo.textRect); // } // } // } // g2d.setColor(Color.magenta); // if (layoutInfo.extraTextLayoutInfoList != null) { // for (CommandButtonLayoutManager.TextLayoutInfo extraTextLayoutInfo : // layoutInfo.extraTextLayoutInfoList) { // if (extraTextLayoutInfo.text != null) { // g2d.draw(extraTextLayoutInfo.textRect); // } // } // } // g2d.setColor(Color.green); // g2d.draw(layoutInfo.popupActionRect); // g2d.dispose(); } protected Color getForegroundColor(boolean isTextPaintedEnabled) { if (isTextPaintedEnabled) { return FlamingoUtilities.getColor(Color.black, "Button.foreground"); } else { return FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground"); } } /** * Paints the icon of the popup area. * * @param g * Graphics context. * @param popupActionRect */ protected void paintPopupActionIcon(Graphics g, Rectangle popupActionRect) { int size = Math.max(popupActionRect.width - 2, 7); if (size % 2 == 0) size--; popupActionIcon.setDimension(new Dimension(size, size)); popupActionIcon.paintIcon(this.commandButton, g, popupActionRect.x + (popupActionRect.width - size) / 2, popupActionRect.y + (popupActionRect.height - size) / 2); } /** * Returns the current icon. * * @return Current icon. */ protected Icon getIconToPaint() { return (toUseDisabledIcon() && this.commandButton.getDisabledIcon() != null) ? this.commandButton .getDisabledIcon() : this.commandButton.getIcon(); } protected boolean toUseDisabledIcon() { // special case for command buttons with POPUP_ONLY kind - // check the popup model boolean toUseDisabledIcon; if (this.commandButton instanceof JCommandButton && ((JCommandButton) this.commandButton).getCommandButtonKind() == JCommandButton.CommandButtonKind.POPUP_ONLY) { toUseDisabledIcon = !((JCommandButton) this.commandButton) .getPopupModel().isEnabled(); } else { toUseDisabledIcon = !this.commandButton.getActionModel() .isEnabled(); } return toUseDisabledIcon; } /** * Paints command button vertical separator. * * @param graphics * Graphics context. * @param separatorArea * Separator area. */ protected void paintButtonVerticalSeparator(Graphics graphics, Rectangle separatorArea) { this.buttonRendererPane.setBounds(0, 0, this.commandButton.getWidth(), this.commandButton.getHeight()); Graphics2D g2d = (Graphics2D) graphics.create(); this.rendererSeparator.setOrientation(JSeparator.VERTICAL); this.buttonRendererPane.paintComponent(g2d, this.rendererSeparator, this.commandButton, separatorArea.x, 2, 2, this.commandButton .getHeight() - 4, true); g2d.dispose(); } /** * Paints command button horizontal separator. * * @param graphics * Graphics context. * @param separatorArea * Separator area. */ protected void paintButtonHorizontalSeparator(Graphics graphics, Rectangle separatorArea) { this.buttonRendererPane.setBounds(0, 0, this.commandButton.getWidth(), this.commandButton.getHeight()); Graphics2D g2d = (Graphics2D) graphics.create(); this.rendererSeparator.setOrientation(JSeparator.HORIZONTAL); this.buttonRendererPane.paintComponent(g2d, this.rendererSeparator, this.commandButton, 2, separatorArea.y, this.commandButton .getWidth() - 4, 2, true); g2d.dispose(); } /** * Paints command button background. * * @param graphics * Graphics context. * @param toFill * Rectangle for the background. */ protected void paintButtonBackground(Graphics graphics, Rectangle toFill) { ButtonModel actionModel = this.commandButton.getActionModel(); PopupButtonModel popupModel = (this.commandButton instanceof JCommandButton) ? ((JCommandButton) this.commandButton) .getPopupModel() : null; // first time - paint the full background passing both models this.paintButtonBackground(graphics, toFill, actionModel, popupModel); Rectangle actionArea = this.getLayoutInfo().actionClickArea; Rectangle popupArea = this.getLayoutInfo().popupClickArea; if ((actionArea != null) && !actionArea.isEmpty()) { // now overlay the action area with the background matching action // model Graphics2D graphicsAction = (Graphics2D) graphics.create(); // System.out.println(actionArea); graphicsAction.clip(actionArea); float actionAlpha = 0.4f; if ((popupModel != null) && !popupModel.isEnabled()) actionAlpha = 1.0f; graphicsAction.setComposite(AlphaComposite.SrcOver .derive(actionAlpha)); // System.out.println(graphicsAction.getClipBounds()); this.paintButtonBackground(graphicsAction, toFill, actionModel); graphicsAction.dispose(); } if ((popupArea != null) && !popupArea.isEmpty()) { // now overlay the popup area with the background matching popup // model Graphics2D graphicsPopup = (Graphics2D) graphics.create(); // System.out.println(popupArea); graphicsPopup.clip(popupArea); // System.out.println(graphicsPopup.getClipBounds()); float popupAlpha = 0.4f; if (!actionModel.isEnabled()) popupAlpha = 1.0f; graphicsPopup.setComposite(AlphaComposite.SrcOver .derive(popupAlpha)); this.paintButtonBackground(graphicsPopup, toFill, popupModel); graphicsPopup.dispose(); } } /** * Paints the background of the command button. * * @param graphics * Graphics context. * @param toFill * Rectangle to fill. * @param modelToUse * Button models to use for computing the background fill. */ protected void paintButtonBackground(Graphics graphics, Rectangle toFill, ButtonModel... modelToUse) { if (modelToUse.length == 0) return; if ((modelToUse.length == 1) && (modelToUse[0] == null)) return; this.buttonRendererPane.setBounds(toFill.x, toFill.y, toFill.width, toFill.height); this.rendererButton.setRolloverEnabled(true); boolean isEnabled = true; boolean isRollover = false; boolean isPressed = true; boolean isArmed = true; boolean isSelected = true; for (ButtonModel model : modelToUse) { if (model == null) continue; isEnabled = isEnabled && model.isEnabled(); isRollover = isRollover || model.isRollover(); isPressed = isPressed && model.isPressed(); isArmed = isArmed && model.isArmed(); isSelected = isSelected && model.isSelected(); if (model instanceof PopupButtonModel) { isRollover = isRollover || ((PopupButtonModel) model).isPopupShowing(); } } this.rendererButton.getModel().setEnabled(isEnabled); this.rendererButton.getModel().setRollover(isRollover); this.rendererButton.getModel().setPressed(isPressed); this.rendererButton.getModel().setArmed(isArmed); this.rendererButton.getModel().setSelected(isSelected); // System.out.println(this.commandButton.getText() + " - e:" // + this.rendererButton.getModel().isEnabled() + ", s:" // + this.rendererButton.getModel().isSelected() + ", r:" // + this.rendererButton.getModel().isRollover() + ", p:" // + this.rendererButton.getModel().isPressed() + ", a:" // + this.rendererButton.getModel().isArmed()); Graphics2D g2d = (Graphics2D) graphics.create(); Color borderColor = FlamingoUtilities.getBorderColor(); if (Boolean.TRUE.equals(this.commandButton .getClientProperty(EMULATE_SQUARE_BUTTON))) { this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - toFill.width / 2, toFill.y - toFill.height / 2, 2 * toFill.width, 2 * toFill.height, true); g2d.setColor(borderColor); g2d.drawRect(toFill.x, toFill.y, toFill.width - 1, toFill.height - 1); } else { AbstractCommandButton.CommandButtonLocationOrderKind locationKind = this.commandButton .getLocationOrderKind(); Insets outsets = (this.rendererButton instanceof JToggleButton) ? ButtonSizingUtils .getInstance().getToggleOutsets() : ButtonSizingUtils.getInstance().getOutsets(); if (locationKind != null) { if (locationKind == AbstractCommandButton.CommandButtonLocationOrderKind.ONLY) { this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - outsets.left, toFill.y - outsets.top, toFill.width + outsets.left + outsets.right, toFill.height + outsets.top + outsets.bottom, true); } else { // special case for parent component which is a vertical // button strip Component parent = this.commandButton.getParent(); if ((parent instanceof JCommandButtonStrip) && (((JCommandButtonStrip) parent).getOrientation() == StripOrientation.VERTICAL)) { switch (locationKind) { case FIRST: this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - outsets.left, toFill.y - outsets.top, toFill.width + outsets.left + outsets.right, 2 * toFill.height, true); g2d.setColor(borderColor); g2d.drawLine(toFill.x + 1, toFill.y + toFill.height - 1, toFill.x + toFill.width - 2, toFill.y + toFill.height - 1); break; case LAST: this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - outsets.left, toFill.y - toFill.height, toFill.width + outsets.left + outsets.right, 2 * toFill.height + outsets.bottom, true); break; case MIDDLE: this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - outsets.left, toFill.y - toFill.height, toFill.width + outsets.left + outsets.right, 3 * toFill.height, true); g2d.setColor(borderColor); g2d.drawLine(toFill.x + 1, toFill.y + toFill.height - 1, toFill.x + toFill.width - 2, toFill.y + toFill.height - 1); } } else { // horizontal boolean ltr = this.commandButton .getComponentOrientation().isLeftToRight(); if (locationKind == AbstractCommandButton.CommandButtonLocationOrderKind.MIDDLE) { this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - toFill.width, toFill.y - outsets.top, 3 * toFill.width, toFill.height + outsets.top + outsets.bottom, true); g2d.setColor(borderColor); g2d.drawLine(toFill.x + toFill.width - 1, toFill.y + 1, toFill.x + toFill.width - 1, toFill.y + toFill.height - 2); } else { boolean curveOnLeft = (ltr && (locationKind == AbstractCommandButton.CommandButtonLocationOrderKind.FIRST)) || (!ltr && (locationKind == AbstractCommandButton.CommandButtonLocationOrderKind.LAST)); if (curveOnLeft) { this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - outsets.left, toFill.y - outsets.top, 2 * toFill.width, toFill.height + outsets.top + outsets.bottom, true); g2d.setColor(borderColor); g2d.drawLine(toFill.x + toFill.width - 1, toFill.y + 1, toFill.x + toFill.width - 1, toFill.y + toFill.height - 2); } else { this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - toFill.width, toFill.y - outsets.top, 2 * toFill.width + outsets.right, toFill.height + outsets.top + outsets.bottom, true); } } } } } else { this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - outsets.left, toFill.y - outsets.top, toFill.width + outsets.left + outsets.right, toFill.height + outsets.top + outsets.bottom, true); } } g2d.dispose(); } /** * Updates the custom dimension. */ protected void updateCustomDimension() { int dimension = this.commandButton.getCustomDimension(); if (dimension > 0) { this.commandButton.getIcon().setDimension( new Dimension(dimension, dimension)); this.commandButton .setDisplayState(CommandButtonDisplayState.FIT_TO_ICON); this.commandButton.invalidate(); this.commandButton.revalidate(); this.commandButton.doLayout(); this.commandButton.repaint(); } } /** * Updates the popup action icon. */ protected void updatePopupActionIcon() { JCommandButton button = (JCommandButton) this.commandButton; if (button.getCommandButtonKind().hasPopup()) { this.popupActionIcon = this.createPopupActionIcon(); } else { this.popupActionIcon = null; } } /** * Paints the button icon. * * @param g * Graphics context. * @param iconRect * Icon rectangle. */ protected void paintButtonIcon(Graphics g, Rectangle iconRect) { Icon iconToPaint = this.getIconToPaint(); if ((iconRect == null) || (iconToPaint == null) || (iconRect.width == 0) || (iconRect.height == 0)) { return; } iconToPaint.paintIcon(this.commandButton, g, iconRect.x, iconRect.y); } /* * (non-Javadoc) * * @see * javax.swing.plaf.ComponentUI#getPreferredSize(javax.swing.JComponent) */ @Override public Dimension getPreferredSize(JComponent c) { AbstractCommandButton button = (AbstractCommandButton) c; return this.layoutManager.getPreferredSize(button); } @Override public CommandButtonLayoutInfo getLayoutInfo() { if (this.layoutInfo != null) { return this.layoutInfo; } this.layoutInfo = this.layoutManager.getLayoutInfo(commandButton, this.commandButton.getGraphics()); return this.layoutInfo; } /** * Returns the layout gap for the visuals of the associated command button. * * @return The layout gap for the visuals of the associated command button. */ protected int getLayoutGap() { Font font = this.commandButton.getFont(); if (font == null) font = UIManager.getFont("Button.font"); return (font.getSize() - 4) / 4; } /** * Returns indication whether the action-popup areas separator is painted. * * @return true if the action-popup areas separator is painted. */ protected boolean isPaintingSeparators() { PopupButtonModel popupModel = (this.commandButton instanceof JCommandButton) ? ((JCommandButton) this.commandButton) .getPopupModel() : null; boolean isActionRollover = this.commandButton.getActionModel() .isRollover(); boolean isPopupRollover = (popupModel != null) && popupModel.isRollover(); // Rectangle actionArea = this.getActionClickArea(); // Rectangle popupArea = this.getPopupClickArea(); // boolean hasNonEmptyAreas = (actionArea.width * actionArea.height // * popupArea.width * popupArea.height > 0); return // hasNonEmptyAreas && (isActionRollover || isPopupRollover); } /** * Returns indication whether the button background is painted. * * @return true if the button background is painted. */ protected boolean isPaintingBackground() { PopupButtonModel popupModel = (this.commandButton instanceof JCommandButton) ? ((JCommandButton) this.commandButton) .getPopupModel() : null; boolean isActionSelected = this.commandButton.getActionModel() .isSelected(); boolean isPopupSelected = (popupModel != null) && popupModel.isSelected(); boolean isActionRollover = this.commandButton.getActionModel() .isRollover(); boolean isPopupRollover = (popupModel != null) && popupModel.isRollover(); boolean isPopupShowing = (popupModel != null) && (popupModel.isPopupShowing()); boolean isActionArmed = this.commandButton.getActionModel().isArmed(); boolean isPopupArmed = (popupModel != null) && (popupModel.isArmed()); return (isActionSelected || isPopupSelected || isActionRollover || isPopupRollover || isPopupShowing || isActionArmed || isPopupArmed || !this.commandButton.isFlat()); } /** * Creates the popup action listener for this command button. * * @return Popup action listener for this command button. */ protected PopupActionListener createPopupActionListener() { return new PopupActionListener() { @Override public void actionPerformed(ActionEvent e) { processPopupAction(); } }; } protected void processPopupAction() { boolean wasPopupShowing = false; if (this.commandButton instanceof JCommandButton) { wasPopupShowing = ((JCommandButton) this.commandButton) .getPopupModel().isPopupShowing(); } // dismiss all the popups that are currently showing // up until button. PopupPanelManager.defaultManager().hidePopups(commandButton); if (!(commandButton instanceof JCommandButton)) return; if (wasPopupShowing) return; JCommandButton jcb = (JCommandButton) this.commandButton; // check if the command button has an associated popup // panel PopupPanelCallback popupCallback = jcb.getPopupCallback(); final JPopupPanel popupPanel = (popupCallback != null) ? popupCallback .getPopupPanel(jcb) : null; if (popupPanel != null) { popupPanel.applyComponentOrientation(jcb.getComponentOrientation()); SwingUtilities.invokeLater(new Runnable() { public void run() { if ((commandButton == null) || (popupPanel == null)) return; if (!commandButton.isShowing()) return; popupPanel.doLayout(); int x = 0; int y = 0; JPopupPanel.PopupPanelCustomizer customizer = popupPanel .getCustomizer(); boolean ltr = commandButton.getComponentOrientation() .isLeftToRight(); if (customizer == null) { switch (((JCommandButton) commandButton) .getPopupOrientationKind()) { case DOWNWARD: if (ltr) { x = commandButton.getLocationOnScreen().x; } else { x = commandButton.getLocationOnScreen().x + commandButton.getWidth() - popupPanel.getPreferredSize().width; } y = commandButton.getLocationOnScreen().y + commandButton.getSize().height; break; case SIDEWARD: if (ltr) { x = commandButton.getLocationOnScreen().x + commandButton.getWidth(); } else { x = commandButton.getLocationOnScreen().x - popupPanel.getPreferredSize().width; } y = commandButton.getLocationOnScreen().y + getLayoutInfo().popupClickArea.y; break; } } else { Rectangle placementRect = customizer.getScreenBounds(); // System.out.println(placementRect); x = placementRect.x; y = placementRect.y; } // make sure that the popup stays in bounds Rectangle scrBounds = commandButton .getGraphicsConfiguration().getBounds(); int pw = popupPanel.getPreferredSize().width; if ((x + pw) > (scrBounds.x + scrBounds.width)) { x = scrBounds.x + scrBounds.width - pw; } int ph = popupPanel.getPreferredSize().height; if ((y + ph) > (scrBounds.y + scrBounds.height)) { y = scrBounds.y + scrBounds.height - ph; } // get the popup and show it if (customizer != null) { Rectangle placementRect = customizer.getScreenBounds(); popupPanel.setPreferredSize(new Dimension( placementRect.width, placementRect.height)); } Popup popup = PopupFactory.getSharedInstance().getPopup( commandButton, popupPanel, x, y); // System.out.println("Showing the popup panel"); PopupPanelManager.defaultManager().addPopup(commandButton, popup, popupPanel); } }); return; } } protected void syncDisabledIcon() { ResizableIcon currDisabledIcon = this.commandButton.getDisabledIcon(); ResizableIcon icon = this.commandButton.getIcon(); if ((currDisabledIcon == null) || (currDisabledIcon instanceof UIResource)) { if (icon != null) { this.commandButton.setDisabledIcon(new ResizableIconUIResource( new FilteredResizableIcon(icon, new ColorConvertOp( ColorSpace.getInstance(ColorSpace.CS_GRAY), null)))); } else { this.commandButton.setDisabledIcon(null); } } else { // disabled icon coming from app code if (icon != null) { this.commandButton.getDisabledIcon() .setDimension( new Dimension(icon.getIconWidth(), icon .getIconHeight())); } } } protected void syncIconDimension() { ResizableIcon icon = this.commandButton.getIcon(); CommandButtonDisplayState commandButtonState = this.commandButton .getDisplayState(); this.layoutManager = commandButtonState .createLayoutManager(this.commandButton); if (icon == null) return; int maxHeight = layoutManager.getPreferredIconSize(); if (maxHeight < 0) { maxHeight = this.commandButton.getIcon().getIconHeight(); } if (commandButtonState != CommandButtonDisplayState.FIT_TO_ICON) { Dimension newDim = new Dimension(maxHeight, maxHeight); icon.setDimension(newDim); } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.ui.CommandButtonUI#getKeyTipAnchorCenterPoint() */ @Override public Point getKeyTipAnchorCenterPoint() { return this.layoutManager .getKeyTipAnchorCenterPoint(this.commandButton); } } src/org/pushingpixels/flamingo/internal/ui/common/BasicScrollablePanelUI.java0000644000175000017500000003741411401230444026526 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.common.JScrollablePanel; import org.pushingpixels.flamingo.api.common.JScrollablePanel.ScrollType; import org.pushingpixels.flamingo.internal.utils.DoubleArrowResizableIcon; /** * Basic UI for scrollable panel {@link JScrollablePanel}. * * @author Kirill Grouchnikov */ public class BasicScrollablePanelUI extends ScrollablePanelUI { /** * The associated scrollable panel. */ protected JScrollablePanel scrollablePanel; private JPanel viewport; private JCommandButton leadingScroller; private JCommandButton trailingScroller; private int viewOffset; private MouseWheelListener mouseWheelListener; private PropertyChangeListener propertyChangeListener; private ComponentListener componentListener; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicScrollablePanelUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.scrollablePanel = (JScrollablePanel) c; super.installUI(this.scrollablePanel); installDefaults(); installComponents(); installListeners(); } protected void installListeners() { this.mouseWheelListener = new MouseWheelListener() { @Override public void mouseWheelMoved(MouseWheelEvent e) { if (scrollablePanel.getScrollType() != JScrollablePanel.ScrollType.VERTICALLY) { return; } int scrollAmount = 8 * e.getScrollAmount() * e.getWheelRotation(); viewOffset += scrollAmount; syncScrolling(); } }; this.scrollablePanel.addMouseWheelListener(this.mouseWheelListener); this.propertyChangeListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("scrollOnRollover".equals(evt.getPropertyName())) { boolean isScrollOnRollover = (Boolean) evt.getNewValue(); leadingScroller.setFireActionOnRollover(isScrollOnRollover); trailingScroller .setFireActionOnRollover(isScrollOnRollover); } } }; this.scrollablePanel .addPropertyChangeListener(this.propertyChangeListener); if (this.scrollablePanel.getView() != null) { this.componentListener = new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { scrollablePanel.doLayout(); } }; this.scrollablePanel.getView().addComponentListener( this.componentListener); } } protected void installComponents() { this.viewport = new JPanel(new LayoutManager() { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension preferredLayoutSize(Container parent) { return new Dimension(10, 10); } @Override public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); } @Override public void layoutContainer(Container parent) { JComponent view = scrollablePanel.getView(); if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) { int viewWidth = view.getPreferredSize().width; int availWidth = parent.getWidth(); int offsetX = -viewOffset; view.setBounds(offsetX, 0, Math.max(viewWidth, availWidth), parent.getHeight()); } else { int viewHeight = view.getPreferredSize().height; int availHeight = parent.getHeight(); int offsetY = -viewOffset; view.setBounds(0, offsetY, parent.getWidth(), Math.max( viewHeight, availHeight)); } } }); JComponent view = scrollablePanel.getView(); if (view != null) { this.viewport.add(view); } this.scrollablePanel.add(this.viewport); this.leadingScroller = this.createLeadingScroller(); this.configureLeftScrollerButtonAction(); this.scrollablePanel.add(this.leadingScroller); this.trailingScroller = this.createTrailingScroller(); this.configureRightScrollerButtonAction(); this.scrollablePanel.add(this.trailingScroller); } protected void installDefaults() { this.scrollablePanel.setLayout(new ScrollablePanelLayout()); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); super.uninstallUI(this.scrollablePanel); } protected void uninstallDefaults() { } protected void uninstallComponents() { this.scrollablePanel.remove(this.viewport); this.scrollablePanel.remove(this.leadingScroller); this.scrollablePanel.remove(this.trailingScroller); } protected void uninstallListeners() { this.scrollablePanel .removePropertyChangeListener(this.propertyChangeListener); this.propertyChangeListener = null; this.scrollablePanel.removeMouseWheelListener(this.mouseWheelListener); this.mouseWheelListener = null; if (this.scrollablePanel.getView() != null) { this.scrollablePanel.getView().removeComponentListener( this.componentListener); this.componentListener = null; } } protected JCommandButton createLeadingScroller() { JCommandButton b = new JCommandButton( null, new DoubleArrowResizableIcon( new Dimension(9, 9), this.scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY ? SwingConstants.WEST : SwingConstants.NORTH)); b.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); b.setFocusable(false); b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); b.putClientProperty(BasicCommandButtonUI.EMULATE_SQUARE_BUTTON, Boolean.TRUE); b.putClientProperty(BasicCommandButtonUI.DONT_DISPOSE_POPUPS, Boolean.TRUE); return b; } protected JCommandButton createTrailingScroller() { JCommandButton b = new JCommandButton( null, new DoubleArrowResizableIcon( new Dimension(9, 9), this.scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY ? SwingConstants.EAST : SwingConstants.SOUTH)); b.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); b.setFocusable(false); b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); b.putClientProperty(BasicCommandButtonUI.EMULATE_SQUARE_BUTTON, Boolean.TRUE); b.putClientProperty(BasicCommandButtonUI.DONT_DISPOSE_POPUPS, Boolean.TRUE); return b; } private void syncScrolling() { this.scrollablePanel.doLayout(); } public void removeScrollers() { if (this.leadingScroller.getParent() == this.scrollablePanel) { this.scrollablePanel.remove(this.leadingScroller); this.scrollablePanel.remove(this.trailingScroller); syncScrolling(); this.scrollablePanel.revalidate(); this.scrollablePanel.repaint(); } } private void addScrollers() { this.scrollablePanel.add(this.leadingScroller); this.scrollablePanel.add(this.trailingScroller); this.scrollablePanel.revalidate(); JComponent view = this.scrollablePanel.getView(); view.setPreferredSize(view.getMinimumSize()); view.setSize(view.getMinimumSize()); this.scrollablePanel.doLayout(); this.scrollablePanel.repaint(); } protected void configureLeftScrollerButtonAction() { this.leadingScroller.setAutoRepeatAction(true); this.leadingScroller.setAutoRepeatActionIntervals(200, 50); this.leadingScroller.setFireActionOnRollover(this.scrollablePanel .isScrollOnRollover()); this.leadingScroller.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { viewOffset -= 12; syncScrolling(); } }); } protected void configureRightScrollerButtonAction() { this.trailingScroller.setAutoRepeatAction(true); this.trailingScroller.setAutoRepeatActionIntervals(200, 50); this.trailingScroller.setFireActionOnRollover(this.scrollablePanel .isScrollOnRollover()); this.trailingScroller.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { viewOffset += 12; syncScrolling(); } }); } @Override public void scrollToIfNecessary(int startPosition, int span) { if (this.scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) { if (this.scrollablePanel.getComponentOrientation().isLeftToRight()) { revealRightEdge(startPosition, span); revealLeftEdge(startPosition); } else { revealLeftEdge(startPosition); revealRightEdge(startPosition, span); } } else { revealBottomEdge(startPosition, span); revealTopEdge(startPosition); } } private void revealLeftEdge(int x) { if (x < viewOffset) { // left edge is not visible viewOffset = x - 5; syncScrolling(); } } private void revealRightEdge(int x, int width) { if ((x + width) > (viewOffset + viewport.getWidth())) { // right edge is not visible viewOffset = x + width - viewport.getWidth() + 5; syncScrolling(); } } private void revealTopEdge(int y) { if (y < viewOffset) { // top edge is not visible viewOffset = y - 5; syncScrolling(); } } private void revealBottomEdge(int y, int height) { if ((y + height) > (viewOffset + viewport.getHeight())) { // bottom edge is not visible viewOffset = y + height - viewport.getHeight() + 5; syncScrolling(); } } @Override public boolean isShowingScrollButtons() { return (this.leadingScroller.isVisible()); } /** * Layout for the scrollable panel. * * @author Kirill Grouchnikov * @author Topologi */ protected class ScrollablePanelLayout implements LayoutManager { /** * Creates new layout manager. */ public ScrollablePanelLayout() { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) { return new Dimension(c.getWidth(), 21); } else { return new Dimension(21, c.getHeight()); } } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) { return new Dimension(10, 21); } else { return new Dimension(21, 10); } } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { int width = c.getWidth(); int height = c.getHeight(); Insets ins = c.getInsets(); JComponent view = scrollablePanel.getView(); Dimension viewPrefSize = view.getPreferredSize(); // System.out.println(width + "*" + height + " - " // + viewPrefSize.width + "*" + viewPrefSize.height); if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) { boolean shouldShowScrollerButtons = (viewPrefSize.width > width); leadingScroller.setVisible(shouldShowScrollerButtons); trailingScroller.setVisible(shouldShowScrollerButtons); int scrollPanelWidth = shouldShowScrollerButtons ? width - ins.left - ins.right - leadingScroller.getPreferredSize().width - trailingScroller.getPreferredSize().width - 4 : width - ins.left - ins.right; int x = ins.left; if (shouldShowScrollerButtons) { int spw = leadingScroller.getPreferredSize().width; leadingScroller.setBounds(x, ins.top, spw, height - ins.top - ins.bottom); x += spw + 2; } viewport.setBounds(x, ins.top, scrollPanelWidth, height - ins.top - ins.bottom); int viewPreferredWidth = view.getPreferredSize().width; if (viewOffset < 0) { viewOffset = 0; } if ((viewPreferredWidth > 0) && (viewOffset + scrollPanelWidth > viewPreferredWidth)) { viewOffset = Math.max(0, viewPreferredWidth - scrollPanelWidth); } viewport.doLayout(); x += scrollPanelWidth + 2; if (shouldShowScrollerButtons) { int spw = trailingScroller.getPreferredSize().width; trailingScroller.setBounds(x, ins.top, spw, height - ins.top - ins.bottom); } } else { boolean shouldShowScrollerButtons = (viewPrefSize.height > height); leadingScroller.setVisible(shouldShowScrollerButtons); trailingScroller.setVisible(shouldShowScrollerButtons); int scrollPanelHeight = shouldShowScrollerButtons ? height - ins.top - ins.bottom - leadingScroller.getPreferredSize().height - trailingScroller.getPreferredSize().height - 4 : height - ins.top - ins.bottom; int y = ins.top; if (shouldShowScrollerButtons) { int sph = leadingScroller.getPreferredSize().height; leadingScroller.setBounds(ins.left, y, width - ins.left - ins.right, sph); y += sph + 2; } viewport.setBounds(ins.left, y, width - ins.left - ins.right, scrollPanelHeight); int viewPreferredHeight = view.getPreferredSize().height; if (viewOffset < 0) { viewOffset = 0; } if ((viewPreferredHeight > 0) && (viewOffset + scrollPanelHeight > viewPreferredHeight)) { viewOffset = Math.max(0, viewPreferredHeight - scrollPanelHeight); } viewport.doLayout(); y += scrollPanelHeight + 2; if (shouldShowScrollerButtons) { int sph = trailingScroller.getPreferredSize().height; trailingScroller.setBounds(ins.left, y, width - ins.left - ins.right, sph); } } if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) { trailingScroller .setEnabled((viewOffset + viewport.getWidth()) < view .getWidth()); } else { trailingScroller .setEnabled((viewOffset + viewport.getHeight()) < view .getHeight()); } leadingScroller.setEnabled(viewOffset > 0); } } } src/org/pushingpixels/flamingo/internal/ui/common/CommandButtonLayoutManagerMedium.java0000644000175000017500000004133711427104506030676 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import javax.swing.JSeparator; import javax.swing.SwingConstants; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.internal.ui.common.popup.BasicCommandPopupMenuUI; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerMedium implements CommandButtonLayoutManager { @Override public int getPreferredIconSize() { return 16; } protected float getIconTextGapFactor() { return 1.0f; } private boolean hasIcon(AbstractCommandButton button) { if (button.getIcon() != null) return true; if (Boolean.TRUE.equals(button .getClientProperty(BasicCommandPopupMenuUI.FORCE_ICON))) return true; return false; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = commandButton.getInsets(); int by = borderInsets.top + borderInsets.bottom; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); String buttonText = commandButton.getText(); int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); boolean hasIcon = this.hasIcon(commandButton); boolean hasText = (buttonText != null); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); int prefIconSize = hasIcon ? this.getPreferredIconSize() : 0; // start with the left insets int width = borderInsets.left; // icon? if (hasIcon) { // padding before the icon width += layoutHGap; // icon width width += prefIconSize; // padding after the icon width += layoutHGap; } // text? if (hasText) { // padding before the text if (hasIcon) { width += (int) (layoutHGap * getIconTextGapFactor()); } else { width += layoutHGap; } // text width width += fm.stringWidth(buttonText); // padding after the text width += layoutHGap; } // popup icon? if (hasPopupIcon) { // padding before the popup icon if (hasText && hasIcon) { width += 2 * layoutHGap; } // text width width += 1 + fm.getHeight() / 2; // padding after the popup icon width += 2 * layoutHGap; } // separator? if (commandButton instanceof JCommandButton) { JCommandButton jcb = (JCommandButton) commandButton; CommandButtonKind buttonKind = jcb.getCommandButtonKind(); boolean hasSeparator = false; if (buttonKind == CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION && (hasIcon || hasText)) { hasSeparator = true; } if (buttonKind == CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP && hasIcon) { hasSeparator = true; } if (hasSeparator) { // space for a vertical separator width += new JSeparator(JSeparator.VERTICAL).getPreferredSize().width; } } // right insets width += borderInsets.right; // and remove the padding before the first and after the last elements width -= 2 * layoutHGap; return new Dimension(width, by + Math.max(prefIconSize, fm.getAscent() + fm.getDescent())); } @Override public void propertyChange(PropertyChangeEvent evt) { } @Override public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) { Insets ins = commandButton.getInsets(); int height = commandButton.getHeight(); boolean hasIcon = this.hasIcon(commandButton); int iconSize = this.getPreferredIconSize(); if (hasIcon) { // bottom-right corner of the icon area return new Point(ins.left + iconSize, (height + iconSize) / 2); } else { // bottom-left corner of the button return new Point(ins.left, 3 * height / 4); } } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, 0, 0); Insets ins = commandButton.getInsets(); result.iconRect = new Rectangle(); result.popupActionRect = new Rectangle(); int width = commandButton.getWidth(); int height = commandButton.getHeight(); String buttonText = commandButton.getText(); int iconSize = this.getPreferredIconSize(); boolean hasIcon = this.hasIcon(commandButton); boolean hasText = (buttonText != null); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); boolean ltr = commandButton.getComponentOrientation().isLeftToRight(); int prefWidth = this.getPreferredSize(commandButton).width; int shiftX = 0; if (commandButton.getHorizontalAlignment() == SwingConstants.CENTER) { if (width > prefWidth) { shiftX = (width - prefWidth) / 2; } } FontMetrics fm = g.getFontMetrics(); int labelHeight = fm.getAscent() + fm.getDescent(); JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton) .getCommandButtonKind() : JCommandButton.CommandButtonKind.ACTION_ONLY; int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); if (ltr) { int x = ins.left + shiftX - layoutHGap; // icon if (hasIcon) { x += layoutHGap; result.iconRect.x = x; result.iconRect.y = (height - iconSize) / 2; result.iconRect.width = iconSize; result.iconRect.height = iconSize; x += (iconSize + layoutHGap); } // text if (hasText) { if (hasIcon) { x += (int) (layoutHGap * getIconTextGapFactor()); } else { x += layoutHGap; } TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); lineLayoutInfo.textRect.x = x; lineLayoutInfo.textRect.y = (height - labelHeight) / 2; lineLayoutInfo.textRect.width = (int) fm.getStringBounds( buttonText, g).getWidth(); lineLayoutInfo.textRect.height = labelHeight; x += lineLayoutInfo.textRect.width; x += layoutHGap; } if (hasPopupIcon) { if (hasText && hasIcon) { x += 2 * layoutHGap; } result.popupActionRect.x = x; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.height = labelHeight + 2; x += result.popupActionRect.width; x += 2 * layoutHGap; } int xBorderBetweenActionAndPopup = 0; int verticalSeparatorWidth = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; // compute the action and popup click areas switch (buttonKind) { case ACTION_ONLY: result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; break; case POPUP_ONLY: result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; break; case ACTION_AND_POPUP_MAIN_ACTION: // 1. break before popup icon if button has text or icon // 2. no break (all popup) if button has no text and no icon if (hasText || hasIcon) { // shift popup action rectangle to the right to // accomodate the vertical separator result.popupActionRect.x += verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.popupActionRect.x - 2 * layoutHGap; result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = xBorderBetweenActionAndPopup; result.popupClickArea.y = 0; result.popupClickArea.width = width - xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = true; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; } break; case ACTION_AND_POPUP_MAIN_POPUP: // 1. break after icon if button has icon // 2. no break (all popup) if button has no icon if (hasIcon) { // shift text rectangle and popup action rectangle to the // right // to accomodate the vertical separator if (result.textLayoutInfoList != null) { for (TextLayoutInfo textLayoutInfo : result.textLayoutInfoList) { textLayoutInfo.textRect.x += verticalSeparatorWidth; } } result.popupActionRect.x += verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.iconRect.x + result.iconRect.width + layoutHGap; result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = xBorderBetweenActionAndPopup; result.popupClickArea.y = 0; result.popupClickArea.width = width - xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = false; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = true; } break; } } else { int x = width - ins.right - shiftX + layoutHGap; // icon if (hasIcon) { x -= layoutHGap; result.iconRect.x = x - iconSize; result.iconRect.y = (height - iconSize) / 2; result.iconRect.width = iconSize; result.iconRect.height = iconSize; x -= (iconSize + layoutHGap); } // text if (hasText) { if (hasIcon) { x -= (int) (layoutHGap * getIconTextGapFactor()); } else { x -= layoutHGap; } TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); lineLayoutInfo.textRect.width = (int) fm.getStringBounds( buttonText, g).getWidth(); lineLayoutInfo.textRect.x = x - lineLayoutInfo.textRect.width; lineLayoutInfo.textRect.y = (height - labelHeight) / 2; lineLayoutInfo.textRect.height = labelHeight; x -= lineLayoutInfo.textRect.width; x -= layoutHGap; } if (hasPopupIcon) { if (hasText && hasIcon) { x -= 2 * layoutHGap; } result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.x = x - result.popupActionRect.width; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.height = labelHeight + 2; x -= result.popupActionRect.width; x -= 2 * layoutHGap; } int xBorderBetweenActionAndPopup = 0; int verticalSeparatorWidth = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; // compute the action and popup click areas switch (buttonKind) { case ACTION_ONLY: result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; break; case POPUP_ONLY: result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; break; case ACTION_AND_POPUP_MAIN_ACTION: // 1. break before popup icon if button has text or icon // 2. no break (all popup) if button has no text and no icon if (hasText || hasIcon) { // shift popup action rectangle to the left to // accomodate the vertical separator result.popupActionRect.x -= verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.popupActionRect.x + result.popupActionRect.width + 2 * layoutHGap; result.actionClickArea.x = xBorderBetweenActionAndPopup; result.actionClickArea.y = 0; result.actionClickArea.width = width - xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = true; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; } break; case ACTION_AND_POPUP_MAIN_POPUP: // 1. break after icon if button has icon // 2. no break (all popup) if button has no icon if (hasIcon) { // shift text rectangle and popup action rectangle to the // left to accomodate the vertical separator if (result.textLayoutInfoList != null) { for (TextLayoutInfo textLayoutInfo : result.textLayoutInfoList) { textLayoutInfo.textRect.x -= verticalSeparatorWidth; } } result.popupActionRect.x -= verticalSeparatorWidth; xBorderBetweenActionAndPopup = result.iconRect.x - layoutHGap; result.actionClickArea.x = xBorderBetweenActionAndPopup; result.actionClickArea.y = 0; result.actionClickArea.width = width - xBorderBetweenActionAndPopup; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = xBorderBetweenActionAndPopup; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = xBorderBetweenActionAndPopup; result.separatorArea.y = 0; result.separatorArea.width = verticalSeparatorWidth; result.separatorArea.height = height; result.isTextInActionArea = false; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = true; } break; } } return result; } } src/org/pushingpixels/flamingo/internal/ui/common/ResizableIconUIResource.java0000644000175000017500000000447711401230444026766 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import javax.swing.plaf.UIResource; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; public class ResizableIconUIResource implements ResizableIcon, UIResource { private ResizableIcon delegate; public ResizableIconUIResource(ResizableIcon delegate) { this.delegate = delegate; } public int getIconHeight() { return delegate.getIconHeight(); } public int getIconWidth() { return delegate.getIconWidth(); } public void paintIcon(Component c, Graphics g, int x, int y) { delegate.paintIcon(c, g, x, y); } public void setDimension(Dimension newDimension) { delegate.setDimension(newDimension); } } src/org/pushingpixels/flamingo/internal/ui/common/JRichTooltipPanel.java0000644000175000017500000000556111401230444025614 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import javax.swing.JPanel; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.RichTooltip; public class JRichTooltipPanel extends JPanel { protected RichTooltip tooltipInfo; /** * @see #getUIClassID */ public static final String uiClassID = "RichTooltipPanelUI"; public JRichTooltipPanel(RichTooltip tooltipInfo) { this.tooltipInfo = tooltipInfo; } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUI() */ @Override public RichTooltipPanelUI getUI() { return (RichTooltipPanelUI) ui; } /** * Sets the look and feel (L&F) object that renders this component. * * @param ui * The UI delegate. */ protected void setUI(RichTooltipPanelUI ui) { super.setUI(ui); } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /* * (non-Javadoc) * * @see javax.swing.JPanel#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((RichTooltipPanelUI) UIManager.getUI(this)); } else { setUI(BasicRichTooltipPanelUI.createUI(this)); } } public RichTooltip getTooltipInfo() { return tooltipInfo; } } src/org/pushingpixels/flamingo/internal/ui/common/BasicCommandToggleButtonUI.java0000644000175000017500000000473511401230444027400 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import javax.swing.*; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.JCommandToggleButton; /** * Basic UI for command toggle button {@link JCommandToggleButton}. * * @author Kirill Grouchnikov */ public class BasicCommandToggleButtonUI extends BasicCommandButtonUI { /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicCommandToggleButtonUI(); } /** * Creates a new UI delegate. */ public BasicCommandToggleButtonUI() { } @Override protected void updatePopupActionIcon() { } @Override protected boolean isPaintingSeparators() { return false; } @Override protected AbstractButton createRendererButton() { return new JToggleButton(""); } } src/org/pushingpixels/flamingo/internal/ui/common/BasicRichTooltipPanelUI.java0000644000175000017500000004317711401230444026707 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.awt.font.*; import java.awt.geom.AffineTransform; import java.text.AttributedString; import java.util.ArrayList; import javax.swing.*; import javax.swing.border.*; import javax.swing.plaf.*; import org.pushingpixels.flamingo.api.common.RichTooltip; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Basic UI for rich tooltip panel {@link JRichTooltipPanel}. * * @author Kirill Grouchnikov */ public class BasicRichTooltipPanelUI extends RichTooltipPanelUI { /** * The associated tooltip panel. */ protected JRichTooltipPanel richTooltipPanel; protected java.util.List titleLabels; protected java.util.List descriptionLabels; protected JLabel mainImageLabel; protected JSeparator footerSeparator; protected JLabel footerImageLabel; protected java.util.List footerLabels; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicRichTooltipPanelUI(); } public BasicRichTooltipPanelUI() { this.titleLabels = new ArrayList(); this.descriptionLabels = new ArrayList(); this.footerLabels = new ArrayList(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.richTooltipPanel = (JRichTooltipPanel) c; super.installUI(this.richTooltipPanel); installDefaults(); installComponents(); installListeners(); this.richTooltipPanel.setLayout(createLayoutManager()); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); super.uninstallUI(this.richTooltipPanel); } /** * Installs default settings for the associated rich tooltip panel. */ protected void installDefaults() { Border b = this.richTooltipPanel.getBorder(); if (b == null || b instanceof UIResource) { Border toSet = UIManager.getBorder("RichTooltipPanel.border"); if (toSet == null) toSet = new BorderUIResource.CompoundBorderUIResource( new LineBorder(FlamingoUtilities.getBorderColor()), new EmptyBorder(2, 4, 3, 4)); this.richTooltipPanel.setBorder(toSet); } LookAndFeel.installProperty(this.richTooltipPanel, "opaque", Boolean.TRUE); } /** * Installs listeners on the associated rich tooltip panel. */ protected void installListeners() { } /** * Installs components on the associated rich tooltip panel. */ protected void installComponents() { } /** * Uninstalls default settings from the associated rich tooltip panel. */ protected void uninstallDefaults() { LookAndFeel.uninstallBorder(this.richTooltipPanel); } /** * Uninstalls listeners from the associated rich tooltip panel. */ protected void uninstallListeners() { } /** * Uninstalls subcomponents from the associated rich tooltip panel. */ protected void uninstallComponents() { this.removeExistingComponents(); } @Override public void update(Graphics g, JComponent c) { this.paintBackground(g); this.paint(g, c); } protected void paintBackground(Graphics g) { Color main = FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground").brighter(); Graphics2D g2d = (Graphics2D) g.create(); g2d.setPaint(new GradientPaint(0, 0, FlamingoUtilities.getLighterColor( main, 0.9), 0, this.richTooltipPanel.getHeight(), FlamingoUtilities.getLighterColor(main, 0.4))); g2d.fillRect(0, 0, this.richTooltipPanel.getWidth(), this.richTooltipPanel.getHeight()); g2d.setFont(FlamingoUtilities.getFont(this.richTooltipPanel, "Ribbon.font", "Button.font", "Panel.font")); g2d.dispose(); } @Override public void paint(Graphics g, JComponent c) { } protected LayoutManager createLayoutManager() { return new RichTooltipPanelLayout(); } protected class RichTooltipPanelLayout implements LayoutManager { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension minimumLayoutSize(Container parent) { return this.preferredLayoutSize(parent); } @Override public Dimension preferredLayoutSize(Container parent) { Insets ins = parent.getInsets(); int gap = getLayoutGap(); Font font = FlamingoUtilities.getFont(parent, "Ribbon.font", "Button.font", "Panel.font"); Font titleFont = font.deriveFont(Font.BOLD); // the main text gets 200 pixels. The width is defined // by this and the presence of the main text. // The height is defined based on the width and the // text broken into multiline paragraphs int descTextWidth = getDescriptionTextWidth(); int width = ins.left + 2 * gap + descTextWidth + ins.right; RichTooltip tooltipInfo = richTooltipPanel.getTooltipInfo(); FontRenderContext frc = new FontRenderContext( new AffineTransform(), true, false); if (tooltipInfo.getMainImage() != null) { width += tooltipInfo.getMainImage().getWidth(null); } int fontHeight = parent.getFontMetrics(font).getHeight(); int height = ins.top; // The title label int titleTextHeight = 0; AttributedString titleAttributedDescription = new AttributedString( tooltipInfo.getTitle()); titleAttributedDescription.addAttribute(TextAttribute.FONT, titleFont); LineBreakMeasurer titleLineBreakMeasurer = new LineBreakMeasurer( titleAttributedDescription.getIterator(), frc); int maxTitleLineWidth = 0; while (true) { TextLayout tl = titleLineBreakMeasurer .nextLayout(descTextWidth); if (tl == null) break; titleTextHeight += fontHeight; int lineWidth = (int) Math.ceil(tl.getBounds().getWidth()); maxTitleLineWidth = Math.max(maxTitleLineWidth, lineWidth); } height += titleTextHeight; // The description text int descriptionTextHeight = 0; for (String descText : tooltipInfo.getDescriptionSections()) { AttributedString descAttributedDescription = new AttributedString( descText); descAttributedDescription .addAttribute(TextAttribute.FONT, font); LineBreakMeasurer descLineBreakMeasurer = new LineBreakMeasurer( descAttributedDescription.getIterator(), frc); while (true) { TextLayout tl = descLineBreakMeasurer .nextLayout(descTextWidth); if (tl == null) break; descriptionTextHeight += fontHeight; } // add an empty line after the paragraph descriptionTextHeight += fontHeight; } if (!tooltipInfo.getDescriptionSections().isEmpty()) { // remove the empty line after the last paragraph descriptionTextHeight -= fontHeight; // add gap between the title and the description descriptionTextHeight += gap; } if (tooltipInfo.getMainImage() != null) { height += Math.max(descriptionTextHeight, new JLabel( new ImageIcon(tooltipInfo.getMainImage())) .getPreferredSize().height); } else { height += descriptionTextHeight; } if ((tooltipInfo.getFooterImage() != null) || (tooltipInfo.getFooterSections().size() > 0)) { height += gap; // The footer separator height += new JSeparator(JSeparator.HORIZONTAL) .getPreferredSize().height; height += gap; int footerTextHeight = 0; int availableWidth = descTextWidth; if (tooltipInfo.getFooterImage() != null) { availableWidth -= tooltipInfo.getFooterImage().getWidth( null); } if (tooltipInfo.getMainImage() != null) { availableWidth += tooltipInfo.getMainImage().getWidth(null); } for (String footerText : tooltipInfo.getFooterSections()) { AttributedString footerAttributedDescription = new AttributedString( footerText); footerAttributedDescription.addAttribute( TextAttribute.FONT, font); LineBreakMeasurer footerLineBreakMeasurer = new LineBreakMeasurer( footerAttributedDescription.getIterator(), frc); while (true) { TextLayout tl = footerLineBreakMeasurer .nextLayout(availableWidth); if (tl == null) break; footerTextHeight += fontHeight; } // add an empty line after the paragraph footerTextHeight += fontHeight; } // remove the empty line after the last paragraph footerTextHeight -= fontHeight; if (tooltipInfo.getFooterImage() != null) { height += Math.max(footerTextHeight, new JLabel( new ImageIcon(tooltipInfo.getFooterImage())) .getPreferredSize().height); } else { height += footerTextHeight; } } height += ins.bottom; // special case for rich tooltips that only have titles if (tooltipInfo.getDescriptionSections().isEmpty() && (tooltipInfo.getMainImage() == null) && tooltipInfo.getFooterSections().isEmpty() && (tooltipInfo.getFooterImage() == null)) { width = maxTitleLineWidth + 1 + ins.left + ins.right; } return new Dimension(width, height); } @Override public void layoutContainer(Container parent) { removeExistingComponents(); Font font = FlamingoUtilities.getFont(parent, "Ribbon.font", "Button.font", "Panel.font"); Insets ins = richTooltipPanel.getInsets(); int y = ins.top; RichTooltip tooltipInfo = richTooltipPanel.getTooltipInfo(); FontRenderContext frc = new FontRenderContext( new AffineTransform(), true, false); int gap = getLayoutGap(); int fontHeight = parent.getFontMetrics(font).getHeight(); Font titleFont = font.deriveFont(Font.BOLD); boolean ltr = richTooltipPanel.getComponentOrientation() .isLeftToRight(); // The title label int titleLabelWidth = parent.getWidth() - ins.left - ins.right; AttributedString titleAtributedDescription = new AttributedString( tooltipInfo.getTitle()); titleAtributedDescription.addAttribute(TextAttribute.FONT, titleFont); LineBreakMeasurer titleLineBreakMeasurer = new LineBreakMeasurer( titleAtributedDescription.getIterator(), frc); int titleCurrOffset = 0; while (true) { TextLayout tl = titleLineBreakMeasurer .nextLayout(titleLabelWidth); if (tl == null) break; int charCount = tl.getCharacterCount(); String line = tooltipInfo.getTitle().substring(titleCurrOffset, titleCurrOffset + charCount); JLabel titleLabel = new JLabel(line); titleLabel.setFont(titleFont); titleLabels.add(titleLabel); richTooltipPanel.add(titleLabel); int currLabelWidth = titleLabel.getPreferredSize().width; if (ltr) { titleLabel.setBounds(ins.left, y, currLabelWidth, fontHeight); } else { titleLabel.setBounds(parent.getWidth() - ins.right - currLabelWidth, y, currLabelWidth, fontHeight); } y += titleLabel.getHeight(); titleCurrOffset += charCount; } y += gap; // The main image int x = ltr ? ins.left : parent.getWidth() - ins.right; if (tooltipInfo.getMainImage() != null) { mainImageLabel = new JLabel(new ImageIcon(tooltipInfo .getMainImage())); richTooltipPanel.add(mainImageLabel); int mainImageWidth = mainImageLabel.getPreferredSize().width; if (ltr) { mainImageLabel.setBounds(x, y, mainImageWidth, mainImageLabel.getPreferredSize().height); x += mainImageWidth; } else { mainImageLabel.setBounds(x - mainImageWidth, y, mainImageWidth, mainImageLabel.getPreferredSize().height); x -= mainImageWidth; } } if (ltr) { x += 2 * gap; } else { x -= 2 * gap; } // The description text int descLabelWidth = ltr ? parent.getWidth() - x - ins.right : x - ins.left; for (String descText : tooltipInfo.getDescriptionSections()) { AttributedString attributedDescription = new AttributedString( descText); attributedDescription.addAttribute(TextAttribute.FONT, font); LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer( attributedDescription.getIterator(), frc); int currOffset = 0; while (true) { TextLayout tl = lineBreakMeasurer .nextLayout(descLabelWidth); if (tl == null) break; int charCount = tl.getCharacterCount(); String line = descText.substring(currOffset, currOffset + charCount); JLabel descLabel = new JLabel(line); descriptionLabels.add(descLabel); richTooltipPanel.add(descLabel); int currDescWidth = descLabel.getPreferredSize().width; if (ltr) { descLabel.setBounds(x, y, currDescWidth, fontHeight); } else { descLabel.setBounds(x - currDescWidth, y, currDescWidth, fontHeight); } y += descLabel.getHeight(); currOffset += charCount; } // add an empty line after the paragraph y += fontHeight; } // remove the empty line after the last paragraph y -= fontHeight; if (mainImageLabel != null) { y = Math.max(y, mainImageLabel.getY() + mainImageLabel.getHeight()); } if ((tooltipInfo.getFooterImage() != null) || (tooltipInfo.getFooterSections().size() > 0)) { y += gap; // The footer separator footerSeparator = new JSeparator(JSeparator.HORIZONTAL); richTooltipPanel.add(footerSeparator); footerSeparator.setBounds(ins.left, y, parent.getWidth() - ins.left - ins.right, footerSeparator .getPreferredSize().height); y += footerSeparator.getHeight() + gap; // The footer image x = ltr ? ins.left : parent.getWidth() - ins.right; if (tooltipInfo.getFooterImage() != null) { footerImageLabel = new JLabel(new ImageIcon(tooltipInfo .getFooterImage())); richTooltipPanel.add(footerImageLabel); int footerImageWidth = footerImageLabel.getPreferredSize().width; if (ltr) { footerImageLabel.setBounds(x, y, footerImageWidth, footerImageLabel.getPreferredSize().height); x += footerImageWidth + 2 * gap; } else { footerImageLabel.setBounds(x - footerImageWidth, y, footerImageWidth, footerImageLabel .getPreferredSize().height); x -= (footerImageWidth + 2 * gap); } } // The footer text int footerLabelWidth = ltr ? parent.getWidth() - x - ins.right : x - ins.left; for (String footerText : tooltipInfo.getFooterSections()) { AttributedString attributedDescription = new AttributedString( footerText); attributedDescription .addAttribute(TextAttribute.FONT, font); LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer( attributedDescription.getIterator(), frc); int currOffset = 0; while (true) { TextLayout tl = lineBreakMeasurer .nextLayout(footerLabelWidth); if (tl == null) break; int charCount = tl.getCharacterCount(); String line = footerText.substring(currOffset, currOffset + charCount); JLabel footerLabel = new JLabel(line); footerLabels.add(footerLabel); richTooltipPanel.add(footerLabel); int currLabelWidth = footerLabel.getPreferredSize().width; if (ltr) { footerLabel.setBounds(x, y, currLabelWidth, fontHeight); } else { footerLabel.setBounds(x - currLabelWidth, y, currLabelWidth, fontHeight); } y += footerLabel.getHeight(); currOffset += charCount; } // add an empty line after the paragraph y += fontHeight; } // remove the empty line after the last paragraph y -= fontHeight; } } } protected int getDescriptionTextWidth() { return 200; } protected int getLayoutGap() { return 4; } protected void removeExistingComponents() { for (JLabel label : this.titleLabels) this.richTooltipPanel.remove(label); if (this.mainImageLabel != null) { this.richTooltipPanel.remove(this.mainImageLabel); } for (JLabel label : this.descriptionLabels) this.richTooltipPanel.remove(label); if (this.footerSeparator != null) { this.richTooltipPanel.remove(this.footerSeparator); } if (this.footerImageLabel != null) { this.richTooltipPanel.remove(this.footerImageLabel); } for (JLabel label : this.footerLabels) this.richTooltipPanel.remove(label); } } src/org/pushingpixels/flamingo/internal/ui/common/BasicCommandMenuButtonUI.java0000644000175000017500000001062611401230444027057 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.awt.event.*; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.JCommandMenuButton; import org.pushingpixels.flamingo.api.common.RolloverActionListener; import org.pushingpixels.flamingo.internal.utils.KeyTipRenderingUtilities; /** * Basic UI delegate for the {@link JCommandMenuButton} component. * * @author Kirill Grouchnikov */ public class BasicCommandMenuButtonUI extends BasicCommandButtonUI { /** * Rollover menu mouse listener. */ protected MouseListener rolloverMenuMouseListener; public static ComponentUI createUI(JComponent c) { return new BasicCommandMenuButtonUI(); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.ui.BasicCommandButtonUI#installListeners() */ @Override protected void installListeners() { super.installListeners(); this.rolloverMenuMouseListener = new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { if (commandButton.isEnabled()) { int modifiers = 0; AWTEvent currentEvent = EventQueue.getCurrentEvent(); if (currentEvent instanceof InputEvent) { modifiers = ((InputEvent) currentEvent).getModifiers(); } else if (currentEvent instanceof ActionEvent) { modifiers = ((ActionEvent) currentEvent).getModifiers(); } fireRolloverActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, commandButton .getActionModel().getActionCommand(), EventQueue.getMostRecentEventTime(), modifiers)); processPopupAction(); } } }; this.commandButton.addMouseListener(this.rolloverMenuMouseListener); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.ui.BasicCommandButtonUI#uninstallListeners() */ @Override protected void uninstallListeners() { this.commandButton.removeMouseListener(this.rolloverMenuMouseListener); this.rolloverMenuMouseListener = null; super.uninstallListeners(); } /** * Fires the rollover action on all registered handlers. * * @param e * Event object. */ protected void fireRolloverActionPerformed(ActionEvent e) { // Guaranteed to return a non-null array RolloverActionListener[] listeners = commandButton .getListeners(RolloverActionListener.class); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 1; i >= 0; i--) { (listeners[i]).actionPerformed(e); } } @Override public void update(Graphics g, JComponent c) { JCommandMenuButton menuButton = (JCommandMenuButton) c; super.update(g, c); // System.out.println("Updating " + menuButton.getText()); KeyTipRenderingUtilities.renderMenuButtonKeyTips(g, menuButton, layoutManager); } } src/org/pushingpixels/flamingo/internal/ui/common/ScrollablePanelUI.java0000644000175000017500000000403611401230444025556 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import javax.swing.plaf.PanelUI; import org.pushingpixels.flamingo.api.common.JScrollablePanel; /** * UI for scrollable panel ({@link JScrollablePanel}). * * @author Kirill Grouchnikov */ public abstract class ScrollablePanelUI extends PanelUI { public abstract void scrollToIfNecessary(int startPosition, int span); public abstract boolean isShowingScrollButtons(); } src/org/pushingpixels/flamingo/internal/ui/common/CommandButtonStripUI.java0000644000175000017500000000365711401230444026320 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.JCommandButtonStrip; /** * UI for button strip ({@link JCommandButtonStrip}). * * @author Kirill Grouchnikov */ public abstract class CommandButtonStripUI extends ComponentUI { } src/org/pushingpixels/flamingo/internal/ui/common/BasicCommandButtonPanelUI.java0000644000175000017500000006214511401230444027215 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import org.pushingpixels.flamingo.api.common.AbstractCommandButton; import org.pushingpixels.flamingo.api.common.JCommandButtonPanel; import org.pushingpixels.flamingo.api.common.JCommandButtonPanel.LayoutKind; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Basic UI for command button panel {@link JCommandButtonPanel}. * * @author Kirill Grouchnikov */ public class BasicCommandButtonPanelUI extends CommandButtonPanelUI { /** * The associated command button panel. */ protected JCommandButtonPanel buttonPanel; /** * Labels of the button panel groups. */ protected JLabel[] groupLabels; /** * Bounds of button panel groups. */ protected Rectangle[] groupRects; /** * Property change listener on {@link #buttonPanel}. */ protected PropertyChangeListener propertyChangeListener; /** * Change listener on {@link #buttonPanel}. */ protected ChangeListener changeListener; /** * Default insets of button panel groups. */ protected static final Insets GROUP_INSETS = new Insets(4, 4, 4, 4); /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicCommandButtonPanelUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.buttonPanel = (JCommandButtonPanel) c; installDefaults(); installComponents(); installListeners(); } /** * Installs defaults on the associated button panel. */ protected void installDefaults() { this.buttonPanel.setLayout(this.createLayoutManager()); Font currFont = this.buttonPanel.getFont(); if ((currFont == null) || (currFont instanceof UIResource)) { Font titleFont = FlamingoUtilities.getFont(null, "CommandButtonPanel.font", "Button.font", "Panel.font"); this.buttonPanel.setFont(titleFont); } } /** * Installs sub-components on the associated button panel. */ protected void installComponents() { this.recomputeGroupHeaders(); } /** * Installs listeners on the associated button panel. */ protected void installListeners() { this.propertyChangeListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if ("maxButtonColumns".equals(evt.getPropertyName()) || "maxButtonRows".equals(evt.getPropertyName()) || "toShowGroupLabels".equals(evt.getPropertyName())) { SwingUtilities.invokeLater(new Runnable() { public void run() { if (buttonPanel != null) { recomputeGroupHeaders(); buttonPanel.revalidate(); buttonPanel.doLayout(); } } }); } if ("layoutKind".equals(evt.getPropertyName())) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (buttonPanel != null) { buttonPanel.setLayout(createLayoutManager()); buttonPanel.revalidate(); buttonPanel.doLayout(); } } }); } } }; this.buttonPanel.addPropertyChangeListener(this.propertyChangeListener); this.changeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { recomputeGroupHeaders(); buttonPanel.revalidate(); buttonPanel.doLayout(); } }; this.buttonPanel.addChangeListener(this.changeListener); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { c.setLayout(null); uninstallListeners(); uninstallComponents(); uninstallDefaults(); this.buttonPanel = null; } /** * Uninstalls defaults from the associated button panel. */ protected void uninstallDefaults() { } /** * Uninstalls sub-components from the associated button panel. */ protected void uninstallComponents() { if (this.groupLabels != null) { for (JLabel groupLabel : this.groupLabels) { this.buttonPanel.remove(groupLabel); } // for (JSeparator groupSeparator : this.groupSeparators) { // this.buttonPanel.remove(groupSeparator); // } } } /** * Uninstalls listeners from the associated button panel. */ protected void uninstallListeners() { this.buttonPanel .removePropertyChangeListener(this.propertyChangeListener); this.propertyChangeListener = null; this.buttonPanel.removeChangeListener(this.changeListener); this.changeListener = null; } /** * Returns the layout manager for the associated button panel. * * @return The layout manager for the associated button panel. */ protected LayoutManager createLayoutManager() { if (this.buttonPanel.getLayoutKind() == LayoutKind.ROW_FILL) return new RowFillLayout(); else return new ColumnFillLayout(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void paint(Graphics g, JComponent c) { Color bg = this.buttonPanel.getBackground(); g.setColor(bg); g.fillRect(0, 0, c.getWidth(), c.getHeight()); for (int i = 0; i < this.buttonPanel.getGroupCount(); i++) { Rectangle groupRect = this.groupRects[i]; this.paintGroupBackground(g, i, groupRect.x, groupRect.y, groupRect.width, groupRect.height); if (this.groupLabels[i].isVisible()) { Rectangle groupTitleBackground = this.groupLabels[i] .getBounds(); this.paintGroupTitleBackground(g, i, groupRect.x, groupTitleBackground.y - getGroupInsets().top, groupRect.width, groupTitleBackground.height + getGroupInsets().top + getLayoutGap()); } } } /** * Paints the background of the specified button panel group. * * @param g * Graphics context. * @param groupIndex * Group index. * @param x * X coordinate of the button group bounds. * @param y * Y coordinate of the button group bounds. * @param width * Width of the button group bounds. * @param height * Height of the button group bounds. */ protected void paintGroupBackground(Graphics g, int groupIndex, int x, int y, int width, int height) { Color c = this.buttonPanel.getBackground(); if ((c == null) || (c instanceof UIResource)) { c = UIManager.getColor("Panel.background"); if (c == null) c = new Color(190, 190, 190); if (groupIndex % 2 == 1) { double coef = 0.95; c = new Color((int) (c.getRed() * coef), (int) (c.getGreen() * coef), (int) (c.getBlue() * coef)); } } g.setColor(c); g.fillRect(x, y, width, height); } /** * Paints the background of the title of specified button panel group. * * @param g * Graphics context. * @param groupIndex * Group index. * @param x * X coordinate of the button group title bounds. * @param y * Y coordinate of the button group title bounds. * @param width * Width of the button group title bounds. * @param height * Height of the button group title bounds. */ protected void paintGroupTitleBackground(Graphics g, int groupIndex, int x, int y, int width, int height) { FlamingoUtilities.renderSurface(g, this.buttonPanel, new Rectangle(x, y, width, height), false, (groupIndex > 0), true); } /** * Returns the height of the group title strip. * * @param groupIndex * Group index. * @return The height of the title strip of the specified group. */ protected int getGroupTitleHeight(int groupIndex) { return this.groupLabels[groupIndex].getPreferredSize().height; } /** * Returns the insets of button panel groups. * * @return The insets of button panel groups. */ protected Insets getGroupInsets() { return GROUP_INSETS; } /** * Row-fill layout for the button panel. * * @author Kirill Grouchnikov */ protected class RowFillLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component comp) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component comp) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container parent) { Insets bInsets = parent.getInsets(); Insets groupInsets = getGroupInsets(); int left = bInsets.left; int right = bInsets.right; int y = bInsets.top; JCommandButtonPanel panel = (JCommandButtonPanel) parent; boolean ltr = panel.getComponentOrientation().isLeftToRight(); // compute max width of buttons int maxButtonWidth = 0; int maxButtonHeight = 0; int groupCount = panel.getGroupCount(); for (int i = 0; i < groupCount; i++) { for (AbstractCommandButton button : panel.getGroupButtons(i)) { maxButtonWidth = Math.max(maxButtonWidth, button .getPreferredSize().width); maxButtonHeight = Math.max(maxButtonHeight, button .getPreferredSize().height); } } groupRects = new Rectangle[groupCount]; int gap = getLayoutGap(); int maxWidth = parent.getWidth() - bInsets.left - bInsets.right - groupInsets.left - groupInsets.right; // for N buttons, there are N-1 gaps. Add the gap to the // available width and divide by the max button width + gap. int buttonsInRow = (maxButtonWidth == 0) ? 0 : (maxWidth + gap) / (maxButtonWidth + gap); int maxButtonColumnsToUse = panel.getMaxButtonColumns(); if (maxButtonColumnsToUse > 0) { buttonsInRow = Math.min(buttonsInRow, maxButtonColumnsToUse); } // System.out.println("Layout : " + buttonsInRow); for (int i = 0; i < groupCount; i++) { int topGroupY = y; y += groupInsets.top; JLabel groupLabel = groupLabels[i]; if (buttonPanel.isToShowGroupLabels()) { int labelWidth = groupLabel.getPreferredSize().width; int labelHeight = getGroupTitleHeight(i); if (groupLabel.getComponentOrientation().isLeftToRight()) { groupLabel.setBounds(left + groupInsets.left, y, labelWidth, labelHeight); } else { groupLabel.setBounds(parent.getWidth() - right - groupInsets.right - labelWidth, y, labelWidth, labelHeight); } y += labelHeight + gap; } int buttonRows = (buttonsInRow == 0) ? 0 : (int) (Math .ceil((double) panel.getGroupButtons(i).size() / buttonsInRow)); if (maxButtonColumnsToUse > 0) { buttonsInRow = Math .min(buttonsInRow, maxButtonColumnsToUse); } // spread the buttons so that we don't have extra space // on the right int actualButtonWidth = (buttonRows > 1) ? (maxWidth - (buttonsInRow - 1) * gap) / buttonsInRow : maxButtonWidth; if (maxButtonColumnsToUse == 1) actualButtonWidth = maxWidth; if (ltr) { int currX = left + groupInsets.left; for (AbstractCommandButton button : panel .getGroupButtons(i)) { int endX = currX + actualButtonWidth; if (endX > (parent.getWidth() - right - groupInsets.right)) { currX = left + groupInsets.left; y += maxButtonHeight; y += gap; } button.setBounds(currX, y, actualButtonWidth, maxButtonHeight); // System.out.println(button.getText() + ":" // + button.isVisible() + ":" + button.getBounds()); currX += actualButtonWidth; currX += gap; } } else { int currX = parent.getWidth() - right - groupInsets.right; for (AbstractCommandButton button : panel .getGroupButtons(i)) { int startX = currX - actualButtonWidth; if (startX < (left + groupInsets.left)) { currX = parent.getWidth() - right - groupInsets.right; y += maxButtonHeight; y += gap; } button.setBounds(currX - actualButtonWidth, y, actualButtonWidth, maxButtonHeight); // System.out.println(button.getText() + ":" // + button.isVisible() + ":" + button.getBounds()); currX -= actualButtonWidth; currX -= gap; } } y += maxButtonHeight + groupInsets.bottom; int bottomGroupY = y; groupRects[i] = new Rectangle(left, topGroupY, (parent .getWidth() - left - right), (bottomGroupY - topGroupY)); } } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container parent) { return new Dimension(20, 20); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container parent) { JCommandButtonPanel panel = (JCommandButtonPanel) parent; int maxButtonColumnsToUse = panel.getMaxButtonColumns(); Insets bInsets = parent.getInsets(); Insets groupInsets = getGroupInsets(); int insetsWidth = bInsets.left + groupInsets.left + bInsets.right + groupInsets.right; // compute max width of buttons int maxButtonWidth = 0; int maxButtonHeight = 0; int groupCount = panel.getGroupCount(); for (int i = 0; i < groupCount; i++) { for (AbstractCommandButton button : panel.getGroupButtons(i)) { maxButtonWidth = Math.max(maxButtonWidth, button .getPreferredSize().width); maxButtonHeight = Math.max(maxButtonHeight, button .getPreferredSize().height); } } // total height int gap = getLayoutGap(); boolean usePanelWidth = (maxButtonColumnsToUse <= 0); int availableWidth = panel.getWidth(); availableWidth -= insetsWidth; if (usePanelWidth) { // this hasn't been set. Compute using the available // width maxButtonColumnsToUse = (availableWidth + gap) / (maxButtonWidth + gap); } int height = bInsets.top + bInsets.bottom; // System.out.print(height + "[" + maxButtonColumnsToUse + "]"); for (int i = 0; i < groupCount; i++) { if (groupLabels[i].isVisible()) { height += (getGroupTitleHeight(i) + gap); } height += (groupInsets.top + groupInsets.bottom); int buttonRows = (int) (Math.ceil((double) panel .getGroupButtons(i).size() / maxButtonColumnsToUse)); height += buttonRows * maxButtonHeight + (buttonRows - 1) * gap; // System.out.print(" " + height); } int prefWidth = usePanelWidth ? availableWidth : maxButtonColumnsToUse * maxButtonWidth + (maxButtonColumnsToUse - 1) * gap + bInsets.left + bInsets.right + groupInsets.left + groupInsets.right; // System.out.println(" : " + height); return new Dimension(Math.max(10, prefWidth), Math.max(10, height)); } } /** * Column-fill layout for the button panel. * * @author Kirill Grouchnikov */ protected class ColumnFillLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component comp) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component comp) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container parent) { Insets bInsets = parent.getInsets(); Insets groupInsets = getGroupInsets(); int top = bInsets.top; int bottom = bInsets.bottom; JCommandButtonPanel panel = (JCommandButtonPanel) parent; boolean ltr = panel.getComponentOrientation().isLeftToRight(); // compute max width of buttons int maxButtonWidth = 0; int maxButtonHeight = 0; int groupCount = panel.getGroupCount(); for (int i = 0; i < groupCount; i++) { for (AbstractCommandButton button : panel.getGroupButtons(i)) { maxButtonWidth = Math.max(maxButtonWidth, button .getPreferredSize().width); maxButtonHeight = Math.max(maxButtonHeight, button .getPreferredSize().height); } } groupRects = new Rectangle[groupCount]; int gap = getLayoutGap(); int maxHeight = parent.getHeight() - bInsets.top - bInsets.bottom - groupInsets.top - groupInsets.bottom; // for N buttons, there are N-1 gaps. Add the gap to the // available width and divide by the max button width + gap. int buttonsInRow = (maxButtonHeight == 0) ? 0 : (maxHeight + gap) / (maxButtonHeight + gap); if (ltr) { int x = bInsets.left + groupInsets.left; for (int i = 0; i < groupCount; i++) { int leftGroupX = x; x += groupInsets.left; int currY = top + groupInsets.top; int buttonColumns = (buttonsInRow == 0) ? 0 : (int) (Math .ceil((double) panel.getGroupButtons(i).size() / buttonsInRow)); // spread the buttons so that we don't have extra space // on the bottom int actualButtonHeight = (buttonColumns > 1) ? (maxHeight - (buttonsInRow - 1) * gap) / buttonsInRow : maxButtonWidth; for (AbstractCommandButton button : panel .getGroupButtons(i)) { int endY = currY + actualButtonHeight; if (endY > (parent.getHeight() - bottom - groupInsets.bottom)) { currY = top + groupInsets.top; x += maxButtonWidth; x += gap; } button.setBounds(x, currY, maxButtonWidth, actualButtonHeight); // System.out.println(button.getText() + ":" // + button.isVisible() + ":" + button.getBounds()); currY += actualButtonHeight; currY += gap; } x += maxButtonWidth + groupInsets.bottom; int rightGroupX = x; groupRects[i] = new Rectangle(leftGroupX, top, (rightGroupX - leftGroupX), (parent.getHeight() - top - bottom)); } } else { int x = panel.getWidth() - bInsets.right - groupInsets.right; for (int i = 0; i < groupCount; i++) { int rightGroupX = x; x -= groupInsets.left; int currY = top + groupInsets.top; int buttonColumns = (buttonsInRow == 0) ? 0 : (int) (Math .ceil((double) panel.getGroupButtons(i).size() / buttonsInRow)); // spread the buttons so that we don't have extra space // on the bottom int actualButtonHeight = (buttonColumns > 1) ? (maxHeight - (buttonsInRow - 1) * gap) / buttonsInRow : maxButtonWidth; for (AbstractCommandButton button : panel .getGroupButtons(i)) { int endY = currY + actualButtonHeight; if (endY > (parent.getHeight() - bottom - groupInsets.bottom)) { currY = top + groupInsets.top; x -= maxButtonWidth; x -= gap; } button.setBounds(x - maxButtonWidth, currY, maxButtonWidth, actualButtonHeight); // System.out.println(button.getText() + ":" // + button.isVisible() + ":" + button.getBounds()); currY += actualButtonHeight; currY += gap; } x -= (maxButtonWidth + groupInsets.bottom); int leftGroupX = x; groupRects[i] = new Rectangle(leftGroupX, top, (rightGroupX - leftGroupX), (parent.getHeight() - top - bottom)); } } } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container parent) { return new Dimension(20, 20); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container parent) { JCommandButtonPanel panel = (JCommandButtonPanel) parent; int maxButtonRowsToUse = panel.getMaxButtonRows(); Insets bInsets = parent.getInsets(); Insets groupInsets = getGroupInsets(); int insetsHeight = bInsets.top + groupInsets.top + bInsets.bottom + groupInsets.bottom; // compute max width of buttons int maxButtonWidth = 0; int maxButtonHeight = 0; int groupCount = panel.getGroupCount(); for (int i = 0; i < groupCount; i++) { for (AbstractCommandButton button : panel.getGroupButtons(i)) { maxButtonWidth = Math.max(maxButtonWidth, button .getPreferredSize().width); maxButtonHeight = Math.max(maxButtonHeight, button .getPreferredSize().height); } } // total width int gap = getLayoutGap(); boolean usePanelHeight = (maxButtonRowsToUse <= 0); int availableHeight = panel.getHeight(); availableHeight -= insetsHeight; if (usePanelHeight) { // this hasn't been set. Compute using the available // height maxButtonRowsToUse = (availableHeight + gap) / (maxButtonHeight + gap); } // go over all groups and see how many columns each one needs int width = bInsets.left + bInsets.right; for (int i = 0; i < groupCount; i++) { width += (groupInsets.left + groupInsets.right); int buttonColumns = (int) (Math.ceil((double) panel .getGroupButtons(i).size() / maxButtonRowsToUse)); width += buttonColumns * maxButtonWidth + (buttonColumns - 1) * gap; } int prefHeight = usePanelHeight ? availableHeight : maxButtonRowsToUse * maxButtonWidth + (maxButtonRowsToUse - 1) * gap + bInsets.top + bInsets.bottom + groupInsets.top + groupInsets.bottom; return new Dimension(Math.max(10, width), Math.max(10, prefHeight)); } } /** * Returns the layout gap for button panel components. * * @return The layout gap for button panel components. */ protected int getLayoutGap() { return 4; } /** * Recomputes the components for button group headers. */ protected void recomputeGroupHeaders() { if (this.groupLabels != null) { for (JLabel groupLabel : this.groupLabels) { this.buttonPanel.remove(groupLabel); } } int groupCount = this.buttonPanel.getGroupCount(); this.groupLabels = new JLabel[groupCount]; for (int i = 0; i < groupCount; i++) { this.groupLabels[i] = new JLabel(this.buttonPanel .getGroupTitleAt(i)); this.groupLabels[i].setComponentOrientation(this.buttonPanel .getComponentOrientation()); this.buttonPanel.add(this.groupLabels[i]); this.groupLabels[i].setVisible(this.buttonPanel .isToShowGroupLabels()); } } /** * Returns the preferred size of the associated button panel for the * specified parameters. * * @param buttonVisibleRows * Target number of visible button rows. * @param titleVisibleRows * Target number of visible group title rows. * @return The preferred size of the associated button panel for the * specified parameters. */ public int getPreferredHeight(int buttonVisibleRows, int titleVisibleRows) { Insets bInsets = this.buttonPanel.getInsets(); Insets groupInsets = getGroupInsets(); int maxButtonHeight = 0; int groupCount = this.buttonPanel.getGroupCount(); for (int i = 0; i < groupCount; i++) { for (AbstractCommandButton button : this.buttonPanel .getGroupButtons(i)) { maxButtonHeight = Math.max(maxButtonHeight, button .getPreferredSize().height); } } // total height int gap = getLayoutGap(); // panel insets int totalHeight = bInsets.top + bInsets.bottom; // height of icon rows totalHeight += buttonVisibleRows * maxButtonHeight; // gaps between icon rows totalHeight += (buttonVisibleRows - 1) * gap; // title height totalHeight += titleVisibleRows * getGroupTitleHeight(0); // title insets totalHeight += (titleVisibleRows - 1) * (groupInsets.top + groupInsets.bottom); return totalHeight; } } src/org/pushingpixels/flamingo/internal/ui/common/CommandButtonPanelUI.java0000644000175000017500000000364411401230444026252 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import javax.swing.plaf.PanelUI; import org.pushingpixels.flamingo.api.common.JCommandButtonPanel; /** * UI for icon panel ({@link JCommandButtonPanel}). * * @author Kirill Grouchnikov */ public abstract class CommandButtonPanelUI extends PanelUI { }src/org/pushingpixels/flamingo/internal/ui/common/CommandButtonLayoutManagerBig.java0000644000175000017500000003233311401230444030144 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import java.util.StringTokenizer; import javax.swing.JSeparator; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerBig implements CommandButtonLayoutManager { protected AbstractCommandButton commandButton; /** * The first part of (possibly) two-lined split of {@link #commandButton}'s * title. */ protected String titlePart1; /** * The second part of (possibly) two-lined split of {@link #commandButton}'s * title. */ protected String titlePart2; public CommandButtonLayoutManagerBig(AbstractCommandButton commandButton) { this.commandButton = commandButton; this.updateTitleStrings(); } @Override public int getPreferredIconSize() { return 32; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = (commandButton == null) ? new Insets(0, 0, 0, 0) : commandButton.getInsets(); int bx = borderInsets.left + borderInsets.right; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); JSeparator jsep = new JSeparator(JSeparator.HORIZONTAL); int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); int layoutVGap = FlamingoUtilities.getVLayoutGap(commandButton); boolean hasIcon = (commandButton.getIcon() != null); boolean hasText = (this.titlePart1 != null); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); int title1Width = (this.titlePart1 == null) ? 0 : fm .stringWidth(this.titlePart1); int title2Width = (this.titlePart2 == null) ? 0 : fm .stringWidth(this.titlePart2); int prefIconSize = hasIcon ? this.getPreferredIconSize() : 0; int width = Math.max(prefIconSize, Math.max(title1Width, title2Width + 4 * layoutHGap + jsep.getPreferredSize().height + (FlamingoUtilities.hasPopupAction(commandButton) ? 1 + fm .getHeight() / 2 : 0))); // start height with the top inset int height = borderInsets.top; // icon? if (hasIcon) { // padding above the icon height += layoutVGap; // icon height height += prefIconSize; // padding below the icon height += layoutVGap; } // text? if (hasText) { // padding above the text height += layoutVGap; // text height - two lines height += 2 * (fm.getAscent() + fm.getDescent()); // padding below the text height += layoutVGap; } // popup icon (no text)? if (!hasText && hasPopupIcon) { // padding above the popup icon height += layoutVGap; // popup icon height - one line of text height += fm.getHeight(); // padding below the popup icon height += layoutVGap; } // separator? if (commandButton instanceof JCommandButton) { JCommandButton jcb = (JCommandButton) commandButton; CommandButtonKind buttonKind = jcb.getCommandButtonKind(); if (hasIcon && buttonKind.hasAction() && buttonKind.hasPopup()) { // space for a horizontal separator height += new JSeparator(JSeparator.HORIZONTAL) .getPreferredSize().height; } } // bottom insets height += borderInsets.bottom; // and remove the padding above the first and below the last elements height -= 2 * layoutVGap; return new Dimension(bx + width, height); } @Override public void propertyChange(PropertyChangeEvent evt) { if ("text".equals(evt.getPropertyName()) || "font".equals(evt.getPropertyName())) { this.updateTitleStrings(); } } /** * Updates the title strings for {@link CommandButtonDisplayState#BIG} and * other relevant states. */ protected void updateTitleStrings() { // Break the title in two parts (the second part may be empty), // finding the "inflection" point. The inflection point is a space // character that breaks the title in two parts, such that the maximal // length of the first part and the second part + action label icon // is minimal between all possible space characters BufferedImage tempImage = new BufferedImage(30, 30, BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) tempImage.getGraphics(); g.setFont(FlamingoUtilities.getFont(this.commandButton, "Ribbon.font", "Button.font", "Panel.font")); FontMetrics fm = g.getFontMetrics(); String title = (this.commandButton == null) ? null : this.commandButton .getText(); if (title != null) { StringTokenizer tokenizer = new StringTokenizer(title, " _-", true); if (tokenizer.countTokens() <= 1) { // single word this.titlePart1 = title; this.titlePart2 = null; } else { int currMaxLength = (int) fm.getStringBounds( this.commandButton.getText(), g).getWidth(); int actionIconWidth = FlamingoUtilities .hasPopupAction(this.commandButton) ? 0 : 2 * FlamingoUtilities.getHLayoutGap(commandButton) + (fm.getAscent() + fm.getDescent()) / 2; String currLeading = ""; while (tokenizer.hasMoreTokens()) { currLeading += tokenizer.nextToken(); String part1 = currLeading; String part2 = title.substring(currLeading.length()); int len1 = (int) fm.getStringBounds(part1, g).getWidth(); int len2 = (int) fm.getStringBounds(part2, g).getWidth() + actionIconWidth; int len = Math.max(len1, len2); if (currMaxLength > len) { currMaxLength = len; this.titlePart1 = part1; this.titlePart2 = part2; } } } } else { this.titlePart1 = null; this.titlePart2 = null; } } @Override public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) { // center of the bottom edge return new Point(commandButton.getWidth() / 2, commandButton .getHeight()); } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, 0, 0); Insets ins = commandButton.getInsets(); result.iconRect = new Rectangle(); result.popupActionRect = new Rectangle(); int width = commandButton.getWidth(); int height = commandButton.getHeight(); int x = ins.left; boolean ltr = commandButton.getComponentOrientation().isLeftToRight(); FontMetrics fm = g.getFontMetrics(); int labelHeight = fm.getAscent() + fm.getDescent(); JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton) .getCommandButtonKind() : JCommandButton.CommandButtonKind.ACTION_ONLY; ResizableIcon buttonIcon = commandButton.getIcon(); boolean hasIcon = (commandButton.getIcon() != null); boolean hasText = (this.titlePart1 != null); boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton); int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton); int layoutVGap = FlamingoUtilities.getVLayoutGap(commandButton); int prefHeight = this.getPreferredSize(commandButton).height; int shiftY = 0; if (height > prefHeight) { shiftY = (height - prefHeight) / 2; } int y = ins.top + shiftY - layoutVGap; // icon if (hasIcon) { y += layoutVGap; int iconHeight = buttonIcon.getIconHeight(); int iconWidth = buttonIcon.getIconWidth(); result.iconRect.x = (width - iconWidth) / 2; result.iconRect.y = y; result.iconRect.width = iconWidth; result.iconRect.height = iconHeight; y += (iconHeight + layoutVGap); } // separator? if (commandButton instanceof JCommandButton) { // horizontal separator is always after the icon if (hasIcon && buttonKind.hasAction() && buttonKind.hasPopup()) { result.separatorOrientation = CommandButtonLayoutManager.CommandButtonSeparatorOrientation.HORIZONTAL; result.separatorArea = new Rectangle(0, 0, 0, 0); result.separatorArea.x = 0; result.separatorArea.y = y; result.separatorArea.width = width; result.separatorArea.height = new JSeparator( JSeparator.HORIZONTAL).getPreferredSize().height; y += result.separatorArea.height; } } int lastTextLineWidth = 0; // text if (hasText) { y += layoutVGap; lastTextLineWidth = (this.titlePart1 != null) ? (int) fm .getStringBounds(this.titlePart1, g).getWidth() : 0; TextLayoutInfo line1LayoutInfo = new TextLayoutInfo(); line1LayoutInfo.text = this.titlePart1; line1LayoutInfo.textRect = new Rectangle(); line1LayoutInfo.textRect.x = ins.left + (width - lastTextLineWidth - ins.left - ins.right) / 2; line1LayoutInfo.textRect.y = y; line1LayoutInfo.textRect.width = lastTextLineWidth; line1LayoutInfo.textRect.height = labelHeight; if (this.titlePart1 != null) { y += labelHeight; } lastTextLineWidth = (this.titlePart2 != null) ? (int) fm .getStringBounds(this.titlePart2, g).getWidth() : 0; int extraWidth = hasPopupIcon ? 4 * layoutHGap + labelHeight / 2 : 0; if (ltr) { x = ins.left + (width - lastTextLineWidth - extraWidth - ins.left - ins.right) / 2; } if (!ltr) { x = width - ins.right - lastTextLineWidth - +(width - lastTextLineWidth - extraWidth - ins.left - ins.right) / 2; } TextLayoutInfo line2LayoutInfo = new TextLayoutInfo(); line2LayoutInfo.text = this.titlePart2; line2LayoutInfo.textRect = new Rectangle(); line2LayoutInfo.textRect.x = x; line2LayoutInfo.textRect.y = y; line2LayoutInfo.textRect.width = lastTextLineWidth; line2LayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(line1LayoutInfo); result.textLayoutInfoList.add(line2LayoutInfo); } if (hasPopupIcon) { if (lastTextLineWidth > 0) { if (ltr) { x += 2 * layoutHGap; x += lastTextLineWidth; } else { x -= 2 * layoutHGap; x -= labelHeight / 2; } } else { x = (width - 1 - labelHeight / 2) / 2; } result.popupActionRect.x = x; result.popupActionRect.y = y - 1; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.height = labelHeight + 2; } switch (buttonKind) { case ACTION_ONLY: result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; break; case POPUP_ONLY: result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; break; case ACTION_AND_POPUP_MAIN_ACTION: case ACTION_AND_POPUP_MAIN_POPUP: // 1. break after icon if button has icon // 2. no break (all popup) if button has no icon if (hasIcon) { int yBorderBetweenActionAndPopupAreas = result.iconRect.y + result.iconRect.height + layoutVGap; result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = yBorderBetweenActionAndPopupAreas; result.popupClickArea.x = 0; result.popupClickArea.y = yBorderBetweenActionAndPopupAreas; result.popupClickArea.width = width; result.popupClickArea.height = height - yBorderBetweenActionAndPopupAreas; result.isTextInActionArea = false; } else { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; } } return result; } } src/org/pushingpixels/flamingo/internal/ui/common/BasicCommandButtonStripUI.java0000644000175000017500000002153211401230444027252 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.*; import javax.swing.JComponent; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.AbstractCommandButton; import org.pushingpixels.flamingo.api.common.JCommandButtonStrip; import org.pushingpixels.flamingo.api.common.JCommandButtonStrip.StripOrientation; /** * Basic UI for button strip {@link JCommandButtonStrip}. * * @author Kirill Grouchnikov */ public class BasicCommandButtonStripUI extends CommandButtonStripUI { /** * The associated button strip. */ protected JCommandButtonStrip buttonStrip; protected ChangeListener changeListener; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicCommandButtonStripUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.buttonStrip = (JCommandButtonStrip) c; installDefaults(); installComponents(); installListeners(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); c.setLayout(null); this.buttonStrip = null; } /** * Installs listeners on the associated button strip. */ protected void installListeners() { this.changeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (buttonStrip.getButtonCount() == 1) { buttonStrip .getButton(0) .setLocationOrderKind( AbstractCommandButton.CommandButtonLocationOrderKind.ONLY); } else { buttonStrip .getButton(0) .setLocationOrderKind( AbstractCommandButton.CommandButtonLocationOrderKind.FIRST); for (int i = 1; i < buttonStrip.getButtonCount() - 1; i++) { buttonStrip .getButton(i) .setLocationOrderKind( AbstractCommandButton.CommandButtonLocationOrderKind.MIDDLE); } buttonStrip .getButton(buttonStrip.getButtonCount() - 1) .setLocationOrderKind( AbstractCommandButton.CommandButtonLocationOrderKind.LAST); } } }; this.buttonStrip.addChangeListener(this.changeListener); } /** * Uninstalls listeners from the associated button strip. */ protected void uninstallListeners() { this.buttonStrip.removeChangeListener(this.changeListener); this.changeListener = null; } /** * Installs defaults on the associated button strip. */ protected void installDefaults() { this.buttonStrip.setBorder(new EmptyBorder(0, 0, 0, 0)); } /** * Uninstalls defaults from the associated button strip. */ protected void uninstallDefaults() { } /** * Installs subcomponents on the associated button strip. */ protected void installComponents() { this.buttonStrip.setLayout(createLayoutManager()); } /** * Uninstalls subcomponents from the associated ribbon. */ protected void uninstallComponents() { } /** * Invoked by installUI to create a layout manager object to * manage the {@link JCommandButtonStrip}. * * @return a layout manager object */ protected LayoutManager createLayoutManager() { return new ButtonStripLayout(); } /** * Layout for the button strip. * * @author Kirill Grouchnikov */ private class ButtonStripLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { int width = 0; int height = 0; if (buttonStrip.getOrientation() == StripOrientation.HORIZONTAL) { for (int i = 0; i < buttonStrip.getButtonCount(); i++) { width += buttonStrip.getButton(i).getPreferredSize().width; height = Math.max(height, buttonStrip.getButton(i) .getPreferredSize().height); } } else { for (int i = 0; i < buttonStrip.getButtonCount(); i++) { height += buttonStrip.getButton(i).getPreferredSize().height; width = Math.max(width, buttonStrip.getButton(i) .getPreferredSize().width); } } Insets ins = c.getInsets(); // System.out.println(ins + ":" + width + ":" + height); return new Dimension(width + ins.left + ins.right, height + ins.top + ins.bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { return this.preferredLayoutSize(c); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { if (buttonStrip.getButtonCount() == 0) return; Insets ins = c.getInsets(); int height = c.getHeight() - ins.top - ins.bottom; int width = c.getWidth() - ins.left - ins.right; if (buttonStrip.getOrientation() == StripOrientation.HORIZONTAL) { int totalPreferredWidth = 0; for (int i = 0; i < buttonStrip.getButtonCount(); i++) { AbstractCommandButton currButton = buttonStrip.getButton(i); totalPreferredWidth += currButton.getPreferredSize().width; } int deltaX = (width - totalPreferredWidth) / buttonStrip.getButtonCount(); if (buttonStrip.getComponentOrientation().isLeftToRight()) { int x = ins.left; for (int i = 0; i < buttonStrip.getButtonCount(); i++) { AbstractCommandButton currButton = buttonStrip .getButton(i); currButton.setBounds(x, ins.top, currButton .getPreferredSize().width + deltaX, height); x += (currButton.getPreferredSize().width + deltaX); } } else { int x = c.getWidth() - ins.right; for (int i = 0; i < buttonStrip.getButtonCount(); i++) { AbstractCommandButton currButton = buttonStrip .getButton(i); int buttonWidth = currButton.getPreferredSize().width + deltaX; currButton.setBounds(x - buttonWidth, ins.top, buttonWidth, height); x -= buttonWidth; } } } else { int totalPreferredHeight = 0; for (int i = 0; i < buttonStrip.getButtonCount(); i++) { AbstractCommandButton currButton = buttonStrip.getButton(i); totalPreferredHeight += currButton.getPreferredSize().height; } float deltaY = (float) (height - totalPreferredHeight) / (float) buttonStrip.getButtonCount(); float y = ins.top; for (int i = 0; i < buttonStrip.getButtonCount(); i++) { AbstractCommandButton currButton = buttonStrip.getButton(i); float buttonHeight = (currButton.getPreferredSize().height + deltaY); currButton.setBounds(ins.left, (int) y, width, (int) Math .ceil(buttonHeight)); y += buttonHeight; } } } } } src/org/pushingpixels/flamingo/internal/ui/common/BasicCommandButtonListener.java0000644000175000017500000002644411401230444027507 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.common; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.*; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.model.PopupButtonModel; /** * Listener to track user interaction with the command buttons. * * @author Kirill Grouchnikov */ public class BasicCommandButtonListener implements MouseListener, MouseMotionListener, FocusListener, ChangeListener { /* * (non-Javadoc) * * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent) */ @Override public void focusLost(FocusEvent e) { AbstractCommandButton b = (AbstractCommandButton) e.getSource(); // System.err.println(e.getComponent() + "\n\tlost " // + (e.isTemporary() ? "temporary" : "permanent") // + " focus to \n\t" + e.getOppositeComponent()); b.getActionModel().setArmed(false); b.getActionModel().setPressed(false); if (b instanceof JCommandButton) { PopupButtonModel popupModel = ((JCommandButton) b).getPopupModel(); popupModel.setPressed(false); popupModel.setArmed(false); } } /* * (non-Javadoc) * * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent) */ @Override public void focusGained(FocusEvent e) { AbstractCommandButton b = (AbstractCommandButton) e.getSource(); b.repaint(); } /* * (non-Javadoc) * * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent) */ @Override public void mousePressed(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { AbstractCommandButton b = (AbstractCommandButton) e.getSource(); JScrollablePanel scrollable = (JScrollablePanel) SwingUtilities .getAncestorOfClass(JScrollablePanel.class, b); if (scrollable != null) { // scroll the viewport of the scrollable panel so that // the button is fully viewed. Point loc = SwingUtilities.convertPoint(b.getParent(), b .getLocation(), scrollable.getView()); if (scrollable.getScrollType() == JScrollablePanel.ScrollType.HORIZONTALLY) { scrollable.scrollToIfNecessary(loc.x, b.getWidth()); } else { scrollable.scrollToIfNecessary(loc.y, b.getHeight()); } } if (b.contains(e.getX(), e.getY())) { CommandButtonUI ui = b.getUI(); Rectangle actionRect = ui.getLayoutInfo().actionClickArea; Rectangle popupRect = ui.getLayoutInfo().popupClickArea; if ((actionRect != null) && actionRect.contains(e.getPoint())) { ButtonModel actionModel = b.getActionModel(); if (actionModel.isEnabled()) { actionModel.setArmed(true); actionModel.setPressed(true); } } else { if ((popupRect != null) && popupRect.contains(e.getPoint())) { PopupButtonModel popupModel = ((JCommandButton) b) .getPopupModel(); if (popupModel.isEnabled()) { popupModel.setArmed(true); popupModel.setPressed(true); } } } // System.err.println(b.getText() + " - hasFocus():" // + b.hasFocus() + ", isRequestFocusEnabled():" // + b.isRequestFocusEnabled()); if (!b.hasFocus() && b.isRequestFocusEnabled()) { b.requestFocusInWindow(); } } } }; /* * (non-Javadoc) * * @see * java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent) */ @Override public void mouseReleased(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { AbstractCommandButton b = (AbstractCommandButton) e.getSource(); b.getActionModel().setPressed(false); if (b instanceof JCommandButton) { ((JCommandButton) b).getPopupModel().setPressed(false); } b.getActionModel().setArmed(false); if (b instanceof JCommandButton) { ((JCommandButton) b).getPopupModel().setArmed(false); } } }; /* * (non-Javadoc) * * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) */ @Override public void mouseClicked(MouseEvent e) { } /* * (non-Javadoc) * * @see * java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent * ) */ @Override public void mouseDragged(MouseEvent e) { } /* * (non-Javadoc) * * @see * java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent) */ @Override public void mouseMoved(MouseEvent e) { this.syncMouseMovement(e); } /* * (non-Javadoc) * * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent) */ @Override public void mouseEntered(MouseEvent e) { this.syncMouseMovement(e); } /** * Synchronizes the action and popup models of the command button with the * specified mouse event. * * @param e * Mouse event for the model synchronization. */ private void syncMouseMovement(MouseEvent e) { AbstractCommandButton b = (AbstractCommandButton) e.getSource(); ButtonModel actionModel = b.getActionModel(); PopupButtonModel popupModel = (b instanceof JCommandButton) ? ((JCommandButton) b) .getPopupModel() : null; CommandButtonUI ui = b.getUI(); Rectangle actionRect = ui.getLayoutInfo().actionClickArea; Rectangle popupRect = ui.getLayoutInfo().popupClickArea; if ((actionRect != null) && actionRect.contains(e.getPoint())) { if (actionModel.isEnabled()) { if (!SwingUtilities.isLeftMouseButton(e)) actionModel.setRollover(true); if (actionModel.isPressed()) actionModel.setArmed(true); } if (popupModel != null && !SwingUtilities.isLeftMouseButton(e)) popupModel.setRollover(false); } else { if ((popupRect != null) && popupRect.contains(e.getPoint())) { if ((popupModel != null) && popupModel.isEnabled()) { if (!SwingUtilities.isLeftMouseButton(e)) popupModel.setRollover(true); if (popupModel.isPressed()) popupModel.setArmed(true); } if (!SwingUtilities.isLeftMouseButton(e)) actionModel.setRollover(false); } } }; /* * (non-Javadoc) * * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent) */ public void mouseExited(MouseEvent e) { AbstractCommandButton b = (AbstractCommandButton) e.getSource(); ButtonModel actionModel = b.getActionModel(); PopupButtonModel popupModel = (b instanceof JCommandButton) ? ((JCommandButton) b) .getPopupModel() : null; actionModel.setRollover(false); actionModel.setArmed(false); if (popupModel != null) { popupModel.setRollover(false); popupModel.setArmed(false); } }; /* * (non-Javadoc) * * @see * javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent * ) */ public void stateChanged(ChangeEvent e) { AbstractCommandButton b = (AbstractCommandButton) e.getSource(); b.repaint(); } /** * Installs keyboard action (space / enter keys) on the specified command * button. * * @param button * Command button. */ public void installKeyboardActions(AbstractCommandButton button) { ActionMap map = new ActionMap(); map.put(PressAction.PRESS, new PressAction(button)); map.put(ReleaseAction.RELEASE, new ReleaseAction(button)); SwingUtilities.replaceUIActionMap(button, map); InputMap km = LookAndFeel.makeInputMap(new Object[] { "SPACE", "pressed", "released SPACE", "released", "ENTER", "pressed", "released ENTER", "released" }); SwingUtilities.replaceUIInputMap(button, JComponent.WHEN_FOCUSED, km); } /** * Button press action. * * @author Kirill Grouchnikov */ private static class PressAction extends AbstractAction { /** * Press action name. */ private static final String PRESS = "pressed"; /** * Associated command button. */ AbstractCommandButton button; /** * Creates a new press action. * * @param button * Associated command button. */ PressAction(AbstractCommandButton button) { super(PRESS); this.button = button; } /* * (non-Javadoc) * * @see * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent * ) */ public void actionPerformed(ActionEvent e) { ButtonModel model = button.getActionModel(); model.setArmed(true); model.setPressed(true); if (!button.hasFocus()) { button.requestFocus(); } } /* * (non-Javadoc) * * @see javax.swing.AbstractAction#isEnabled() */ @Override public boolean isEnabled() { return button.getActionModel().isEnabled(); } } /** * Button release action. * * @author Kirill Grouchnikov */ private static class ReleaseAction extends AbstractAction { /** * Release action name. */ private static final String RELEASE = "released"; /** * Associated command button. */ AbstractCommandButton button; /** * Creates a new release action. * * @param button * Associated command button. */ ReleaseAction(AbstractCommandButton button) { super(RELEASE); this.button = button; } /* * (non-Javadoc) * * @see * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent * ) */ public void actionPerformed(ActionEvent e) { ButtonModel model = button.getActionModel(); model.setPressed(false); model.setArmed(false); } /* * (non-Javadoc) * * @see javax.swing.AbstractAction#isEnabled() */ @Override public boolean isEnabled() { return button.getActionModel().isEnabled(); } } /** * Uninstalls keyboard action (space / enter keys) from the specified * command button. * * @param button * Command button. */ public void uninstallKeyboardActions(AbstractCommandButton button) { SwingUtilities.replaceUIInputMap(button, JComponent.WHEN_IN_FOCUSED_WINDOW, null); SwingUtilities.replaceUIInputMap(button, JComponent.WHEN_FOCUSED, null); SwingUtilities.replaceUIActionMap(button, null); } } src/org/pushingpixels/flamingo/internal/ui/ribbon/0000755000175000017500000000000011415653300021400 5ustar tonytonysrc/org/pushingpixels/flamingo/internal/ui/ribbon/JFlowBandControlPanel.java0000644000175000017500000000737611401230446026404 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.Component; import java.util.*; import javax.swing.*; import javax.swing.plaf.UIResource; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; /** * Control panel of a single {@link JRibbonBand}. This class is for internal use * only and should not be directly used by the applications. * * @author Kirill Grouchnikov */ public class JFlowBandControlPanel extends AbstractBandControlPanel implements UIResource { /** * List of all components of this control panel. */ private List comps; /** * The UI class ID string. */ public static final String uiClassID = "FlowBandControlPanelUI"; /** * Creates a control panel for specified ribbon band. * * @param ribbonBand * Ribbon band. */ public JFlowBandControlPanel() { super(); this.comps = new LinkedList(); } /** * Sets the new UI delegate. * * @param ui * New UI delegate. */ public void setUI(BandControlPanelUI ui) { super.setUI(ui); } /* * (non-Javadoc) * * @see javax.swing.JPanel#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((BandControlPanelUI) UIManager.getUI(this)); } else { setUI(new BasicFlowBandControlPanelUI()); } } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUI() */ @Override public BandControlPanelUI getUI() { return (BandControlPanelUI) ui; } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /** * Adds new panel to this control panel. * * @param panel * Panel to add */ public void addFlowComponent(JComponent comp) { this.comps.add(comp); super.add(comp); } /** * Returns regular panels of this control panel. * * @return Regular panels of this control panel. */ public List getFlowComponents() { return this.comps; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/0000755000175000017500000000000011401230446023042 5ustar tonytony././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootsrc/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/BasicRibbonApplicationMenuPopupPanelUI.javasrc/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/BasicRibbonApplicationMenuPopupPanelUI.jav0000644000175000017500000003564311422072050033205 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon.appmenu; import java.awt.*; import java.awt.event.ActionEvent; import java.util.List; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonPopupOrientationKind; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback; import org.pushingpixels.flamingo.internal.ui.common.popup.BasicPopupPanelUI; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Basic UI for ribbon application menu button * {@link JRibbonApplicationMenuButton}. * * @author Kirill Grouchnikov */ public class BasicRibbonApplicationMenuPopupPanelUI extends BasicPopupPanelUI { protected JPanel panelLevel1; protected JPanel panelLevel2; protected JPanel footerPanel; protected static final CommandButtonDisplayState MENU_TILE_LEVEL_1 = new CommandButtonDisplayState( "Ribbon application menu tile level 1", 32) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton commandButton) { return new CommandButtonLayoutManagerMenuTileLevel1(); } }; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicRibbonApplicationMenuPopupPanelUI(); } /** * The associated application menu button. */ protected JRibbonApplicationMenuPopupPanel applicationMenuPopupPanel; protected JPanel mainPanel; /* * (non-Javadoc) * * @see * javax.swing.plaf.basic.BasicButtonUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.applicationMenuPopupPanel = (JRibbonApplicationMenuPopupPanel) c; this.popupPanel = (JPopupPanel) c; this.applicationMenuPopupPanel.setLayout(new BorderLayout()); installDefaults(); installComponents(); installListeners(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); this.applicationMenuPopupPanel = null; } @Override protected void installDefaults() { super.installDefaults(); } @Override protected void installComponents() { super.installComponents(); this.mainPanel = createMainPanel(); this.panelLevel1 = new JPanel(); this.panelLevel1.setLayout(new LayoutManager() { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension preferredLayoutSize(Container parent) { int height = 0; int width = 0; for (int i = 0; i < parent.getComponentCount(); i++) { Dimension pref = parent.getComponent(i).getPreferredSize(); height += pref.height; width = Math.max(width, pref.width); } Insets ins = parent.getInsets(); return new Dimension(width + ins.left + ins.right, height + ins.top + ins.bottom); } @Override public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); } @Override public void layoutContainer(Container parent) { Insets ins = parent.getInsets(); int topY = ins.top; for (int i = 0; i < parent.getComponentCount(); i++) { Component comp = parent.getComponent(i); Dimension pref = comp.getPreferredSize(); comp.setBounds(ins.left, topY, parent.getWidth() - ins.left - ins.right, pref.height); topY += pref.height; } } }); final RibbonApplicationMenu ribbonAppMenu = this.applicationMenuPopupPanel .getRibbonAppMenu(); if (ribbonAppMenu != null) { List> primaryEntries = ribbonAppMenu .getPrimaryEntries(); int primaryGroupCount = primaryEntries.size(); for (int i = 0; i < primaryGroupCount; i++) { for (final RibbonApplicationMenuEntryPrimary menuEntry : primaryEntries .get(i)) { final JCommandMenuButton commandButton = new JCommandMenuButton( menuEntry.getText(), menuEntry.getIcon()); commandButton .setCommandButtonKind(menuEntry.getEntryKind()); commandButton.addActionListener(menuEntry .getMainActionListener()); commandButton.setActionKeyTip(menuEntry.getActionKeyTip()); commandButton.setPopupKeyTip(menuEntry.getPopupKeyTip()); if (menuEntry.getDisabledIcon() != null) { commandButton.setDisabledIcon(menuEntry .getDisabledIcon()); } if (menuEntry.getSecondaryGroupCount() == 0) { // if there are no secondary menu items, register the // application rollover callback to populate the // second level panel commandButton .addRolloverActionListener(new RolloverActionListener() { @Override public void actionPerformed(ActionEvent e) { // System.out.println("Rollover action"); PrimaryRolloverCallback callback = menuEntry .getRolloverCallback(); if (callback != null) { callback .menuEntryActivated(panelLevel2); } else { // default callback? PrimaryRolloverCallback defaultCallback = ribbonAppMenu .getDefaultCallback(); if (defaultCallback != null) { defaultCallback .menuEntryActivated(panelLevel2); } else { panelLevel2.removeAll(); panelLevel2.revalidate(); panelLevel2.repaint(); } } panelLevel2 .applyComponentOrientation(applicationMenuPopupPanel .getComponentOrientation()); } }); } else { // register a core callback to populate the second level // panel with secondary menu items final PrimaryRolloverCallback coreCallback = new PrimaryRolloverCallback() { @Override public void menuEntryActivated(JPanel targetPanel) { targetPanel.removeAll(); targetPanel.setLayout(new BorderLayout()); JRibbonApplicationMenuPopupPanelSecondary secondary = new JRibbonApplicationMenuPopupPanelSecondary( menuEntry) { @Override public void removeNotify() { super.removeNotify(); commandButton.getPopupModel() .setPopupShowing(false); } }; secondary .applyComponentOrientation(applicationMenuPopupPanel .getComponentOrientation()); targetPanel.add(secondary, BorderLayout.CENTER); } }; commandButton .addRolloverActionListener(new RolloverActionListener() { @Override public void actionPerformed(ActionEvent e) { coreCallback .menuEntryActivated(panelLevel2); // emulate showing the popup so the // button remains "selected" commandButton.getPopupModel() .setPopupShowing(true); } }); } commandButton.setDisplayState(MENU_TILE_LEVEL_1); commandButton .setHorizontalAlignment(SwingUtilities.LEADING); commandButton .setPopupOrientationKind(CommandButtonPopupOrientationKind.SIDEWARD); commandButton.setEnabled(menuEntry.isEnabled()); this.panelLevel1.add(commandButton); } if (i < (primaryGroupCount - 1)) { this.panelLevel1.add(new JPopupMenu.Separator()); } } } mainPanel.add(this.panelLevel1, BorderLayout.LINE_START); this.panelLevel2 = new JPanel(); this.panelLevel2.setBorder(new Border() { @Override public Insets getBorderInsets(Component c) { boolean ltr = c.getComponentOrientation().isLeftToRight(); return new Insets(0, ltr ? 1 : 0, 0, ltr ? 0 : 1); } @Override public boolean isBorderOpaque() { return true; } @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { g.setColor(FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground")); boolean ltr = c.getComponentOrientation().isLeftToRight(); int xToPaint = ltr ? x : x + width - 1; g.drawLine(xToPaint, y, xToPaint, y + height); } }); this.panelLevel2.setPreferredSize(new Dimension(30 * FlamingoUtilities .getFont(this.panelLevel1, "Ribbon.font", "Button.font", "Panel.font").getSize() - 30, 10)); mainPanel.add(this.panelLevel2, BorderLayout.CENTER); if (ribbonAppMenu != null) { if (ribbonAppMenu.getDefaultCallback() != null) { ribbonAppMenu.getDefaultCallback().menuEntryActivated( this.panelLevel2); } } this.applicationMenuPopupPanel.add(mainPanel, BorderLayout.CENTER); this.footerPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING)) { @Override protected void paintComponent(Graphics g) { FlamingoUtilities.renderSurface(g, footerPanel, new Rectangle( 0, 0, footerPanel.getWidth(), footerPanel.getHeight()), false, false, false); } }; if (ribbonAppMenu != null) { for (RibbonApplicationMenuEntryFooter footerEntry : ribbonAppMenu .getFooterEntries()) { JCommandButton commandFooterButton = new JCommandButton( footerEntry.getText(), footerEntry.getIcon()); if (footerEntry.getDisabledIcon() != null) { commandFooterButton.setDisabledIcon(footerEntry .getDisabledIcon()); } commandFooterButton .setCommandButtonKind(CommandButtonKind.ACTION_ONLY); commandFooterButton.addActionListener(footerEntry .getMainActionListener()); commandFooterButton .setDisplayState(CommandButtonDisplayState.MEDIUM); commandFooterButton.setFlat(false); commandFooterButton.setEnabled(footerEntry.isEnabled()); this.footerPanel.add(commandFooterButton); } } this.applicationMenuPopupPanel .add(this.footerPanel, BorderLayout.SOUTH); this.applicationMenuPopupPanel.setBorder(new Border() { @Override public Insets getBorderInsets(Component c) { return new Insets(20, 2, 2, 2); } @Override public boolean isBorderOpaque() { return true; } @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { g.setColor(FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground")); g.drawRect(x, y, width - 1, height - 1); g.setColor(FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground").brighter().brighter()); g.drawRect(x + 1, y + 1, width - 3, height - 3); FlamingoUtilities.renderSurface(g, applicationMenuPopupPanel, new Rectangle(x + 2, y + 2, width - 4, 24), false, false, false); // draw the application menu button JRibbonApplicationMenuButton button = applicationMenuPopupPanel .getAppMenuButton(); JRibbonApplicationMenuButton rendererButton = new JRibbonApplicationMenuButton( applicationMenuPopupPanel.getAppMenuButton() .getRibbon()); rendererButton.setPopupKeyTip(button.getPopupKeyTip()); rendererButton.setIcon(button.getIcon()); rendererButton.getPopupModel().setRollover(false); rendererButton.getPopupModel().setPressed(true); rendererButton.getPopupModel().setArmed(true); rendererButton.getPopupModel().setPopupShowing(true); CellRendererPane buttonRendererPane = new CellRendererPane(); Point buttonLoc = button.getLocationOnScreen(); Point panelLoc = c.getLocationOnScreen(); buttonRendererPane.setBounds(panelLoc.x - buttonLoc.x, panelLoc.y - buttonLoc.y, button.getWidth(), button .getHeight()); buttonRendererPane.paintComponent(g, rendererButton, (Container) c, -panelLoc.x + buttonLoc.x, -panelLoc.y + buttonLoc.y, button.getWidth(), button .getHeight(), true); } }); } protected JPanel createMainPanel() { JPanel result = new JPanel(new BorderLayout()); result.setBorder(new Border() { @Override public Insets getBorderInsets(Component c) { return new Insets(2, 2, 2, 2); } @Override public boolean isBorderOpaque() { return true; } @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { g.setColor(FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground").brighter().brighter()); g.drawRect(x, y, width - 1, height - 1); g.setColor(FlamingoUtilities.getColor(Color.gray, "Label.disabledForeground")); g.drawRect(x + 1, y + 1, width - 3, height - 3); } }); return result; } @Override protected void installListeners() { super.installListeners(); } @Override protected void uninstallDefaults() { super.uninstallDefaults(); } @Override protected void uninstallComponents() { super.uninstallComponents(); } @Override protected void uninstallListeners() { super.uninstallListeners(); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.ui.BasicCommandButtonUI#paint(java.awt.Graphics * , javax.swing.JComponent) */ @Override public void paint(Graphics g, JComponent c) { Graphics2D g2d = (Graphics2D) g.create(); g2d.dispose(); } public JPanel getPanelLevel1() { return panelLevel1; } public JPanel getPanelLevel2() { return panelLevel2; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootsrc/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/CommandButtonLayoutManagerMenuTileLevel2.javasrc/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/CommandButtonLayoutManagerMenuTileLevel2.j0000644000175000017500000003642511401230446033207 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon.appmenu; import java.awt.*; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute; import java.beans.PropertyChangeEvent; import java.text.AttributedString; import java.util.ArrayList; import javax.swing.JSeparator; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerMenuTileLevel2 implements CommandButtonLayoutManager { @Override public int getPreferredIconSize() { return 32; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = commandButton.getInsets(); int bx = borderInsets.left + borderInsets.right; int by = borderInsets.top + borderInsets.bottom; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); JSeparator jsep = new JSeparator(JSeparator.VERTICAL); int titleWidth = fm.stringWidth(commandButton.getText()); int layoutHGap = 2 * FlamingoUtilities.getHLayoutGap(commandButton); int layoutVGap = 2 * FlamingoUtilities.getVLayoutGap(commandButton); int widthMed = this.getPreferredIconSize() + 2 * layoutHGap + jsep.getPreferredSize().width + titleWidth + (FlamingoUtilities.hasPopupAction(commandButton) ? 1 + fm.getHeight() / 2 + 4 * layoutHGap + jsep.getPreferredSize().width : 0); // height - three lines of text and two gaps between them. // The gap between the lines is half the main gap. int fontHeight = fm.getAscent() + fm.getDescent(); int textHeight = fontHeight + layoutVGap; String extraText = commandButton.getExtraText(); if ((extraText != null) && (extraText.length() > 0)) { textHeight += 2 * fontHeight; } return new Dimension(bx + widthMed, by + Math.max(this.getPreferredIconSize(), textHeight)); } @Override public void propertyChange(PropertyChangeEvent evt) { } @Override public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) { Insets ins = commandButton.getInsets(); int height = commandButton.getHeight(); ResizableIcon buttonIcon = commandButton.getIcon(); // bottom-right corner of the icon area return new Point(ins.left + buttonIcon.getIconWidth(), (height + buttonIcon.getIconHeight()) / 2); } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, 0, 0); Insets ins = commandButton.getInsets(); result.iconRect = new Rectangle(); result.popupActionRect = new Rectangle(); int width = commandButton.getWidth(); int height = commandButton.getHeight(); FontMetrics fm = g.getFontMetrics(); int labelHeight = fm.getAscent() + fm.getDescent(); JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton) .getCommandButtonKind() : JCommandButton.CommandButtonKind.ACTION_ONLY; if (buttonKind == JCommandButton.CommandButtonKind.ACTION_ONLY) { result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; } if (buttonKind == JCommandButton.CommandButtonKind.POPUP_ONLY) { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; } JSeparator jsep = new JSeparator(JSeparator.VERTICAL); int layoutHGap = 2 * FlamingoUtilities.getHLayoutGap(commandButton); int layoutVGap = 2 * FlamingoUtilities.getVLayoutGap(commandButton); ResizableIcon buttonIcon = commandButton.getIcon(); boolean ltr = commandButton.getComponentOrientation().isLeftToRight(); if (ltr) { int x = ins.left; // medium icon, 1-line text, 1-line extra text and action arrow result.iconRect.x = x; result.iconRect.y = ins.top + layoutVGap; result.iconRect.width = buttonIcon.getIconWidth(); result.iconRect.height = buttonIcon.getIconHeight(); x += buttonIcon.getIconWidth(); if (buttonKind == JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP) { result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = x + layoutHGap; result.actionClickArea.height = height; result.popupClickArea.x = x + layoutHGap; result.popupClickArea.y = 0; result.popupClickArea.width = width - x - layoutHGap; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = x + layoutHGap; result.separatorArea.y = 0; result.separatorArea.width = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; result.separatorArea.height = height; result.isTextInActionArea = false; } x += 2 * layoutHGap + jsep.getPreferredSize().width; TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); lineLayoutInfo.textRect.x = x; lineLayoutInfo.textRect.y = ins.top + layoutVGap / 2; lineLayoutInfo.textRect.width = fm.stringWidth(commandButton .getText()); lineLayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); String extraText = commandButton.getExtraText(); if ((extraText == null) || (extraText.length() == 0)) { lineLayoutInfo.textRect.y = (height - labelHeight) / 2; } else { AttributedString attributedDescription = new AttributedString( commandButton.getExtraText()); attributedDescription.addAttribute(TextAttribute.FONT, g .getFont()); LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer( attributedDescription.getIterator(), ((Graphics2D) g) .getFontRenderContext()); // The max width of the extra text line - need to leave // space for the popup arrow icon int maxFirstExtraLineWidth = width - x - ins.right - layoutHGap - labelHeight; int breakIndex = lineBreakMeasurer .nextOffset(maxFirstExtraLineWidth); TextLayoutInfo extraLineLayoutInfo1 = new TextLayoutInfo(); extraLineLayoutInfo1.text = commandButton.getExtraText() .substring(0, breakIndex); extraLineLayoutInfo1.textRect = new Rectangle(); extraLineLayoutInfo1.textRect.x = x; extraLineLayoutInfo1.textRect.y = ins.top + layoutVGap + labelHeight; extraLineLayoutInfo1.textRect.width = fm .stringWidth(extraLineLayoutInfo1.text); extraLineLayoutInfo1.textRect.height = labelHeight; TextLayoutInfo extraLineLayoutInfo2 = new TextLayoutInfo(); extraLineLayoutInfo2.text = commandButton.getExtraText() .substring(breakIndex); extraLineLayoutInfo2.textRect = new Rectangle(); extraLineLayoutInfo2.textRect.x = x; extraLineLayoutInfo2.textRect.y = ins.top + layoutVGap + 2 * labelHeight; extraLineLayoutInfo2.textRect.width = fm .stringWidth(extraLineLayoutInfo2.text); extraLineLayoutInfo2.textRect.height = labelHeight; result.extraTextLayoutInfoList = new ArrayList(); result.extraTextLayoutInfoList.add(extraLineLayoutInfo1); result.extraTextLayoutInfoList.add(extraLineLayoutInfo2); } x += fm.getStringBounds(commandButton.getText(), g).getWidth(); if (buttonKind == JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION) { // popup click areas are right aligned result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width - ins.right - labelHeight; result.actionClickArea.height = height; result.popupClickArea.x = width - ins.right - labelHeight; result.popupClickArea.y = 0; result.popupClickArea.width = labelHeight + ins.right; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = width - ins.right - labelHeight; result.separatorArea.y = 0; result.separatorArea.width = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; result.separatorArea.height = height; result.isTextInActionArea = true; } if (FlamingoUtilities.hasPopupAction(commandButton)) { result.popupActionRect.x = width - ins.right - labelHeight * 3 / 4; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.height = labelHeight + 2; } } else { int x = commandButton.getWidth() - ins.right; // medium icon, 1-line text, 1-line extra text and action arrow result.iconRect.x = x - buttonIcon.getIconWidth(); result.iconRect.y = ins.top + layoutVGap; result.iconRect.width = buttonIcon.getIconWidth(); result.iconRect.height = buttonIcon.getIconHeight(); x -= buttonIcon.getIconWidth(); if (buttonKind == JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP) { result.actionClickArea.x = x + layoutHGap; result.actionClickArea.y = 0; result.actionClickArea.width = width - x - layoutHGap; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = x + layoutHGap; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = x + layoutHGap; result.separatorArea.y = 0; result.separatorArea.width = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; result.separatorArea.height = height; result.isTextInActionArea = false; } x -= (2 * layoutHGap + jsep.getPreferredSize().width); TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); lineLayoutInfo.textRect.width = fm.stringWidth(commandButton .getText()); lineLayoutInfo.textRect.x = x - lineLayoutInfo.textRect.width; lineLayoutInfo.textRect.y = ins.top + layoutVGap / 2; lineLayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); String extraText = commandButton.getExtraText(); if ((extraText == null) || (extraText.length() == 0)) { lineLayoutInfo.textRect.y = (height - labelHeight) / 2; } else { AttributedString attributedDescription = new AttributedString( commandButton.getExtraText()); attributedDescription.addAttribute(TextAttribute.FONT, g .getFont()); LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer( attributedDescription.getIterator(), ((Graphics2D) g) .getFontRenderContext()); // The max width of the extra text line - need to leave // space for the popup arrow icon int maxFirstExtraLineWidth = x - ins.left - layoutHGap - labelHeight; int breakIndex = lineBreakMeasurer .nextOffset(maxFirstExtraLineWidth); TextLayoutInfo extraLineLayoutInfo1 = new TextLayoutInfo(); extraLineLayoutInfo1.text = commandButton.getExtraText() .substring(0, breakIndex); extraLineLayoutInfo1.textRect = new Rectangle(); extraLineLayoutInfo1.textRect.width = fm .stringWidth(extraLineLayoutInfo1.text); extraLineLayoutInfo1.textRect.x = x - extraLineLayoutInfo1.textRect.width; extraLineLayoutInfo1.textRect.y = ins.top + layoutVGap + labelHeight; extraLineLayoutInfo1.textRect.height = labelHeight; TextLayoutInfo extraLineLayoutInfo2 = new TextLayoutInfo(); extraLineLayoutInfo2.text = commandButton.getExtraText() .substring(breakIndex); extraLineLayoutInfo2.textRect = new Rectangle(); extraLineLayoutInfo2.textRect.width = fm .stringWidth(extraLineLayoutInfo2.text); extraLineLayoutInfo2.textRect.x = x - extraLineLayoutInfo2.textRect.width; extraLineLayoutInfo2.textRect.y = ins.top + layoutVGap + 2 * labelHeight; extraLineLayoutInfo2.textRect.height = labelHeight; result.extraTextLayoutInfoList = new ArrayList(); result.extraTextLayoutInfoList.add(extraLineLayoutInfo1); result.extraTextLayoutInfoList.add(extraLineLayoutInfo2); } if (buttonKind == JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION) { // popup click areas are left aligned result.actionClickArea.x = labelHeight + ins.left; result.actionClickArea.y = 0; result.actionClickArea.width = width - ins.right - labelHeight; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = ins.left + labelHeight; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = labelHeight + ins.left; result.separatorArea.y = 0; result.separatorArea.width = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; result.separatorArea.height = height; result.isTextInActionArea = true; } if (FlamingoUtilities.hasPopupAction(commandButton)) { result.popupActionRect.x = ins.left + labelHeight / 4; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.height = labelHeight + 2; } } return result; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/BasicRibbonApplicationMenuButtonUI.java0000644000175000017500000002030211401230446032522 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon.appmenu; import java.awt.*; import java.awt.geom.Ellipse2D; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.*; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; import org.pushingpixels.flamingo.api.common.popup.PopupPanelCallback; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonUI; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Basic UI for ribbon application menu button * {@link JRibbonApplicationMenuButton}. * * @author Kirill Grouchnikov */ public class BasicRibbonApplicationMenuButtonUI extends BasicCommandButtonUI { /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicRibbonApplicationMenuButtonUI(); } /** * The associated application menu button. */ protected JRibbonApplicationMenuButton applicationMenuButton; /* * (non-Javadoc) * * @see * javax.swing.plaf.basic.BasicButtonUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.applicationMenuButton = (JRibbonApplicationMenuButton) c; super.installUI(c); } @Override protected void installDefaults() { super.installDefaults(); Border border = this.commandButton.getBorder(); if (border == null || border instanceof UIResource) { Border toInstall = UIManager .getBorder("RibbonApplicationMenuButton.border"); if (toInstall == null) toInstall = new BorderUIResource.EmptyBorderUIResource(4, 4, 4, 4); this.commandButton.setBorder(toInstall); } this.commandButton.setOpaque(false); } @Override protected void configureRenderer() { this.buttonRendererPane = new CellRendererPane(); this.commandButton.add(buttonRendererPane); this.rendererButton = new JButton(""); } @Override protected void unconfigureRenderer() { this.commandButton.remove(this.buttonRendererPane); this.buttonRendererPane = null; this.rendererButton = null; } @Override protected void installComponents() { super.installComponents(); final JRibbonApplicationMenuButton appMenuButton = (JRibbonApplicationMenuButton) this.commandButton; appMenuButton.setPopupCallback(new PopupPanelCallback() { @Override public JPopupPanel getPopupPanel(final JCommandButton commandButton) { JRibbonFrame ribbonFrame = (JRibbonFrame) SwingUtilities .getWindowAncestor(commandButton); final JRibbon ribbon = ribbonFrame.getRibbon(); RibbonApplicationMenu ribbonMenu = ribbon.getApplicationMenu(); final JRibbonApplicationMenuPopupPanel menuPopupPanel = new JRibbonApplicationMenuPopupPanel( appMenuButton, ribbonMenu); menuPopupPanel.applyComponentOrientation(appMenuButton .getComponentOrientation()); menuPopupPanel .setCustomizer(new JPopupPanel.PopupPanelCustomizer() { @Override public Rectangle getScreenBounds() { boolean ltr = commandButton .getComponentOrientation() .isLeftToRight(); int pw = menuPopupPanel.getPreferredSize().width; int x = ltr ? ribbon.getLocationOnScreen().x : ribbon.getLocationOnScreen().x + ribbon.getWidth() - pw; int y = commandButton.getLocationOnScreen().y + commandButton.getSize().height / 2 + 2; // make sure that the menu popup stays // in bounds Rectangle scrBounds = commandButton .getGraphicsConfiguration().getBounds(); if ((x + pw) > (scrBounds.x + scrBounds.width)) { x = scrBounds.x + scrBounds.width - pw; } int ph = menuPopupPanel.getPreferredSize().height; if ((y + ph) > (scrBounds.y + scrBounds.height)) { y = scrBounds.y + scrBounds.height - ph; } return new Rectangle( x, y, menuPopupPanel.getPreferredSize().width, menuPopupPanel.getPreferredSize().height); } }); return menuPopupPanel; } }); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.ui.BasicCommandButtonUI#paint(java.awt.Graphics * , javax.swing.JComponent) */ @Override public void paint(Graphics g, JComponent c) { Graphics2D g2d = (Graphics2D) g.create(); // g2d.setColor(Color.red); // g2d.fillRect(0, 0, c.getWidth(), c.getHeight()); Insets ins = c.getInsets(); // System.out.println(c.getWidth() + ":" + c.getHeight()); this.paintButtonBackground(g2d, new Rectangle(ins.left, ins.top, c .getWidth() - ins.left - ins.right, c.getHeight() - ins.top - ins.bottom)); this.layoutInfo = this.layoutManager.getLayoutInfo(this.commandButton, g); commandButton.putClientProperty("icon.bounds", layoutInfo.iconRect); this.paintButtonIcon(g2d, layoutInfo.iconRect); g2d.dispose(); } /** * Paints the button background. * * @param graphics * Graphics context. * @param toFill * Rectangle to fill. * @param button * The button itself. */ @Override protected void paintButtonBackground(Graphics graphics, Rectangle toFill) { // graphics.setColor(Color.red); // graphics.fillRect(toFill.x, toFill.y, toFill.width, // toFill.height); // if (true) // return; // // System.out.println(toFill); this.buttonRendererPane.setBounds(toFill.x, toFill.y, toFill.width, toFill.height); ButtonModel model = this.rendererButton.getModel(); model.setEnabled(true); model.setSelected(this.applicationMenuButton.getPopupModel() .isSelected()); model.setRollover(this.applicationMenuButton.getPopupModel() .isRollover()); model.setPressed(this.applicationMenuButton.getPopupModel().isPressed() || this.applicationMenuButton.getPopupModel().isPopupShowing()); model.setArmed(this.applicationMenuButton.getActionModel().isArmed()); Graphics2D g2d = (Graphics2D) graphics.create(); g2d.translate(toFill.x, toFill.y); Shape clip = g2d.getClip(); g2d.clip(new Ellipse2D.Double(0, 0, toFill.width, toFill.height)); this.rendererButton.setBorderPainted(false); this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.applicationMenuButton, -toFill.width / 2, -toFill.height / 2, 2 * toFill.width, 2 * toFill.height, true); g2d.setColor(FlamingoUtilities.getBorderColor().darker()); g2d.setClip(clip); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.draw(new Ellipse2D.Double(0, 0, toFill.width, toFill.height)); g2d.dispose(); } } src/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/JRibbonApplicationMenuButton.java0000644000175000017500000001164611410720156031451 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon.appmenu; import java.awt.*; import java.beans.PropertyChangeEvent; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.EmptyResizableIcon; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.ribbon.JRibbon; import org.pushingpixels.flamingo.api.ribbon.JRibbonFrame; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonUI; /** * The main application menu button for {@link JRibbon} component placed in a * {@link JRibbonFrame}. This class is for internal use only and is intended for * look-and-feel layer customization. * * @author Kirill Grouchnikov */ public class JRibbonApplicationMenuButton extends JCommandButton { private JRibbon ribbon; /** * The UI class ID string. */ public static final String uiClassID = "RibbonApplicationMenuButtonUI"; private final static CommandButtonDisplayState APP_MENU_BUTTON_STATE = new CommandButtonDisplayState( "Ribbon Application Menu Button", 24) { @Override public org.pushingpixels.flamingo.api.common.CommandButtonLayoutManager createLayoutManager( org.pushingpixels.flamingo.api.common.AbstractCommandButton commandButton) { return new CommandButtonLayoutManager() { public int getPreferredIconSize() { return 24; } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, commandButton .getWidth(), commandButton.getHeight()); result.popupActionRect = new Rectangle(0, 0, 0, 0); ResizableIcon icon = commandButton.getIcon(); result.iconRect = new Rectangle( (commandButton.getWidth() - icon.getIconWidth()) / 2, (commandButton.getHeight() - icon.getIconHeight()) / 2, icon.getIconWidth(), icon.getIconHeight()); result.isTextInActionArea = false; return result; } @Override public Dimension getPreferredSize( AbstractCommandButton commandButton) { return new Dimension(40, 40); } @Override public void propertyChange(PropertyChangeEvent evt) { } @Override public Point getKeyTipAnchorCenterPoint( AbstractCommandButton commandButton) { // dead center return new Point(commandButton.getWidth() / 2, commandButton.getHeight() / 2); } }; } }; /** * Creates a new application menu button. */ public JRibbonApplicationMenuButton(JRibbon ribbon) { super("", new EmptyResizableIcon(16)); this.setCommandButtonKind(CommandButtonKind.POPUP_ONLY); this.setDisplayState(APP_MENU_BUTTON_STATE); this.ribbon = ribbon; } /* * (non-Javadoc) * * @see javax.swing.JButton#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((BasicCommandButtonUI) UIManager.getUI(this)); } else { setUI(BasicRibbonApplicationMenuButtonUI.createUI(this)); } } /* * (non-Javadoc) * * @see javax.swing.JButton#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } public JRibbon getRibbon() { return this.ribbon; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/JRibbonApplicationMenuPopupPanel.java0000644000175000017500000000640411401230446032253 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon.appmenu; import javax.swing.JPanel; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu; import org.pushingpixels.flamingo.internal.ui.common.popup.BasicPopupPanelUI; public class JRibbonApplicationMenuPopupPanel extends JPopupPanel { /** * The UI class ID string. */ public static final String uiClassID = "RibbonApplicationMenuPopupPanelUI"; protected JRibbonApplicationMenuButton appMenuButton; protected RibbonApplicationMenu ribbonAppMenu; public JRibbonApplicationMenuPopupPanel( final JRibbonApplicationMenuButton button, RibbonApplicationMenu ribbonAppMenu) { this.appMenuButton = button; this.ribbonAppMenu = ribbonAppMenu; this.updateUI(); } public JPanel getPanelLevel1() { return ((BasicRibbonApplicationMenuPopupPanelUI) getUI()) .getPanelLevel1(); } public JPanel getPanelLevel2() { return ((BasicRibbonApplicationMenuPopupPanelUI) getUI()) .getPanelLevel2(); } /* * (non-Javadoc) * * @see javax.swing.JButton#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((BasicPopupPanelUI) UIManager.getUI(this)); } else { setUI(BasicRibbonApplicationMenuPopupPanelUI.createUI(this)); } } /* * (non-Javadoc) * * @see javax.swing.JButton#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } public JRibbonApplicationMenuButton getAppMenuButton() { return appMenuButton; } public RibbonApplicationMenu getRibbonAppMenu() { return ribbonAppMenu; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootsrc/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/JRibbonApplicationMenuPopupPanelSecondary.javasrc/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/JRibbonApplicationMenuPopupPanelSecondary.0000644000175000017500000000751211422071032033257 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon.appmenu; import javax.swing.SwingUtilities; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonPopupOrientationKind; import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary; import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntrySecondary; public class JRibbonApplicationMenuPopupPanelSecondary extends JCommandButtonPanel { protected static final CommandButtonDisplayState MENU_TILE_LEVEL_2 = new CommandButtonDisplayState( "Ribbon application menu tile level 2", 32) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton commandButton) { return new CommandButtonLayoutManagerMenuTileLevel2(); } }; public JRibbonApplicationMenuPopupPanelSecondary( RibbonApplicationMenuEntryPrimary primaryMenuEntry) { super(MENU_TILE_LEVEL_2); this.setMaxButtonColumns(1); int groupCount = primaryMenuEntry.getSecondaryGroupCount(); for (int i = 0; i < groupCount; i++) { String groupDesc = primaryMenuEntry.getSecondaryGroupTitleAt(i); this.addButtonGroup(groupDesc); for (final RibbonApplicationMenuEntrySecondary menuEntry : primaryMenuEntry .getSecondaryGroupEntries(i)) { JCommandMenuButton commandButton = new JCommandMenuButton( menuEntry.getText(), menuEntry.getIcon()); commandButton.setExtraText(menuEntry.getDescriptionText()); commandButton.setCommandButtonKind(menuEntry.getEntryKind()); commandButton.addActionListener(menuEntry .getMainActionListener()); commandButton.setDisplayState(MENU_TILE_LEVEL_2); commandButton.setHorizontalAlignment(SwingUtilities.LEADING); commandButton .setPopupOrientationKind(CommandButtonPopupOrientationKind.SIDEWARD); commandButton.setEnabled(menuEntry.isEnabled()); commandButton.setPopupCallback(menuEntry.getPopupCallback()); commandButton.setActionKeyTip(menuEntry.getActionKeyTip()); commandButton.setPopupKeyTip(menuEntry.getPopupKeyTip()); if (menuEntry.getDisabledIcon() != null) { commandButton.setDisabledIcon(menuEntry.getDisabledIcon()); } this.addButtonToLastGroup(commandButton); } } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootsrc/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/CommandButtonLayoutManagerMenuTileLevel1.javasrc/org/pushingpixels/flamingo/internal/ui/ribbon/appmenu/CommandButtonLayoutManagerMenuTileLevel1.j0000644000175000017500000002570511401230446033205 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon.appmenu; import java.awt.*; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import javax.swing.JSeparator; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerMenuTileLevel1 implements CommandButtonLayoutManager { @Override public int getPreferredIconSize() { return 32; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = commandButton.getInsets(); int bx = borderInsets.left + borderInsets.right; int by = borderInsets.top + borderInsets.bottom; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); JSeparator jsep = new JSeparator(JSeparator.VERTICAL); int titleWidth = fm.stringWidth(commandButton.getText()); int layoutHGap = 2 * FlamingoUtilities.getHLayoutGap(commandButton); int layoutVGap = 2 * FlamingoUtilities.getVLayoutGap(commandButton); int widthMed = this.getPreferredIconSize() + 2 * layoutHGap + jsep.getPreferredSize().width + titleWidth + (FlamingoUtilities.hasPopupAction(commandButton) ? 1 + fm.getHeight() / 2 + 4 * layoutHGap + jsep.getPreferredSize().width : 0); return new Dimension(bx + widthMed, by + Math.max(this.getPreferredIconSize(), 2 * (fm.getAscent() + fm.getDescent()) + layoutVGap)); } @Override public void propertyChange(PropertyChangeEvent evt) { } @Override public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) { Insets ins = commandButton.getInsets(); int height = commandButton.getHeight(); ResizableIcon buttonIcon = commandButton.getIcon(); // bottom-right corner of the icon area return new Point(ins.left + buttonIcon.getIconWidth(), height - ins.top - ins.bottom); } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, 0, 0); Insets ins = commandButton.getInsets(); result.iconRect = new Rectangle(); result.popupActionRect = new Rectangle(); int width = commandButton.getWidth(); int height = commandButton.getHeight(); FontMetrics fm = g.getFontMetrics(); int labelHeight = fm.getAscent() + fm.getDescent(); JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton) .getCommandButtonKind() : JCommandButton.CommandButtonKind.ACTION_ONLY; if (buttonKind == JCommandButton.CommandButtonKind.ACTION_ONLY) { result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; } if (buttonKind == JCommandButton.CommandButtonKind.POPUP_ONLY) { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; result.isTextInActionArea = false; } JSeparator jsep = new JSeparator(JSeparator.VERTICAL); int layoutHGap = 2 * FlamingoUtilities.getHLayoutGap(commandButton); boolean ltr = commandButton.getComponentOrientation().isLeftToRight(); ResizableIcon buttonIcon = commandButton.getIcon(); if (ltr) { int x = ins.left; // small icon, 1-line text, 1-line extra text and action arrow result.iconRect.x = x; result.iconRect.y = (height - buttonIcon.getIconHeight()) / 2; result.iconRect.width = buttonIcon.getIconWidth(); result.iconRect.height = buttonIcon.getIconHeight(); x += commandButton.getIcon().getIconWidth(); if (buttonKind == JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP) { result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = x + layoutHGap; result.actionClickArea.height = height; result.popupClickArea.x = x + layoutHGap; result.popupClickArea.y = 0; result.popupClickArea.width = width - x - layoutHGap; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = x + layoutHGap; result.separatorArea.y = 0; result.separatorArea.width = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; result.separatorArea.height = height; result.isTextInActionArea = false; } x += 2 * layoutHGap + jsep.getPreferredSize().width; TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); lineLayoutInfo.textRect.x = x; lineLayoutInfo.textRect.y = (height - labelHeight) / 2; lineLayoutInfo.textRect.width = fm.stringWidth(commandButton .getText()); lineLayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); x += fm.getStringBounds(commandButton.getText(), g).getWidth(); if (buttonKind == JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION) { // popup click areas are right aligned result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width - ins.right - labelHeight; result.actionClickArea.height = height; result.popupClickArea.x = width - ins.right - labelHeight; result.popupClickArea.y = 0; result.popupClickArea.width = labelHeight + ins.right; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = width - ins.right - labelHeight; result.separatorArea.y = 0; result.separatorArea.width = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; result.separatorArea.height = height; result.isTextInActionArea = true; } if (FlamingoUtilities.hasPopupAction(commandButton)) { result.popupActionRect.x = width - ins.right - labelHeight * 3 / 4; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.height = labelHeight + 2; } } else { int x = commandButton.getWidth() - ins.right; // small icon, 1-line text, 1-line extra text and action arrow result.iconRect.x = x - buttonIcon.getIconWidth(); result.iconRect.y = (height - buttonIcon.getIconHeight()) / 2; result.iconRect.width = buttonIcon.getIconWidth(); result.iconRect.height = buttonIcon.getIconHeight(); x -= commandButton.getIcon().getIconWidth(); if (buttonKind == JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP) { result.actionClickArea.x = x + layoutHGap; result.actionClickArea.y = 0; result.actionClickArea.width = width - x - layoutHGap; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = x + layoutHGap; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = x + layoutHGap; result.separatorArea.y = 0; result.separatorArea.width = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; result.separatorArea.height = height; result.isTextInActionArea = false; } x -= (2 * layoutHGap + jsep.getPreferredSize().width); TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); lineLayoutInfo.textRect.width = fm.stringWidth(commandButton .getText()); lineLayoutInfo.textRect.x = x - lineLayoutInfo.textRect.width; lineLayoutInfo.textRect.y = (height - labelHeight) / 2; lineLayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); if (buttonKind == JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION) { // popup click areas are left aligned result.actionClickArea.x = labelHeight + ins.left; result.actionClickArea.y = 0; result.actionClickArea.width = width - ins.right - labelHeight; result.actionClickArea.height = height; result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = ins.left + labelHeight; result.popupClickArea.height = height; result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL; result.separatorArea = new Rectangle(); result.separatorArea.x = labelHeight + ins.left; result.separatorArea.y = 0; result.separatorArea.width = new JSeparator(JSeparator.VERTICAL) .getPreferredSize().width; result.separatorArea.height = height; result.isTextInActionArea = true; } if (FlamingoUtilities.hasPopupAction(commandButton)) { result.popupActionRect.x = ins.left + labelHeight / 4; result.popupActionRect.y = (height - labelHeight) / 2 - 1; result.popupActionRect.width = 1 + labelHeight / 2; result.popupActionRect.height = labelHeight + 2; } } return result; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/BasicRibbonGalleryUI.java0000644000175000017500000006313411401230446026202 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.*; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButtonStrip.StripOrientation; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.popup.*; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager.PopupEvent; import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonUI; import org.pushingpixels.flamingo.internal.utils.*; /** * Basic UI for ribbon gallery {@link JRibbonGallery}. * * @author Kirill Grouchnikov */ public class BasicRibbonGalleryUI extends RibbonGalleryUI { /** * The associated ribbon gallery. */ protected JRibbonGallery ribbonGallery; /** * The index of the first visible button. */ protected int firstVisibleButtonIndex; /** * The count of visible buttons. */ protected int visibleButtonsInEachRow; protected int visibleButtonRowNumber; /** * The button that scrolls down the associated {@link #ribbonGallery}. */ protected JCommandButton scrollDownButton; /** * The button that scrolls up the associated {@link #ribbonGallery}. */ protected JCommandButton scrollUpButton; /** * The button that shows the associated popup gallery. */ protected ExpandCommandButton expandActionButton; /** * Contains the scroll down, scroll up and show popup buttons. * * @see #scrollDownButton * @see #scrollUpButton * @see #expandActionButton */ protected JCommandButtonStrip buttonStrip; /** * Listener on the gallery scroll-down button. */ protected ActionListener scrollDownListener; /** * Listener on the gallery scroll-up button. */ protected ActionListener scrollUpListener; /** * Listener on the gallery expand button. */ protected ActionListener expandListener; /** * Listener on the {@link PopupPanelManager} changes to sync the * {@link JRibbonGallery#setShowingPopupPanel(boolean)} once the popup * gallery is dismissed by the user. */ protected PopupPanelManager.PopupListener popupListener; /** * Property change listener. */ protected PropertyChangeListener propertyChangeListener; /** * Ribbon gallery margin. */ protected Insets margin; /** * Button strip as a UI resource. * * @author Kirill Grouchnikov */ protected static class JButtonStripUIResource extends JCommandButtonStrip implements UIResource { /** * Creates a new UI-resource button strip. */ public JButtonStripUIResource() { super(); } /** * Creates a new UI-resource button strip. * * @param orientation * Orientation for this strip. */ public JButtonStripUIResource(StripOrientation orientation) { super(orientation); } } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicRibbonGalleryUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.ribbonGallery = (JRibbonGallery) c; this.firstVisibleButtonIndex = 0; // this.visibleButtonsCount = 0; this.installDefaults(); this.installComponents(); this.installListeners(); c.setLayout(createLayoutManager()); } /** * Installs subcomponents on the associated ribbon gallery. */ protected void installComponents() { this.buttonStrip = new JButtonStripUIResource(StripOrientation.VERTICAL); this.buttonStrip.setDisplayState(CommandButtonDisplayState.FIT_TO_ICON); this.ribbonGallery.add(this.buttonStrip); this.scrollUpButton = this.createScrollUpButton(); this.scrollDownButton = this.createScrollDownButton(); this.expandActionButton = this.createExpandButton(); this.syncExpandKeyTip(); this.buttonStrip.add(this.scrollUpButton); this.buttonStrip.add(this.scrollDownButton); this.buttonStrip.add(this.expandActionButton); } /** * Creates the scroll-down button. * * @return Scroll-down button. */ protected JCommandButton createScrollDownButton() { JCommandButton result = new JCommandButton(new ArrowResizableIcon(9, SwingConstants.SOUTH)); result.setFocusable(false); result.setName("RibbonGallery.scrollDownButton"); result.setFlat(false); result.putClientProperty(BasicCommandButtonUI.DONT_DISPOSE_POPUPS, Boolean.TRUE); result.setAutoRepeatAction(true); return result; } /** * Creates the scroll-up button. * * @return Scroll-up button. */ protected JCommandButton createScrollUpButton() { JCommandButton result = new JCommandButton(new ArrowResizableIcon(9, SwingConstants.NORTH)); result.setFocusable(false); result.setName("RibbonGallery.scrollUpButton"); result.setFlat(false); result.putClientProperty(BasicCommandButtonUI.DONT_DISPOSE_POPUPS, Boolean.TRUE); result.setAutoRepeatAction(true); return result; } /** * Creates the expand button. * * @return Expand button. */ protected ExpandCommandButton createExpandButton() { ExpandCommandButton result = new ExpandCommandButton( new DoubleArrowResizableIcon(9, SwingConstants.SOUTH)); result.getActionModel().setFireActionOnPress(true); result.setFocusable(false); result.setName("RibbonGallery.expandButton"); result.setFlat(false); result.putClientProperty(BasicCommandButtonUI.DONT_DISPOSE_POPUPS, Boolean.TRUE); return result; } /** * Uninstalls subcomponents from the associated ribbon gallery. */ protected void uninstallComponents() { this.buttonStrip.remove(this.scrollUpButton); this.buttonStrip.remove(this.scrollDownButton); this.buttonStrip.remove(this.expandActionButton); this.ribbonGallery.remove(this.buttonStrip); } /** * Installs defaults on the associated ribbon gallery. */ protected void installDefaults() { this.margin = UIManager.getInsets("RibbonGallery.margin"); if (this.margin == null) this.margin = new Insets(3, 3, 3, 3); Border b = this.ribbonGallery.getBorder(); if (b == null || b instanceof UIResource) { Border toSet = UIManager.getBorder("RibbonGallery.border"); if (toSet == null) toSet = new BorderUIResource.EmptyBorderUIResource(2, 2, 2, 2); this.ribbonGallery.setBorder(toSet); } this.ribbonGallery.setOpaque(false); } /** * Uninstalls defaults from the associated ribbon gallery. */ protected void uninstallDefaults() { } /** * Installs listeners on the associated ribbon gallery. */ protected void installListeners() { this.scrollDownListener = new ActionListener() { public void actionPerformed(ActionEvent e) { scrollOneRowDown(); ribbonGallery.revalidate(); } }; this.scrollDownButton.addActionListener(this.scrollDownListener); this.scrollUpListener = new ActionListener() { public void actionPerformed(ActionEvent e) { scrollOneRowUp(); ribbonGallery.revalidate(); } }; this.scrollUpButton.addActionListener(this.scrollUpListener); this.expandListener = new ActionListener() { public void actionPerformed(ActionEvent e) { PopupPanelManager.defaultManager().hidePopups(ribbonGallery); SwingUtilities.invokeLater(new Runnable() { public void run() { PopupFactory popupFactory = PopupFactory .getSharedInstance(); JCommandButtonPanel popupButtonPanel = ribbonGallery .getPopupButtonPanel(); final Point loc = ribbonGallery.getLocationOnScreen(); final JCommandPopupMenu popupMenu = new JCommandPopupMenu( popupButtonPanel, ribbonGallery .getPreferredPopupMaxButtonColumns(), ribbonGallery .getPreferredPopupMaxVisibleButtonRows()); if (ribbonGallery.getPopupCallback() != null) { ribbonGallery.getPopupCallback().popupToBeShown( popupMenu); } popupMenu.applyComponentOrientation(ribbonGallery .getComponentOrientation()); popupMenu .setCustomizer(new JPopupPanel.PopupPanelCustomizer() { @Override public Rectangle getScreenBounds() { Rectangle scrBounds = ribbonGallery .getGraphicsConfiguration() .getBounds(); boolean ltr = popupMenu .getComponentOrientation() .isLeftToRight(); Dimension pref = popupMenu .getPreferredSize(); int width = Math.max(pref.width, ribbonGallery.getWidth()); int height = pref.height; int x = ltr ? loc.x : loc.x + width - pref.width; int y = loc.y; // make sure that the popup stays in // bounds if ((x + width) > (scrBounds.x + scrBounds.width)) { x = scrBounds.x + scrBounds.width - width; } if ((y + height) > (scrBounds.y + scrBounds.height)) { y = scrBounds.y + scrBounds.height - height; } return new Rectangle(x, y, width, height); } }); // mark the gallery so that it doesn't try to re-layout // itself. ribbonGallery.setShowingPopupPanel(true); // get the popup and show it Dimension pref = popupMenu.getPreferredSize(); int width = Math.max(pref.width, ribbonGallery .getWidth()); boolean ltr = ribbonGallery.getComponentOrientation() .isLeftToRight(); int x = ltr ? loc.x : loc.x + ribbonGallery.getWidth() - width; Popup popup = popupFactory.getPopup(ribbonGallery, popupMenu, x, loc.y); ribbonGallery.repaint(); PopupPanelManager.defaultManager().addPopup( ribbonGallery, popup, popupMenu); // scroll to reveal the selected button if (popupButtonPanel.getSelectedButton() != null) { Rectangle selectionButtonBounds = popupButtonPanel .getSelectedButton().getBounds(); popupButtonPanel .scrollRectToVisible(selectionButtonBounds); } } }); } }; this.expandActionButton.addActionListener(this.expandListener); this.popupListener = new PopupPanelManager.PopupListener() { @Override public void popupHidden(PopupEvent event) { if (event.getPopupOriginator() == ribbonGallery) { // reset the rollover state for all the buttons // in the gallery for (int i = 0; i < ribbonGallery.getButtonCount(); i++) { ribbonGallery.getButtonAt(i).getActionModel() .setRollover(false); } ribbonGallery.setShowingPopupPanel(false); } } @Override public void popupShown(PopupEvent event) { } }; PopupPanelManager.defaultManager().addPopupListener(this.popupListener); this.propertyChangeListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("selectedButton".equals(evt.getPropertyName())) { scrollToSelected(); ribbonGallery.revalidate(); } if ("expandKeyTip".equals(evt.getPropertyName())) { syncExpandKeyTip(); } if ("buttonDisplayState".equals(evt.getPropertyName())) { firstVisibleButtonIndex = 0; ribbonGallery.revalidate(); } } }; this.ribbonGallery .addPropertyChangeListener(this.propertyChangeListener); } /** * Uninstalls listeners from the associated ribbon gallery. */ protected void uninstallListeners() { this.scrollDownButton.removeActionListener(this.scrollDownListener); this.scrollDownListener = null; this.scrollUpButton.removeActionListener(this.scrollUpListener); this.scrollUpListener = null; this.expandActionButton.removeActionListener(this.expandListener); this.expandListener = null; PopupPanelManager.defaultManager().removePopupListener( this.popupListener); this.popupListener = null; this.ribbonGallery .removePropertyChangeListener(this.propertyChangeListener); this.propertyChangeListener = null; } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { c.setLayout(null); this.uninstallListeners(); this.uninstallDefaults(); this.uninstallComponents(); this.ribbonGallery = null; } /** * Invoked by installUI to create a layout manager object to * manage the {@link JCommandButtonStrip}. * * @return a layout manager object */ protected LayoutManager createLayoutManager() { return new RibbonGalleryLayout(); } /** * Layout for the ribbon gallery. * * @author Kirill Grouchnikov */ private class RibbonGalleryLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { return new Dimension(ribbonGallery.getPreferredWidth(ribbonGallery .getDisplayPriority(), c.getHeight()), c.getHeight()); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { return this.preferredLayoutSize(c); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { int width = c.getWidth(); int height = c.getHeight(); Insets borderInsets = ribbonGallery.getInsets(); int galleryHeight = height - margin.top - margin.bottom; int buttonHeight = galleryHeight - borderInsets.top - borderInsets.bottom; visibleButtonRowNumber = 1; CommandButtonDisplayState galleryButtonDisplayState = ribbonGallery .getButtonDisplayState(); if (galleryButtonDisplayState == CommandButtonDisplayState.SMALL) { buttonHeight /= 3; visibleButtonRowNumber = 3; } boolean ltr = c.getComponentOrientation().isLeftToRight(); int scrollerButtonHeight = galleryHeight / 3; int scrollerButtonWidth = 15; int buttonX = ltr ? width - scrollerButtonWidth - margin.right : margin.left; scrollDownButton.setPreferredSize(new Dimension( scrollerButtonWidth, scrollerButtonHeight)); scrollUpButton.setPreferredSize(new Dimension(scrollerButtonWidth, scrollerButtonHeight)); // special case (if available height doesn't divide 3) expandActionButton.setPreferredSize(new Dimension( scrollerButtonWidth, galleryHeight - 2 * scrollerButtonHeight)); buttonStrip.setBounds(buttonX, margin.top, scrollerButtonWidth, galleryHeight); buttonStrip.doLayout(); if (!ribbonGallery.isShowingPopupPanel()) { // hide all buttons and compute the button width int maxButtonWidth = buttonHeight; if (galleryButtonDisplayState == JRibbonBand.BIG_FIXED_LANDSCAPE) { maxButtonWidth = maxButtonWidth * 5 / 4; } for (int i = 0; i < ribbonGallery.getButtonCount(); i++) { JCommandToggleButton currButton = ribbonGallery .getButtonAt(i); currButton.setVisible(false); } int gap = getLayoutGap(); // compute how many buttons can fit in each row visibleButtonsInEachRow = 0; int availableButtonsSpace = ltr ? buttonX - margin.left : width - buttonX - scrollerButtonWidth - margin.right; while (true) { // gap on the left, gap in between every two adjacent // buttons and gap on the right int neededSpace = visibleButtonsInEachRow * maxButtonWidth + (visibleButtonsInEachRow + 1) * gap; if (neededSpace > availableButtonsSpace) { visibleButtonsInEachRow--; break; } visibleButtonsInEachRow++; } // System.out.println("Visible in each row " + // visibleButtonsInEachRow); // compute how many pixels we can distribute among the visible // buttons int neededSpace = visibleButtonsInEachRow * maxButtonWidth + (visibleButtonsInEachRow + 1) * gap; int startX = ltr ? margin.left + gap : width - margin.right - gap; int availableWidth = ltr ? buttonX - margin.right : width - buttonX - scrollerButtonWidth - margin.left; int toAddToButtonWidth = (availableWidth - neededSpace) / visibleButtonsInEachRow; // compute how many buttons can fit in the available horizontal // space int lastVisibleButtonIndex = firstVisibleButtonIndex + visibleButtonRowNumber * visibleButtonsInEachRow - 1; lastVisibleButtonIndex = Math.min(lastVisibleButtonIndex, ribbonGallery.getButtonCount() - 1); int currCountInRow = 0; int buttonY = margin.top + borderInsets.top; int singleButtonWidth = maxButtonWidth + toAddToButtonWidth; for (int i = firstVisibleButtonIndex; i <= lastVisibleButtonIndex; i++) { JCommandToggleButton currButton = ribbonGallery .getButtonAt(i); // show button and set bounds currButton.setVisible(true); if (ltr) { currButton.setBounds(startX, buttonY, singleButtonWidth, buttonHeight); startX += (singleButtonWidth + gap); } else { currButton.setBounds(startX - singleButtonWidth, buttonY, singleButtonWidth, buttonHeight); startX -= (singleButtonWidth + gap); } currCountInRow++; if (currCountInRow == visibleButtonsInEachRow) { currCountInRow = 0; if (ltr) { startX = margin.left + gap; } else { startX = width - margin.right - gap; } buttonY += buttonHeight; } } if (ribbonGallery.getButtonCount() == 0) { scrollDownButton.setEnabled(false); scrollUpButton.setEnabled(false); expandActionButton.setEnabled(false); } else { // Scroll down button is enabled when the last button is not // showing scrollDownButton.setEnabled(!ribbonGallery.getButtonAt( ribbonGallery.getButtonCount() - 1).isVisible()); // Scroll up button is enabled when the first button is not // showing scrollUpButton.setEnabled(!ribbonGallery.getButtonAt(0) .isVisible()); expandActionButton.setEnabled(true); } } } } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void paint(Graphics g, JComponent c) { Graphics2D graphics = (Graphics2D) g.create(); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); this.paintRibbonGalleryBorder(graphics); graphics.dispose(); } /** * Paints ribbon gallery border. * * @param graphics * Graphics context. * @param toFill * Rectangle for the background. */ protected void paintRibbonGalleryBorder(Graphics graphics) { Graphics2D g2d = (Graphics2D) graphics.create(); g2d.setColor(FlamingoUtilities.getBorderColor()); Shape outerContour = FlamingoUtilities.getRibbonGalleryOutline( this.margin.left, this.ribbonGallery.getWidth() - margin.right, this.margin.top, this.ribbonGallery.getHeight() - this.margin.bottom, 2); if (this.ribbonGallery.getComponentOrientation().isLeftToRight()) { g2d.clipRect(0, 0, this.ribbonGallery.getWidth() - margin.right - buttonStrip.getWidth() / 2, this.ribbonGallery .getHeight()); } else { g2d.clipRect(margin.left + buttonStrip.getWidth() / 2, 0, this.ribbonGallery.getWidth() - margin.left - buttonStrip.getWidth() / 2, this.ribbonGallery .getHeight()); } g2d.draw(outerContour); g2d.dispose(); } /** * Returns the layout gap for the controls in the associated ribbon gallery. * * @return The layout gap for the controls in the associated ribbon gallery. */ protected int getLayoutGap() { return 4; } /** * Returns the preferred width of the ribbon gallery for the specified * parameters. * * @param buttonCount * Button count. * @param availableHeight * Available height in pixels. * @return The preferred width of the ribbon gallery for the specified * parameters. */ public int getPreferredWidth(int buttonCount, int availableHeight) { Insets borderInsets = ribbonGallery.getInsets(); int galleryHeight = availableHeight - margin.top - margin.bottom; int buttonHeight = galleryHeight - borderInsets.top - borderInsets.bottom; // start at the left margin int result = margin.left; // add all the gallery buttons - based on the display state CommandButtonDisplayState galleryButtonDisplayState = ribbonGallery .getButtonDisplayState(); if (galleryButtonDisplayState == CommandButtonDisplayState.SMALL) { result += buttonCount * buttonHeight / 3; } if (galleryButtonDisplayState == JRibbonBand.BIG_FIXED) { result += buttonCount * buttonHeight; } if (galleryButtonDisplayState == JRibbonBand.BIG_FIXED_LANDSCAPE) { result += buttonCount * buttonHeight * 5 / 4; } // and the gaps between them (including before first and after last) result += (buttonCount + 1) * getLayoutGap(); // and the control button strip width result += 15; // and the gap to the right margin result += margin.right; // System.out.println(buttonCount + "/" + availableHeight + "/" // + buttonHeight + " --> " + result); return result; } /** * Scrolls the contents of this ribbon gallery one row down. */ protected void scrollOneRowDown() { this.firstVisibleButtonIndex += this.visibleButtonsInEachRow; // int buttonCount = this.ribbonGallery.getButtonCount(); // // compute the last visible button // this.lastVisibleButtonIndex = this.firstVisibleButtonIndex // + this.visibleButtonsCount - 1; // if (this.lastVisibleButtonIndex >= buttonCount) // this.lastVisibleButtonIndex = buttonCount - 1; } /** * Scrolls the contents of this ribbon gallery one row up. */ protected void scrollOneRowUp() { this.firstVisibleButtonIndex -= this.visibleButtonsInEachRow; // this.firstVisibleButtonIndex -= this.visibleButtonsCount; // // int buttonCount = this.ribbonGallery.getButtonCount(); // // compute the last visible button // this.lastVisibleButtonIndex = this.firstVisibleButtonIndex // + this.visibleButtonsCount - 1; // // // update the last visible index so there's overlap between the // rows // // this.lastVisibleButtonIndex = this.firstVisibleButtonIndex; // // this.firstVisibleButtonIndex = this.lastVisibleButtonIndex // // - this.visibleButtonsCount + 1; // // if (this.firstVisibleButtonIndex < 0) // // this.firstVisibleButtonIndex = 0; } /** * Scrolls the contents of this ribbon gallery to reveal the currently * selected button. */ protected void scrollToSelected() { JCommandToggleButton selected = this.ribbonGallery.getSelectedButton(); if (selected == null) return; int selIndex = -1; for (int i = 0; i < this.ribbonGallery.getButtonCount(); i++) { if (this.ribbonGallery.getButtonAt(i) == selected) { selIndex = i; break; } } if (selIndex < 0) return; // is already shown? if ((selIndex >= this.firstVisibleButtonIndex) && (selIndex < (this.firstVisibleButtonIndex + this.visibleButtonRowNumber * this.visibleButtonsInEachRow))) return; // not visible? if (this.visibleButtonsInEachRow <= 0) return; while (true) { if (selIndex < this.firstVisibleButtonIndex) { // need to scroll up this.scrollOneRowUp(); } else { this.scrollOneRowDown(); } if ((selIndex >= this.firstVisibleButtonIndex) && (selIndex < (this.firstVisibleButtonIndex + this.visibleButtonRowNumber * this.visibleButtonsInEachRow))) { return; } } } protected void syncExpandKeyTip() { this.expandActionButton.setActionKeyTip(this.ribbonGallery .getExpandKeyTip()); } @KeyTipManager.HasNextKeyTipChain protected static class ExpandCommandButton extends JCommandButton { public ExpandCommandButton(ResizableIcon icon) { super(icon); } } } src/org/pushingpixels/flamingo/internal/ui/ribbon/BasicRibbonBandUI.java0000644000175000017500000006757311406773016025474 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.plaf.*; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.popup.*; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy; import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonUI; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; import org.pushingpixels.flamingo.internal.utils.RenderingUtils; import org.pushingpixels.trident.Timeline; import org.pushingpixels.trident.swing.SwingRepaintCallback; /** * Basic UI for ribbon band {@link JRibbonBand}. * * @author Kirill Grouchnikov * @author Matt Nathan */ public class BasicRibbonBandUI extends RibbonBandUI { /** * The associated ribbon band. */ protected AbstractRibbonBand ribbonBand; /** * The button for collapsed state. */ protected JCommandButton collapsedButton; /** * The band expand button. Is visible when the * {@link JRibbonBand#getExpandActionListener()} of the associated ribbon * band is not null. */ protected AbstractCommandButton expandButton; protected float rolloverAmount; protected Timeline rolloverTimeline; /** * Mouse listener on the associated ribbon band. */ protected MouseListener mouseListener; /** * Listens to property changes on the associated ribbon band. */ protected PropertyChangeListener propertyChangeListener; /** * Action listener on the expand button. */ protected ActionListener expandButtonActionListener; /** * Popup panel that shows the contents of the ribbon band when it is in a * collapsed state. * * @author Kirill Grouchnikov */ protected static class CollapsedButtonPopupPanel extends JPopupPanel { /** * The main component of this popup panel. Can be * null. */ protected Component component; /** * Creates popup gallery with the specified component. * * @param component * The main component of the popup gallery. * @param originalSize * The original dimension of the main component. */ public CollapsedButtonPopupPanel(Component component, Dimension originalSize) { this.component = component; this.setLayout(new BorderLayout()); this.add(component, BorderLayout.CENTER); // System.out.println("Popup dim is " + originalSize); this.setPreferredSize(originalSize); this.setSize(originalSize); } /** * Removes the main component of this popup gallery. * * @return The removed main component. */ public Component removeComponent() { this.remove(this.component); return this.component; } /** * Returns the main component of this popup gallery. * * @return The main component. */ public Component getComponent() { return this.component; } } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicRibbonBandUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.ribbonBand = (AbstractRibbonBand) c; this.rolloverTimeline = new Timeline(this); this.rolloverTimeline.addPropertyToInterpolate("rolloverAmount", 0.0f, 1.0f); this.rolloverTimeline.addCallback(new SwingRepaintCallback( this.ribbonBand)); this.rolloverTimeline.setDuration(250); installDefaults(); installComponents(); installListeners(); c.setLayout(createLayoutManager()); AWTRibbonEventListener.install(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { c.setLayout(null); uninstallListeners(); uninstallDefaults(); uninstallComponents(); if (!AWTRibbonEventListener.uninstall()) { // should remove other methods of tracking } } /** * Installs default parameters on the associated ribbon band. */ protected void installDefaults() { Color bg = this.ribbonBand.getBackground(); if (bg == null || bg instanceof UIResource) { this.ribbonBand.setBackground(FlamingoUtilities.getColor( Color.lightGray, "RibbonBand.background", "Panel.background")); } Border b = this.ribbonBand.getBorder(); if (b == null || b instanceof UIResource) { Border toSet = UIManager.getBorder("RibbonBand.border"); if (toSet == null) toSet = new BorderUIResource(new RoundBorder(FlamingoUtilities .getBorderColor(), new Insets(2, 2, 2, 2))); this.ribbonBand.setBorder(toSet); } } /** * Installs subcomponents on the associated ribbon band. */ protected void installComponents() { this.collapsedButton = new JCommandButton(this.ribbonBand.getTitle(), this.ribbonBand.getIcon()); this.collapsedButton.setDisplayState(CommandButtonDisplayState.BIG); this.collapsedButton .setCommandButtonKind(JCommandButton.CommandButtonKind.POPUP_ONLY); this.collapsedButton.setPopupKeyTip(this.ribbonBand .getCollapsedStateKeyTip()); this.ribbonBand.add(this.collapsedButton); if (this.ribbonBand.getExpandActionListener() != null) { this.expandButton = this.createExpandButton(); this.ribbonBand.add(this.expandButton); } } /** * Creates the expand button for the associated ribbon band. * * @return Expand button for the associated ribbon band. */ protected JCommandButton createExpandButton() { ResizableIcon icon = FlamingoUtilities .getRibbonBandExpandIcon(this.ribbonBand); JCommandButton result = new JCommandButton(null, icon); result.setFlat(true); result.putClientProperty(BasicCommandButtonUI.EMULATE_SQUARE_BUTTON, Boolean.TRUE); result.setBorder(new EmptyBorder(3, 2, 3, 2)); result.setActionKeyTip(this.ribbonBand.getExpandButtonKeyTip()); result.setActionRichTooltip(this.ribbonBand .getExpandButtonRichTooltip()); return result; } protected void syncExpandButtonIcon() { this.expandButton.setIcon(FlamingoUtilities .getRibbonBandExpandIcon(this.ribbonBand)); } /** * Installs listeners on the associated ribbon band. */ protected void installListeners() { // without this empty adapter, the global listener never // gets mouse entered events on the ribbon band this.mouseListener = new MouseAdapter() { }; this.ribbonBand.addMouseListener(this.mouseListener); configureExpandButton(); this.propertyChangeListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("title".equals(evt.getPropertyName())) ribbonBand.repaint(); if ("expandButtonKeyTip".equals(evt.getPropertyName())) { if (expandButton != null) { expandButton .setActionKeyTip((String) evt.getNewValue()); } } if ("expandButtonRichTooltip".equals(evt.getPropertyName())) { if (expandButton != null) { expandButton.setActionRichTooltip((RichTooltip) evt .getNewValue()); } } if ("collapsedStateKeyTip".equals(evt.getPropertyName())) { if (collapsedButton != null) { collapsedButton.setPopupKeyTip((String) evt .getNewValue()); } } if ("expandActionListener".equals(evt.getPropertyName())) { ActionListener oldListener = (ActionListener) evt .getOldValue(); ActionListener newListener = (ActionListener) evt .getNewValue(); if ((oldListener != null) && (newListener == null)) { // need to remove unconfigureExpandButton(); ribbonBand.remove(expandButton); expandButton = null; ribbonBand.revalidate(); } if ((oldListener == null) && (newListener != null)) { // need to add expandButton = createExpandButton(); configureExpandButton(); ribbonBand.add(expandButton); ribbonBand.revalidate(); } if ((oldListener != null) && (newListener != null)) { // need to reconfigure expandButton.removeActionListener(oldListener); expandButton.addActionListener(newListener); } } if ("componentOrientation".equals(evt.getPropertyName())) { if (expandButton != null) { syncExpandButtonIcon(); } } } }; this.ribbonBand.addPropertyChangeListener(this.propertyChangeListener); } protected void configureExpandButton() { if (this.expandButton != null) { this.expandButton.addActionListener(this.ribbonBand .getExpandActionListener()); this.expandButtonActionListener = new ActionListener() { public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { trackMouseCrossing(false); } }); } }; this.expandButton .addActionListener(this.expandButtonActionListener); } } /** * Uninstalls default parameters from the associated ribbon band. */ protected void uninstallDefaults() { LookAndFeel.uninstallBorder(this.ribbonBand); } /** * Uninstalls components from the associated ribbon band. */ protected void uninstallComponents() { if (this.collapsedButton.isVisible()) { // restore the control panel to the ribbon band. CollapsedButtonPopupPanel popupPanel = (collapsedButton .getPopupCallback() == null) ? null : (CollapsedButtonPopupPanel) collapsedButton .getPopupCallback().getPopupPanel(collapsedButton); if (popupPanel != null) { AbstractRibbonBand bandFromPopup = (AbstractRibbonBand) popupPanel .removeComponent(); ribbonBand.setControlPanel(bandFromPopup.getControlPanel()); ribbonBand.setPopupRibbonBand(null); collapsedButton.setPopupCallback(null); } } this.ribbonBand.remove(this.collapsedButton); this.collapsedButton = null; if (this.expandButton != null) this.ribbonBand.remove(this.expandButton); this.expandButton = null; this.ribbonBand = null; } /** * Uninstalls listeners from the associated ribbon band. */ protected void uninstallListeners() { this.ribbonBand .removePropertyChangeListener(this.propertyChangeListener); this.propertyChangeListener = null; this.ribbonBand.removeMouseListener(this.mouseListener); this.mouseListener = null; unconfigureExpandButton(); } protected void unconfigureExpandButton() { if (this.expandButton != null) { this.expandButton .removeActionListener(this.expandButtonActionListener); this.expandButtonActionListener = null; this.expandButton.removeActionListener(this.ribbonBand .getExpandActionListener()); } } /** * Invoked by installUI to create a layout manager object to * manage the {@link JCommandButtonStrip}. * * @return a layout manager object */ protected LayoutManager createLayoutManager() { return new RibbonBandLayout(); } /** * Layout for the ribbon band. * * @author Kirill Grouchnikov */ private class RibbonBandLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { Insets ins = c.getInsets(); AbstractBandControlPanel controlPanel = ribbonBand .getControlPanel(); boolean useCollapsedButton = (controlPanel == null) || (!controlPanel.isVisible()); int width = useCollapsedButton ? collapsedButton.getPreferredSize().width : controlPanel.getPreferredSize().width; int height = (useCollapsedButton ? collapsedButton .getPreferredSize().height : controlPanel .getPreferredSize().height) + getBandTitleHeight(); // System.out.println(ribbonBand.getTitle() + ":" + height); // Preferred height of the ribbon band is: // 1. Insets on top and bottom // 2. Preferred height of the control panel // 3. Preferred height of the band title panel // System.out.println("Ribbon band pref = " // + (height + ins.top + ins.bottom)); return new Dimension(width + 2 + ins.left + ins.right, height + ins.top + ins.bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { Insets ins = c.getInsets(); AbstractBandControlPanel controlPanel = ribbonBand .getControlPanel(); boolean useCollapsedButton = (controlPanel == null) || (!controlPanel.isVisible()); int width = useCollapsedButton ? collapsedButton.getMinimumSize().width : controlPanel.getMinimumSize().width; int height = useCollapsedButton ? collapsedButton.getMinimumSize().height + getBandTitleHeight() : controlPanel.getMinimumSize().height + getBandTitleHeight(); // System.out.println(useCollapsedButton + ":" + height); // Preferred height of the ribbon band is: // 1. Insets on top and bottom // 2. Preferred height of the control panel // 3. Preferred height of the band title panel return new Dimension(width + 2 + ins.left + ins.right, height + ins.top + ins.bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { // System.out.println("Ribbon band real = " + c.getHeight()); if (!c.isVisible()) return; Insets ins = c.getInsets(); int availableHeight = c.getHeight() - ins.top - ins.bottom; RibbonBandResizePolicy resizePolicy = ((AbstractRibbonBand) c) .getCurrentResizePolicy(); if (resizePolicy instanceof IconRibbonBandResizePolicy) { collapsedButton.setVisible(true); int w = collapsedButton.getPreferredSize().width; // System.out.println("Width for collapsed of '" // + ribbonBand.getTitle() + "' is " + w); collapsedButton.setBounds((c.getWidth() - w) / 2, ins.top, w, c .getHeight() - ins.top - ins.bottom); // System.out.println("Collapsed " + collapsedButton.getHeight() // + ":" + c.getHeight()); if (collapsedButton.getPopupCallback() == null) { final AbstractRibbonBand popupBand = ribbonBand.cloneBand(); popupBand.setControlPanel(ribbonBand.getControlPanel()); List resizePolicies = ribbonBand .getResizePolicies(); popupBand.setResizePolicies(resizePolicies); RibbonBandResizePolicy largest = resizePolicies.get(0); popupBand.setCurrentResizePolicy(largest); int gap = popupBand.getControlPanel().getUI() .getLayoutGap(); final Dimension size = new Dimension(ins.left + ins.right + gap + largest.getPreferredWidth(availableHeight, gap), ins.top + ins.bottom + Math.max(c.getHeight(), ribbonBand .getControlPanel() .getPreferredSize().height + getBandTitleHeight())); // System.out.println(ribbonBand.getTitle() + ":" // + size.getHeight()); collapsedButton.setPopupCallback(new PopupPanelCallback() { @Override public JPopupPanel getPopupPanel( JCommandButton commandButton) { return new CollapsedButtonPopupPanel(popupBand, size); } }); // collapsedButton.setGallery(new JPopupGallery(ribbonBand // .getControlPanel(), size)); ribbonBand.setControlPanel(null); ribbonBand.setPopupRibbonBand(popupBand); } if (expandButton != null) expandButton.setBounds(0, 0, 0, 0); return; } if (collapsedButton.isVisible()) { // was icon and now is normal band - have to restore the // control panel CollapsedButtonPopupPanel popupPanel = (collapsedButton .getPopupCallback() != null) ? (CollapsedButtonPopupPanel) collapsedButton .getPopupCallback().getPopupPanel(collapsedButton) : null; if (popupPanel != null) { AbstractRibbonBand bandFromPopup = (AbstractRibbonBand) popupPanel .removeComponent(); ribbonBand.setControlPanel(bandFromPopup.getControlPanel()); ribbonBand.setPopupRibbonBand(null); collapsedButton.setPopupCallback(null); } } collapsedButton.setVisible(false); AbstractBandControlPanel controlPanel = ribbonBand .getControlPanel(); controlPanel.setVisible(true); controlPanel.setBounds(ins.left, ins.top, c.getWidth() - ins.left - ins.right, c.getHeight() - getBandTitleHeight() - ins.top - ins.bottom); controlPanel.doLayout(); if (expandButton != null) { int ebpw = expandButton.getPreferredSize().width; int ebph = expandButton.getPreferredSize().height; int maxHeight = getBandTitleHeight() - 4; if (ebph > maxHeight) ebph = maxHeight; int expandButtonBottomY = c.getHeight() - (getBandTitleHeight() - ebph) / 2; boolean ltr = ribbonBand.getComponentOrientation() .isLeftToRight(); if (ltr) { expandButton.setBounds(c.getWidth() - ins.right - ebpw, expandButtonBottomY - ebph, ebpw, ebph); } else { expandButton.setBounds(ins.left, expandButtonBottomY - ebph, ebpw, ebph); } } } } /** * Event listener to handle global ribbon events. Currently handles: *
    *
  • Marking a ribbon band to be hovered when the mouse moves over it.
  • *
  • Mouse wheel events anywhere in the ribbon to rotate the selected * task.
  • *
*/ private static class AWTRibbonEventListener implements AWTEventListener { private static AWTRibbonEventListener instance; private int installCount = 0; private AbstractRibbonBand lastHovered; public static void install() { if (instance == null) { instance = new AWTRibbonEventListener(); java.security.AccessController .doPrivileged(new java.security.PrivilegedAction() { public Object run() { Toolkit .getDefaultToolkit() .addAWTEventListener( instance, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK); return null; } }); } instance.installCount++; } public static boolean uninstall() { if (instance != null) { instance.installCount--; if (instance.installCount == 0) { // really uninstall Toolkit.getDefaultToolkit() .removeAWTEventListener(instance); instance = null; } return true; } return false; } public void eventDispatched(AWTEvent event) { MouseEvent mouseEvent = (MouseEvent) event; if (mouseEvent.getID() == MouseEvent.MOUSE_ENTERED) { Object object = event.getSource(); if (!(object instanceof Component)) { return; } Component component = (Component) object; AbstractRibbonBand band = (component instanceof AbstractRibbonBand) ? ((AbstractRibbonBand) component) : (AbstractRibbonBand) SwingUtilities .getAncestorOfClass(AbstractRibbonBand.class, component); setHoveredBand(band); } if (mouseEvent.getID() == MouseEvent.MOUSE_WHEEL) { if (PopupPanelManager.defaultManager().getShownPath().size() > 0) return; Object object = event.getSource(); if (!(object instanceof Component)) { return; } Component component = (Component) object; // get the deepest subcomponent at the event point MouseWheelEvent mouseWheelEvent = (MouseWheelEvent) mouseEvent; Component deepest = SwingUtilities.getDeepestComponentAt( component, mouseWheelEvent.getX(), mouseWheelEvent .getY()); JRibbon ribbon = (JRibbon) SwingUtilities.getAncestorOfClass( JRibbon.class, deepest); if (ribbon != null) { // if the mouse wheel scroll has happened inside a ribbon, // ask the UI delegate to handle it ribbon.getUI().handleMouseWheelEvent( (MouseWheelEvent) mouseEvent); } } } private void setHoveredBand(AbstractRibbonBand band) { if (lastHovered == band) { return; // nothing to do as we are already over } if (lastHovered != null) { // RibbonBandUI ui = lastHovered.getUI(); lastHovered.getUI().trackMouseCrossing(false); } lastHovered = band; if (band != null) { band.getUI().trackMouseCrossing(true); } } } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void update(Graphics g, JComponent c) { Graphics2D g2d = (Graphics2D) g.create(); RenderingUtils.installDesktopHints(g2d); super.update(g2d, c); g2d.dispose(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void paint(Graphics g, JComponent c) { Graphics2D graphics = (Graphics2D) g.create(); Insets ins = ribbonBand.getInsets(); this.paintBandBackground(graphics, new Rectangle(0, 0, c.getWidth(), c .getHeight())); if (!(ribbonBand.getCurrentResizePolicy() instanceof IconRibbonBandResizePolicy)) { String title = ribbonBand.getTitle(); int titleHeight = getBandTitleHeight(); int bandTitleTopY = c.getHeight() - titleHeight; this.paintBandTitleBackground(graphics, new Rectangle(0, bandTitleTopY, c.getWidth(), titleHeight), title); boolean ltr = ribbonBand.getComponentOrientation().isLeftToRight(); int titleWidth = c.getWidth() - 2 * ins.left - 2 * ins.right; int titleX = 2 * ins.left; if (expandButton != null) { if (ltr) { titleWidth = expandButton.getX() - 2 * ins.right - 2 * ins.left; } else { titleWidth = ribbonBand.getWidth() - expandButton.getX() - expandButton.getWidth() - 2 * ins.right - 2 * ins.left; titleX = expandButton.getX() + expandButton.getWidth() + 2 * ins.left; } } this.paintBandTitle(graphics, new Rectangle(titleX, bandTitleTopY, titleWidth, titleHeight), title); } graphics.dispose(); } /** * Paints band title pane. * * @param g * Graphics context. * @param titleRectangle * Rectangle for the title pane. * @param title * Title string. */ protected void paintBandTitle(Graphics g, Rectangle titleRectangle, String title) { // fix for issue 10 - empty ribbon band if (titleRectangle.width <= 0) return; Graphics2D graphics = (Graphics2D) g.create(); graphics.setFont(FlamingoUtilities.getFont(this.ribbonBand, "Ribbon.font", "Button.font", "Panel.font")); FontMetrics fm = graphics.getFontMetrics(); int y = titleRectangle.y - 2 + (titleRectangle.height + fm.getAscent()) / 2; int currLength = (int) fm.getStringBounds(title, g).getWidth(); String titleToPaint = title; while (currLength > titleRectangle.width) { title = title.substring(0, title.length() - 1); titleToPaint = title + "..."; currLength = (int) fm.getStringBounds(titleToPaint, g).getWidth(); } int x = titleRectangle.x + (titleRectangle.width - currLength) / 2; graphics.setColor(this.ribbonBand.getForeground()); graphics.drawString(titleToPaint, x, y); graphics.dispose(); } /** * Paints band title pane. * * @param g * Graphics context. * @param titleRectangle * Rectangle for the title pane. * @param title * Title string. */ protected void paintBandTitleBackground(Graphics g, Rectangle titleRectangle, String title) { Graphics2D g2d = (Graphics2D) g.create(); g2d.setComposite(AlphaComposite.SrcOver .derive(0.7f + 0.3f * this.rolloverAmount)); FlamingoUtilities.renderSurface(g2d, this.ribbonBand, titleRectangle, this.rolloverAmount > 0.0f, true, false); g2d.dispose(); } public void setRolloverAmount(float rolloverAmount) { this.rolloverAmount = rolloverAmount; } /** * Paints band background. * * @param graphics * Graphics context. * @param toFill * Rectangle for the background. */ protected void paintBandBackground(Graphics graphics, Rectangle toFill) { graphics.setColor(ribbonBand.getBackground()); graphics.fillRect(toFill.x, toFill.y, toFill.width, toFill.height); } @Override public float getRolloverAmount() { return this.rolloverAmount; } /** * Returns the height of the ribbon band title area. * * @return The height of the ribbon band title area. */ @Override public int getBandTitleHeight() { Font font = FlamingoUtilities.getFont(this.ribbonBand, "Ribbon.font", "Button.font", "Panel.font"); if (font == null) { // Nimbus - is that you? font = new JLabel().getFont(); } int result = font.getSize() + 5; if (result % 2 == 0) result++; return result; } /** * Round border for the ribbon bands. * * @author Kirill Grouchnikov */ protected static class RoundBorder implements Border { /** * Border color. */ protected Color color; /** * Border insets. */ protected Insets insets; /** * Creates the new border. * * @param color * Border color. * @param insets * Border insets. */ public RoundBorder(Color color, Insets insets) { this.color = color; this.insets = insets; } @Override public Insets getBorderInsets(Component c) { return this.insets; } @Override public boolean isBorderOpaque() { return false; } @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(this.color); g2d.drawRoundRect(x, y, width - 1, height - 1, 3, 3); g2d.dispose(); } } @Override public int getPreferredCollapsedWidth() { return this.collapsedButton.getPreferredSize().width + 2; } @Override public void trackMouseCrossing(boolean isMouseIn) { if (isMouseIn) { this.rolloverTimeline.play(); } else { this.rolloverTimeline.playReverse(); } this.ribbonBand.repaint(); } /** * This method is for unit tests only and should not be called by the * application code. * * @return The expand button of the matching ribbon band. */ public AbstractCommandButton getExpandButton() { return this.expandButton; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/BandControlPanelUI.java0000644000175000017500000000407711401230446025673 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import javax.swing.plaf.PanelUI; /** * UI for control panel of ribbon band ({@link JBandControlPanel}). * * @author Kirill Grouchnikov */ public abstract class BandControlPanelUI extends PanelUI { /** * Returns the layout gap for the controls in the associated control panel. * * @return The layout gap for the controls in the associated control panel. */ public abstract int getLayoutGap(); } src/org/pushingpixels/flamingo/internal/ui/ribbon/BasicRibbonTaskToggleButtonUI.java0000644000175000017500000002541211401515236030043 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.awt.image.BufferedImage; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.*; import javax.swing.plaf.basic.BasicGraphicsUtils; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager.PopupEvent; import org.pushingpixels.flamingo.api.ribbon.JRibbon; import org.pushingpixels.flamingo.api.ribbon.RibbonContextualTaskGroup; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandToggleButtonUI; import org.pushingpixels.flamingo.internal.utils.*; /** * Basic UI for toggle button of ribbon tasks {@link JRibbonTaskToggleButton}. * * @author Kirill Grouchnikov */ public class BasicRibbonTaskToggleButtonUI extends BasicCommandToggleButtonUI { protected PopupPanelManager.PopupListener popupListener; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicRibbonTaskToggleButtonUI(); } @Override protected void installDefaults() { super.installDefaults(); Font f = this.commandButton.getFont(); if (f == null || f instanceof UIResource) { this.commandButton.setFont(FlamingoUtilities.getFont(null, "Ribbon.font", "Button.font", "Panel.font")); } Border border = this.commandButton.getBorder(); if (border == null || border instanceof UIResource) { Border toInstall = UIManager .getBorder("RibbonTaskToggleButton.border"); if (toInstall == null) toInstall = new BorderUIResource.EmptyBorderUIResource(1, 12, 1, 12); this.commandButton.setBorder(toInstall); } this.commandButton.setFlat(true); this.commandButton.setOpaque(false); } @Override protected void installListeners() { super.installListeners(); this.popupListener = new PopupPanelManager.PopupListener() { @Override public void popupShown(PopupEvent event) { if (event.getSource() == commandButton) { commandButton.getActionModel() .setSelected(isTaskSelected()); } } @Override public void popupHidden(PopupEvent event) { if (event.getSource() == commandButton) { commandButton.getActionModel() .setSelected(isTaskSelected()); } } private boolean isTaskSelected() { JRibbon ribbon = (JRibbon) SwingUtilities.getAncestorOfClass( JRibbon.class, commandButton); if (ribbon == null) return false; return ribbon.getSelectedTask() == ((JRibbonTaskToggleButton) commandButton) .getRibbonTask(); } }; PopupPanelManager.defaultManager().addPopupListener(this.popupListener); } @Override protected void uninstallListeners() { PopupPanelManager.defaultManager().removePopupListener( this.popupListener); this.popupListener = null; super.uninstallListeners(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void update(Graphics g, JComponent c) { Graphics2D g2d = (Graphics2D) g.create(); RenderingUtils.installDesktopHints(g2d); this.layoutInfo = this.layoutManager.getLayoutInfo(this.commandButton, g); this.paintButtonBackground(g2d, new Rectangle(0, 0, c.getWidth(), c .getHeight() + 10)); this.paintText(g2d); g2d.dispose(); } protected void paintText(Graphics g) { FontMetrics fm = g.getFontMetrics(); String toPaint = this.commandButton.getText(); // compute the insets int fullInsets = this.commandButton.getInsets().left; int pw = this.getPreferredSize(this.commandButton).width; int mw = this.getMinimumSize(this.commandButton).width; int w = this.commandButton.getWidth(); int h = this.commandButton.getHeight(); int insets = fullInsets - (pw - w) * (fullInsets - 2) / (pw - mw); // and the text rectangle Rectangle textRect = new Rectangle(insets, 1 + (h - fm.getHeight()) / 2, w - 2 * insets, fm.getHeight()); // show the first characters that fit into the available text rectangle while (true) { if (toPaint.length() == 0) break; int strWidth = fm.stringWidth(toPaint); if (strWidth <= textRect.width) break; toPaint = toPaint.substring(0, toPaint.length() - 1); } BasicGraphicsUtils.drawString(g, toPaint, -1, textRect.x, textRect.y + fm.getAscent()); } /** * Paints the button background. * * @param graphics * Graphics context. * @param toFill * Rectangle to fill. * @param button * The button itself. */ @Override protected void paintButtonBackground(Graphics graphics, Rectangle toFill) { JRibbon ribbon = (JRibbon) SwingUtilities.getAncestorOfClass( JRibbon.class, this.commandButton); this.buttonRendererPane.setBounds(toFill.x, toFill.y, toFill.width, toFill.height); ButtonModel model = this.rendererButton.getModel(); model.setEnabled(this.commandButton.isEnabled()); model.setSelected(false); // System.out.println(toggleTabButton.getText() + ":" // + toggleTabButton.isSelected()); // selected task toggle button should not have any background if // the ribbon is minimized and it is not shown in a popup boolean displayAsSelected = this.commandButton.getActionModel() .isSelected(); model.setRollover(displayAsSelected || this.commandButton.getActionModel().isRollover()); model.setPressed(false); if (model.isRollover()) { Graphics2D g2d = (Graphics2D) graphics.create(); // partial translucency if it is not selected if (!this.commandButton.getActionModel().isSelected()) { g2d.setComposite(AlphaComposite.SrcOver.derive(0.4f)); } g2d.translate(toFill.x, toFill.y); Color contextualGroupHueColor = ((JRibbonTaskToggleButton) this.commandButton) .getContextualGroupHueColor(); boolean isContextualTask = (contextualGroupHueColor != null); if (!isContextualTask) { Shape clip = g2d.getClip(); g2d.clip(FlamingoUtilities.getRibbonTaskToggleButtonOutline( toFill.width, toFill.height, 2)); this.buttonRendererPane.paintComponent(g2d, this.rendererButton, this.commandButton, toFill.x - toFill.width / 2, toFill.y - toFill.height / 2, 2 * toFill.width, 2 * toFill.height, true); g2d.setColor(FlamingoUtilities.getBorderColor().darker()); g2d.setClip(clip); g2d.draw(FlamingoUtilities.getRibbonTaskToggleButtonOutline( toFill.width, toFill.height + 1, 2)); } else { // draw to an offscreen image, colorize and draw the colorized // image BufferedImage offscreen = FlamingoUtilities.getBlankImage( toFill.width, toFill.height); Graphics2D offscreenGraphics = offscreen.createGraphics(); Shape clip = g2d.getClip(); offscreenGraphics.clip(FlamingoUtilities .getRibbonTaskToggleButtonOutline(toFill.width, toFill.height, 2)); this.buttonRendererPane.paintComponent(offscreenGraphics, this.rendererButton, this.commandButton, toFill.x - toFill.width / 2, toFill.y - toFill.height / 2, 2 * toFill.width, 2 * toFill.height, true); offscreenGraphics.setColor(FlamingoUtilities.getBorderColor() .darker()); offscreenGraphics.setClip(clip); offscreenGraphics.draw(FlamingoUtilities .getRibbonTaskToggleButtonOutline(toFill.width, toFill.height + 1, 2)); offscreenGraphics.dispose(); ColorShiftFilter filter = new ColorShiftFilter( contextualGroupHueColor, RibbonContextualTaskGroup.HUE_ALPHA); BufferedImage colorized = filter.filter(offscreen, null); g2d.drawImage(colorized, 0, 0, null); } g2d.dispose(); } } /* * (non-Javadoc) * * @see * javax.swing.plaf.basic.BasicButtonUI#getPreferredSize(javax.swing.JComponent * ) */ @Override public Dimension getPreferredSize(JComponent c) { JRibbonTaskToggleButton b = (JRibbonTaskToggleButton) c; Icon icon = b.getIcon(); String text = b.getText(); Font font = b.getFont(); FontMetrics fm = b.getFontMetrics(font); Rectangle iconR = new Rectangle(); Rectangle textR = new Rectangle(); Rectangle viewR = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE); SwingUtilities.layoutCompoundLabel(b, fm, text, icon, SwingUtilities.CENTER, b.getHorizontalAlignment(), SwingUtilities.CENTER, SwingUtilities.CENTER, viewR, iconR, textR, (text == null ? 0 : 6)); Rectangle r = iconR.union(textR); Insets insets = b.getInsets(); r.width += insets.left + insets.right; r.height += insets.top + insets.bottom; return r.getSize(); } @Override public Dimension getMinimumSize(JComponent c) { JRibbonTaskToggleButton b = (JRibbonTaskToggleButton) c; Icon icon = b.getIcon(); String text = "Www"; Font font = b.getFont(); FontMetrics fm = b.getFontMetrics(font); Rectangle iconR = new Rectangle(); Rectangle textR = new Rectangle(); Rectangle viewR = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE); SwingUtilities.layoutCompoundLabel(b, fm, text, icon, SwingUtilities.CENTER, b.getHorizontalAlignment(), SwingUtilities.CENTER, SwingUtilities.CENTER, viewR, iconR, textR, (text == null ? 0 : 6)); Rectangle r = iconR.union(textR); Insets insets = b.getInsets(); r.width += 4; r.height += insets.top + insets.bottom; return r.getSize(); } } src/org/pushingpixels/flamingo/internal/ui/ribbon/CommandButtonLayoutManagerBigFixedLandscape.java0000644000175000017500000001335511401230446032727 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import javax.swing.JSeparator; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerBigFixedLandscape implements CommandButtonLayoutManager { @Override public int getPreferredIconSize() { return 32; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = commandButton.getInsets(); int bx = borderInsets.left + borderInsets.right; int by = borderInsets.top + borderInsets.bottom; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); JSeparator jsep = new JSeparator(JSeparator.VERTICAL); int layoutVGap = FlamingoUtilities.getVLayoutGap(commandButton); // icon, label int fillTitleWidth = fm.stringWidth(commandButton.getText()); int widthFull = Math.max(this.getPreferredIconSize(), fillTitleWidth); int heightFull = by + this.getPreferredIconSize() + layoutVGap + jsep.getPreferredSize().width; if (commandButton.getText() != null) { heightFull += fm.getHeight(); } widthFull = Math.max(widthFull, heightFull * 5 / 4); return new Dimension(bx + widthFull, heightFull); } @Override public void propertyChange(PropertyChangeEvent evt) { } @Override public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) { // center of the bottom edge return new Point(commandButton.getWidth() / 2, commandButton .getHeight()); } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, 0, 0); Insets ins = commandButton.getInsets(); result.iconRect = new Rectangle(); result.popupActionRect = new Rectangle(); int width = commandButton.getWidth(); int height = commandButton.getHeight(); int x = ins.left; int y = ins.top; FontMetrics fm = g.getFontMetrics(); int labelHeight = fm.getAscent() + fm.getDescent(); JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton) .getCommandButtonKind() : JCommandButton.CommandButtonKind.ACTION_ONLY; result.isTextInActionArea = false; if (buttonKind == JCommandButton.CommandButtonKind.ACTION_ONLY) { result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; } if (buttonKind == JCommandButton.CommandButtonKind.POPUP_ONLY) { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; } JSeparator jsep = new JSeparator(JSeparator.VERTICAL); // int layoutGap = FlamingoUtilities.getLayoutGap(commandButton); ResizableIcon buttonIcon = commandButton.getIcon(); if (commandButton.getText() == null) { y = ins.top + (height - ins.top - ins.bottom - buttonIcon .getIconHeight()) / 2; } result.iconRect.x = (width - buttonIcon.getIconWidth()) / 2; result.iconRect.y = y; result.iconRect.width = buttonIcon.getIconWidth(); result.iconRect.height = buttonIcon.getIconHeight(); y += buttonIcon.getIconHeight(); y += jsep.getPreferredSize().width; TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); int labelWidth = (int) fm.getStringBounds(commandButton.getText(), g) .getWidth(); lineLayoutInfo.textRect.x = ins.left + (width - labelWidth - ins.left - ins.right) / 2; lineLayoutInfo.textRect.y = y; lineLayoutInfo.textRect.width = labelWidth; lineLayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); return result; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/AbstractBandControlPanelUI.java0000644000175000017500000001364011401230446027353 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.UIResource; import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.icon.EmptyResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Basic UI for control panel of ribbon band {@link JBandControlPanel}. * * @author Kirill Grouchnikov */ abstract class AbstractBandControlPanelUI extends BandControlPanelUI { /** * The associated control panel. */ protected AbstractBandControlPanel controlPanel; protected JCommandButton dummy; public static final String TOP_ROW = "flamingo.internal.ribbonBandControlPanel.topRow"; public static final String MID_ROW = "flamingo.internal.ribbonBandControlPanel.midRow"; public static final String BOTTOM_ROW = "flamingo.internal.ribbonBandControlPanel.bottomRow"; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.controlPanel = (AbstractBandControlPanel) c; this.dummy = new JCommandButton("Dummy", new EmptyResizableIcon(16)); this.dummy.setDisplayState(CommandButtonDisplayState.BIG); this.dummy .setCommandButtonKind(CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); installDefaults(); installComponents(); installListeners(); c.setLayout(createLayoutManager()); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); c.setLayout(null); this.controlPanel = null; } /** * Installs listeners on the associated control panel of a ribbon band. */ protected void installListeners() { } /** * Uninstalls listeners from the associated control panel of a ribbon band. */ protected void uninstallListeners() { } /** * Installs components on the associated control panel of a ribbon band. */ protected void installComponents() { } /** * Uninstalls components from the associated control panel of a ribbon band. */ protected void uninstallComponents() { } /** * Installs default parameters on the associated control panel of a ribbon * band. */ protected void installDefaults() { Color bg = this.controlPanel.getBackground(); if (bg == null || bg instanceof UIResource) { this.controlPanel.setBackground(FlamingoUtilities.getColor( Color.lightGray, "ControlPanel.background", "Panel.background")); } Border b = this.controlPanel.getBorder(); if (b == null || b instanceof UIResource) { Border toSet = UIManager.getBorder("ControlPanel.border"); if (toSet == null) new BorderUIResource.EmptyBorderUIResource(1, 2, 1, 2); this.controlPanel.setBorder(toSet); } } /** * Uninstalls default parameters from the associated control panel. */ protected void uninstallDefaults() { LookAndFeel.uninstallBorder(this.controlPanel); } /** * Invoked by installUI to create a layout manager object to * manage the {@link JBandControlPanel}. * * @return a layout manager object */ protected abstract LayoutManager createLayoutManager(); /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void paint(Graphics g, JComponent c) { Graphics2D graphics = (Graphics2D) g.create(); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); this.paintBandBackground(graphics, new Rectangle(0, 0, c.getWidth(), c .getHeight())); graphics.dispose(); } /** * Paints band background. * * @param graphics * Graphics context. * @param toFill * Rectangle for the background. */ protected void paintBandBackground(Graphics graphics, Rectangle toFill) { graphics.setColor(controlPanel.getBackground()); graphics.fillRect(toFill.x, toFill.y, toFill.width, toFill.height); } @Override public int getLayoutGap() { return 4; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/RibbonGalleryUI.java0000644000175000017500000000355011401230446025234 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import javax.swing.plaf.ComponentUI; /** * UI for in-ribbon gallery ({@link JRibbonGallery}). * * @author Kirill Grouchnikov */ public abstract class RibbonGalleryUI extends ComponentUI { } src/org/pushingpixels/flamingo/internal/ui/ribbon/BasicFlowBandControlPanelUI.java0000644000175000017500000001645211401230446027465 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.util.List; import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand; import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy; import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; import org.pushingpixels.flamingo.internal.ui.ribbon.BasicRibbonBandUI.CollapsedButtonPopupPanel; /** * Basic UI for control panel of ribbon band {@link JBandControlPanel}. * * @author Kirill Grouchnikov */ public class BasicFlowBandControlPanelUI extends AbstractBandControlPanelUI { /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicFlowBandControlPanelUI(); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.ribbon.ui.AbstractBandControlPanelUI#createLayoutManager * () */ @Override protected LayoutManager createLayoutManager() { return new FlowControlPanelLayout(); } /** * Layout for the control panel of flow ribbon band. * * @author Kirill Grouchnikov */ private class FlowControlPanelLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { // The height of ribbon band control panel is // computed based on the preferred height of a command // button in BIG state. int buttonHeight = dummy.getPreferredSize().height; int vGap = getLayoutGap() * 3 / 4; int minusGaps = buttonHeight - 2 * vGap; switch (minusGaps % 3) { case 1: buttonHeight += 2; break; case 2: buttonHeight++; break; } Insets ins = c.getInsets(); return new Dimension(c.getWidth(), buttonHeight + ins.top + ins.bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { return this.preferredLayoutSize(c); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { JFlowBandControlPanel flowBandControlPanel = (JFlowBandControlPanel) c; AbstractRibbonBand ribbonBand = flowBandControlPanel .getRibbonBand(); RibbonBandResizePolicy currentResizePolicy = ribbonBand .getCurrentResizePolicy(); if (currentResizePolicy == null) return; boolean ltr = c.getComponentOrientation().isLeftToRight(); // need place for border Insets ins = c.getInsets(); int x = ins.left; int gap = getLayoutGap(); int availableHeight = c.getHeight() - ins.top - ins.bottom; if (SwingUtilities.getAncestorOfClass( CollapsedButtonPopupPanel.class, c) != null) { List resizePolicies = ribbonBand .getResizePolicies(); // install the most permissive resize policy on the popup // panel of a collapsed ribbon band resizePolicies.get(0).install(availableHeight, gap); } else { if (currentResizePolicy instanceof IconRibbonBandResizePolicy) { return; } // Installs the resize policy currentResizePolicy.install(availableHeight, gap); } // compute the max preferred height of the components and the // number of rows int maxHeight = 0; int rowCount = 1; for (JComponent flowComponent : flowBandControlPanel .getFlowComponents()) { Dimension prefSize = flowComponent.getPreferredSize(); if ((x + prefSize.width) > (c.getWidth() - ins.right)) { x = ins.left; rowCount++; } x += prefSize.width + gap; maxHeight = Math.max(maxHeight, prefSize.height); } // rowCount++; int vGap = (availableHeight - rowCount * maxHeight) / rowCount; if (vGap < 0) { vGap = 2; maxHeight = (availableHeight - vGap * (rowCount - 1)) / rowCount; } int y = ins.top + vGap / 2; x = ltr ? ins.left : c.getWidth() - ins.right; int rowIndex = 0; for (JComponent flowComponent : flowBandControlPanel .getFlowComponents()) { Dimension prefSize = flowComponent.getPreferredSize(); if (ltr) { if ((x + prefSize.width) > (c.getWidth() - ins.right)) { x = ins.left; y += (maxHeight + vGap); rowIndex++; } } else { if ((x - prefSize.width) < ins.left) { x = c.getWidth() - ins.right; y += (maxHeight + vGap); rowIndex++; } } int height = Math.min(maxHeight, prefSize.height); if (ltr) { flowComponent.setBounds(x, y + (maxHeight - height) / 2, prefSize.width, height); } else { flowComponent.setBounds(x - prefSize.width, y + (maxHeight - height) / 2, prefSize.width, height); } flowComponent.putClientProperty( AbstractBandControlPanelUI.TOP_ROW, Boolean .valueOf(rowIndex == 0)); flowComponent.putClientProperty( AbstractBandControlPanelUI.MID_ROW, Boolean .valueOf((rowIndex > 0) && (rowIndex < (rowCount - 1)))); flowComponent.putClientProperty( AbstractBandControlPanelUI.BOTTOM_ROW, Boolean .valueOf(rowIndex == (rowCount - 1))); if (ltr) { x += (prefSize.width + gap); } else { x -= (prefSize.width + gap); } } } } } src/org/pushingpixels/flamingo/internal/ui/ribbon/AbstractBandControlPanel.java0000644000175000017500000000477411401230446027125 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import javax.swing.JPanel; import javax.swing.plaf.UIResource; import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand; import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; /** * Control panel of a single {@link JRibbonBand}. This class is for internal use * only and should not be directly used by the applications. * * @author Kirill Grouchnikov */ public class AbstractBandControlPanel extends JPanel implements UIResource { private AbstractRibbonBand ribbonBand; public AbstractBandControlPanel() { } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUI() */ @Override public BandControlPanelUI getUI() { return (BandControlPanelUI) ui; } public void setRibbonBand(AbstractRibbonBand ribbonBand) { this.ribbonBand = ribbonBand; } public AbstractRibbonBand getRibbonBand() { return this.ribbonBand; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/BasicBandControlPanelUI.java0000644000175000017500000004447311412757716026661 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.AbstractCommandButton; import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy; import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; import org.pushingpixels.flamingo.internal.ui.ribbon.BasicRibbonBandUI.CollapsedButtonPopupPanel; /** * Basic UI for control panel of ribbon band {@link JBandControlPanel}. * * @author Kirill Grouchnikov */ public class BasicBandControlPanelUI extends AbstractBandControlPanelUI { private JSeparator[] groupSeparators; private JLabel[] groupLabels; protected ChangeListener changeListener; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicBandControlPanelUI(); } /** * Invoked by installUI to create a layout manager object to * manage the {@link JBandControlPanel}. * * @return a layout manager object */ @Override protected LayoutManager createLayoutManager() { return new ControlPanelLayout(); } @Override protected void installListeners() { super.installListeners(); this.changeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { syncGroupHeaders(); controlPanel.revalidate(); } }; ((JBandControlPanel) this.controlPanel) .addChangeListener(this.changeListener); } @Override protected void uninstallListeners() { ((JBandControlPanel) this.controlPanel) .removeChangeListener(this.changeListener); this.changeListener = null; super.uninstallListeners(); } @Override protected void installComponents() { super.installComponents(); this.syncGroupHeaders(); } @Override protected void uninstallComponents() { if (this.groupSeparators != null) { for (JSeparator groupSeparator : this.groupSeparators) { this.controlPanel.remove(groupSeparator); } } if (this.groupLabels != null) { for (JLabel groupLabel : this.groupLabels) { if (groupLabel != null) this.controlPanel.remove(groupLabel); } } super.uninstallComponents(); } protected void syncGroupHeaders() { if (this.groupSeparators != null) { for (JSeparator groupSeparator : this.groupSeparators) { this.controlPanel.remove(groupSeparator); } } if (this.groupLabels != null) { for (JLabel groupLabel : this.groupLabels) { if (groupLabel != null) this.controlPanel.remove(groupLabel); } } int groupCount = ((JBandControlPanel) this.controlPanel) .getControlPanelGroupCount(); if (groupCount > 1) { this.groupSeparators = new JSeparator[groupCount - 1]; for (int i = 0; i < groupCount - 1; i++) { this.groupSeparators[i] = new JSeparator(JSeparator.VERTICAL); this.controlPanel.add(this.groupSeparators[i]); } } if (groupCount > 0) { this.groupLabels = new JLabel[groupCount]; for (int i = 0; i < groupCount; i++) { String title = ((JBandControlPanel) this.controlPanel) .getControlPanelGroupTitle(i); if (title != null) { this.groupLabels[i] = new JLabel(title); this.controlPanel.add(this.groupLabels[i]); } } } } /** * Layout for the control panel of ribbon band. * * @author Kirill Grouchnikov */ private class ControlPanelLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { // The height of ribbon band control panel is // computed based on the preferred height of a command // button in BIG state. int buttonHeight = dummy.getPreferredSize().height; int vGap = getLayoutGap() * 3 / 4; int minusGaps = buttonHeight - 2 * vGap; switch (minusGaps % 3) { case 1: buttonHeight += 2; break; case 2: buttonHeight++; break; } Insets ins = c.getInsets(); // System.out.println("Control panel pref = " // + (buttonHeight + ins.top + ins.bottom)); return new Dimension(c.getWidth(), buttonHeight + ins.top + ins.bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { return this.preferredLayoutSize(c); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { // System.out.println("Control panel real = " + c.getHeight()); AbstractRibbonBand ribbonBand = ((JBandControlPanel) c) .getRibbonBand(); RibbonBandResizePolicy currentResizePolicy = ribbonBand .getCurrentResizePolicy(); if (currentResizePolicy == null) return; boolean ltr = c.getComponentOrientation().isLeftToRight(); // need place for border Insets ins = c.getInsets(); int gap = getLayoutGap(); int x = ltr ? ins.left + gap / 2 : c.getWidth() - ins.right - gap / 2; int availableHeight = c.getHeight() - ins.top - ins.bottom; if (SwingUtilities.getAncestorOfClass( CollapsedButtonPopupPanel.class, c) != null) { // install the most permissive resize policy on the popup // panel of a collapsed ribbon band List resizePolicies = ribbonBand .getResizePolicies(); resizePolicies.get(0).install(availableHeight, gap); } else { if (currentResizePolicy instanceof IconRibbonBandResizePolicy) { return; } // Installs the resize policy - this updates the display // priority of all the galleries and buttons currentResizePolicy.install(availableHeight, gap); } int controlPanelGroupIndex = 0; for (JBandControlPanel.ControlPanelGroup controlPanelGroup : ((JBandControlPanel) controlPanel) .getControlPanelGroups()) { // handle the group separators if (controlPanelGroupIndex > 0) { int prefW = groupSeparators[controlPanelGroupIndex - 1] .getPreferredSize().width; int sepX = ltr ? x - gap + (gap - prefW) / 2 : x + gap / 2 - (gap - prefW) / 2; groupSeparators[controlPanelGroupIndex - 1].setBounds(sepX, ins.top, prefW, availableHeight); } boolean hasLeadingComponent = false; boolean isCoreContent = controlPanelGroup.isCoreContent(); if (isCoreContent) { // how much vertical space is available in each row? int singleRowHeight = availableHeight / 3; boolean hasTitle = (controlPanelGroup.getGroupTitle() != null); int maxWidthInCurrColumn = 0; if (hasTitle) { JLabel titleLabel = groupLabels[controlPanelGroupIndex]; int pw = titleLabel.getPreferredSize().width; int titleLabelHeight = Math.min(singleRowHeight - gap / 4, titleLabel.getPreferredSize().height); int yNudge = singleRowHeight - titleLabelHeight; int baseline = (titleLabelHeight > 0) ? titleLabel .getBaseline(pw, titleLabelHeight) : 0; if (ltr) { titleLabel.setBounds(x + gap, ins.top + yNudge - titleLabelHeight + baseline, pw, titleLabelHeight); } else { titleLabel.setBounds(x - gap - pw, ins.top + yNudge - titleLabelHeight + baseline, pw, titleLabelHeight); } maxWidthInCurrColumn = gap + pw; } List ribbonComps = controlPanelGroup .getRibbonComps(); Map ribbonCompRowSpans = controlPanelGroup .getRibbonCompsRowSpans(); List currColumn = new ArrayList(); // if a group has a title, then the core components in that // group will take two rows instead of three int startingRow = hasTitle ? 1 : 0; int rowIndex = startingRow; for (int i = 0; i < ribbonComps.size(); i++) { JRibbonComponent coreComp = ribbonComps.get(i); int prefWidth = coreComp.getPreferredSize().width; int rowSpan = ribbonCompRowSpans.get(coreComp); // do we need to start a new column? int nextRowIndex = rowIndex + rowSpan; if (nextRowIndex > 3) { if (ltr) { if (hasLeadingComponent) x += gap; x += maxWidthInCurrColumn; } else { if (hasLeadingComponent) x -= gap; x -= maxWidthInCurrColumn; } hasLeadingComponent = true; maxWidthInCurrColumn = 0; rowIndex = startingRow; currColumn.clear(); } // how much vertical space does a component get? int compHeight = Math.min(rowSpan * singleRowHeight - gap / 4, coreComp.getPreferredSize().height); int yNudge = rowSpan * singleRowHeight - compHeight; int y = rowIndex * singleRowHeight + ins.top; if (ltr) { coreComp.setBounds(x, y + yNudge, prefWidth, compHeight); } else { coreComp.setBounds(x - prefWidth, y + yNudge, prefWidth, compHeight); } maxWidthInCurrColumn = Math.max(maxWidthInCurrColumn, prefWidth); currColumn.add(coreComp); coreComp.putClientProperty( AbstractBandControlPanelUI.TOP_ROW, Boolean .valueOf(rowIndex == 0)); coreComp.putClientProperty( AbstractBandControlPanelUI.MID_ROW, Boolean .valueOf((rowIndex > 0) && (rowIndex < 2))); coreComp.putClientProperty( AbstractBandControlPanelUI.BOTTOM_ROW, Boolean .valueOf(rowIndex == 2)); // scan the components in this column and make them to // have the same width as the widest component in this // column for (JRibbonComponent comp : currColumn) { Rectangle bounds = comp.getBounds(); if (ltr) { comp.setBounds(bounds.x, bounds.y, maxWidthInCurrColumn, bounds.height); } else { comp.setBounds(bounds.x + bounds.width - maxWidthInCurrColumn, bounds.y, maxWidthInCurrColumn, bounds.height); } comp.doLayout(); } // System.out // .println(rowSpan + ":" + coreComp.getBounds()); rowIndex += rowSpan; } if ((rowIndex > 0) && (rowIndex <= 3)) { if (ltr) { if (hasLeadingComponent) x += gap; x += maxWidthInCurrColumn; } else { if (hasLeadingComponent) x -= gap; x -= maxWidthInCurrColumn; } hasLeadingComponent = true; } } else { // The galleries for (RibbonElementPriority elementPriority : RibbonElementPriority .values()) { for (JRibbonGallery gallery : controlPanelGroup .getRibbonGalleries(elementPriority)) { int pw = gallery.getPreferredWidth(gallery .getDisplayPriority(), availableHeight); if (ltr) { gallery.setBounds(x, ins.top, pw, availableHeight); if (hasLeadingComponent) x += gap; x += pw; } else { gallery.setBounds(x - pw, ins.top, pw, availableHeight); if (hasLeadingComponent) x -= gap; x -= pw; } hasLeadingComponent = true; } } Map> buttonMap = new HashMap>(); for (RibbonElementPriority elementPriority : RibbonElementPriority .values()) { for (AbstractCommandButton commandButton : controlPanelGroup .getRibbonButtons(elementPriority)) { CommandButtonDisplayState state = commandButton .getDisplayState(); if (buttonMap.get(state) == null) { buttonMap.put(state, new ArrayList()); } buttonMap.get(state).add(commandButton); } } List bigs = buttonMap .get(CommandButtonDisplayState.BIG); if (bigs != null) { for (AbstractCommandButton bigButton : bigs) { // Big buttons int bigButtonWidth = bigButton.getPreferredSize().width; if (hasLeadingComponent) { if (ltr) { x += gap; } else { x -= gap; } } if (ltr) { bigButton.setBounds(x, ins.top, bigButtonWidth, availableHeight); } else { bigButton.setBounds(x - bigButtonWidth, ins.top, bigButtonWidth, availableHeight); } bigButton.putClientProperty(TOP_ROW, Boolean.FALSE); bigButton.putClientProperty(MID_ROW, Boolean.FALSE); bigButton.putClientProperty(BOTTOM_ROW, Boolean.FALSE); if (ltr) { x += bigButtonWidth; } else { x -= bigButtonWidth; } hasLeadingComponent = true; } } // Medium buttons int vGap = gap * 3 / 4; int medSmallButtonHeight = (availableHeight - 2 * vGap) / 3; int index3 = 0; int maxWidth3 = 0; List mediums = buttonMap .get(CommandButtonDisplayState.MEDIUM); if (mediums != null) { for (AbstractCommandButton mediumButton : mediums) { int medWidth = mediumButton.getPreferredSize().width; maxWidth3 = Math.max(maxWidth3, medWidth); if (hasLeadingComponent && (index3 == 0)) { if (ltr) { x += gap; } else { x -= gap; } } int buttonTop = (medSmallButtonHeight + vGap) * index3; int buttonBottom = (medSmallButtonHeight + vGap) * (index3 + 1) - vGap; if (ltr) { mediumButton.setBounds(x, ins.top + buttonTop, medWidth, buttonBottom - buttonTop); } else { mediumButton.setBounds(x - medWidth, ins.top + buttonTop, medWidth, buttonBottom - buttonTop); } mediumButton.putClientProperty(TOP_ROW, Boolean .valueOf(index3 == 0)); mediumButton.putClientProperty(MID_ROW, Boolean .valueOf(index3 == 1)); mediumButton.putClientProperty(BOTTOM_ROW, Boolean .valueOf(index3 == 2)); index3++; if (index3 == 3) { // last button in threesome index3 = 0; if (ltr) { x += maxWidth3; } else { x -= maxWidth3; } hasLeadingComponent = true; maxWidth3 = 0; } } } // at this point, maxWidth3 may be non-null. We can safely // add it, since in this case there will be no buttons // left in lowButtons if (maxWidth3 > 0) { if (ltr) { x += maxWidth3; } else { x -= maxWidth3; } hasLeadingComponent = true; } index3 = 0; maxWidth3 = 0; List smalls = buttonMap .get(CommandButtonDisplayState.SMALL); if (smalls != null) { for (AbstractCommandButton smallButton : smalls) { int lowWidth = smallButton.getPreferredSize().width; maxWidth3 = Math.max(maxWidth3, lowWidth); if (hasLeadingComponent && (index3 == 0)) { if (ltr) { x += gap; } else { x -= gap; } } int buttonTop = (medSmallButtonHeight + vGap) * index3; int buttonBottom = (medSmallButtonHeight + vGap) * (index3 + 1) - vGap; if (ltr) { smallButton.setBounds(x, ins.top + buttonTop, lowWidth, buttonBottom - buttonTop); } else { smallButton.setBounds(x - lowWidth, ins.top + buttonTop, lowWidth, buttonBottom - buttonTop); } smallButton.putClientProperty(TOP_ROW, Boolean .valueOf(index3 == 0)); smallButton.putClientProperty(MID_ROW, Boolean .valueOf(index3 == 1)); smallButton.putClientProperty(BOTTOM_ROW, Boolean .valueOf(index3 == 2)); index3++; if (index3 == 3) { // last button in threesome index3 = 0; if (ltr) { x += maxWidth3; } else { x -= maxWidth3; } hasLeadingComponent = true; maxWidth3 = 0; } } } if ((index3 < 3) && (maxWidth3 > 0)) { if (ltr) { x += maxWidth3; } else { x -= maxWidth3; } hasLeadingComponent = true; } } // space for the separator if (ltr) { x += gap * 3 / 2; } else { x -= gap * 3 / 2; } controlPanelGroupIndex++; } } } } src/org/pushingpixels/flamingo/internal/ui/ribbon/RibbonUI.java0000644000175000017500000000475311401230446023722 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.Rectangle; import java.awt.event.MouseWheelEvent; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.ribbon.JRibbon; import org.pushingpixels.flamingo.api.ribbon.RibbonContextualTaskGroup; /** * UI for ribbon ({@link JRibbon}). * * @author Kirill Grouchnikov */ public abstract class RibbonUI extends ComponentUI { /** * Returns the bounds of the specified contextual task group. * * @param group * Contextual task group. * @return The bounds of the specified contextual task group. */ public abstract Rectangle getContextualTaskGroupBounds( RibbonContextualTaskGroup group); public abstract boolean isShowingScrollsForTaskToggleButtons(); public abstract boolean isShowingScrollsForBands(); public abstract void handleMouseWheelEvent(MouseWheelEvent e); } src/org/pushingpixels/flamingo/internal/ui/ribbon/BasicRibbonComponentUI.java0000644000175000017500000003411611422342760026551 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.image.ColorConvertOp; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.common.HorizontalAlignment; import org.pushingpixels.flamingo.api.common.icon.FilteredResizableIcon; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent; import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; public class BasicRibbonComponentUI extends RibbonComponentUI { /** * The associated ribbon component. */ protected JRibbonComponent ribbonComponent; protected JLabel captionLabel; protected PropertyChangeListener propertyChangeListener; protected ResizableIcon disabledIcon; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicRibbonComponentUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.ribbonComponent = (JRibbonComponent) c; installDefaults(); installComponents(); installListeners(); c.setLayout(createLayoutManager()); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { c.setLayout(null); uninstallListeners(); uninstallDefaults(); uninstallComponents(); } /** * Installs default parameters on the associated ribbon component. */ protected void installDefaults() { if (!this.ribbonComponent.isSimpleWrapper()) { ResizableIcon icon = this.ribbonComponent.getIcon(); if (icon != null) { icon.setDimension(new Dimension(16, 16)); this.disabledIcon = createDisabledIcon(); } } this.ribbonComponent.getMainComponent().setOpaque(false); this.ribbonComponent.setOpaque(false); } /** * Installs subcomponents on the associated ribbon component. */ protected void installComponents() { this.captionLabel = new JLabel(this.ribbonComponent.getCaption()); this.captionLabel.setEnabled(this.ribbonComponent.isEnabled()); this.ribbonComponent.add(this.captionLabel); JComponent mainComponent = this.ribbonComponent.getMainComponent(); this.ribbonComponent.add(mainComponent); } /** * Installs listeners on the associated ribbon component. */ protected void installListeners() { this.propertyChangeListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("enabled".equals(evt.getPropertyName())) { boolean isEnabled = (Boolean) evt.getNewValue(); ribbonComponent.getMainComponent().setEnabled(isEnabled); if (!ribbonComponent.isSimpleWrapper()) { captionLabel.setEnabled(isEnabled); } ribbonComponent.repaint(); } if ("caption".equals(evt.getPropertyName())) { captionLabel.setText((String) evt.getNewValue()); } if ("displayPriority".equals(evt.getPropertyName())) { ribbonComponent.revalidate(); ribbonComponent.doLayout(); } } }; this.ribbonComponent .addPropertyChangeListener(this.propertyChangeListener); } /** * Uninstalls default parameters from the associated ribbon component. */ protected void uninstallDefaults() { } /** * Uninstalls components from the associated ribbon component. */ protected void uninstallComponents() { JComponent mainComponent = this.ribbonComponent.getMainComponent(); this.ribbonComponent.remove(mainComponent); this.ribbonComponent.remove(this.captionLabel); this.captionLabel = null; } /** * Uninstalls listeners from the associated ribbon component. */ protected void uninstallListeners() { this.ribbonComponent .removePropertyChangeListener(this.propertyChangeListener); this.propertyChangeListener = null; } @Override public Point getKeyTipAnchorCenterPoint() { if (this.ribbonComponent.isSimpleWrapper()) { return new Point( this.ribbonComponent.getMainComponent().getX() + 10, this.ribbonComponent.getHeight()); } else { return new Point(this.captionLabel.getX(), this.ribbonComponent .getHeight()); } } protected LayoutManager createLayoutManager() { return new ExtComponentLayout(); } protected class ExtComponentLayout implements LayoutManager { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension minimumLayoutSize(Container parent) { Insets ins = ribbonComponent.getInsets(); JComponent mainComponent = ribbonComponent.getMainComponent(); Dimension minMain = mainComponent.getMinimumSize(); int width = ins.left; int height = minMain.height; if (isIconVisible(ribbonComponent.getDisplayPriority())) { ResizableIcon icon = ribbonComponent.getIcon(); if (icon != null) { width += (icon.getIconWidth() + getLayoutGap()); height = Math.max(height, icon.getIconHeight()); } } if (isCaptionVisible(ribbonComponent.getDisplayPriority())) { Dimension minCaption = captionLabel.getMinimumSize(); width += (minCaption.width + getLayoutGap()); height = Math.max(height, minCaption.height); } width += minMain.width; width += ins.right; height += (ins.top + ins.bottom); return new Dimension(width, height); } @Override public Dimension preferredLayoutSize(Container parent) { return getPreferredSize(ribbonComponent.getDisplayPriority()); } @Override public void layoutContainer(Container parent) { JRibbonComponent ribbonComp = (JRibbonComponent) parent; Insets ins = ribbonComp.getInsets(); int gap = getLayoutGap(); int availableHeight = ribbonComp.getHeight() - ins.top - ins.bottom; int availableWidth = ribbonComp.getWidth() - ins.left - ins.right; HorizontalAlignment horizAlignment = ribbonComp .getHorizontalAlignment(); JComponent mainComp = ribbonComp.getMainComponent(); Dimension prefMainDim = mainComp.getPreferredSize(); int prefMainWidth = prefMainDim.width; int finalHeight = Math.min(prefMainDim.height, availableHeight); boolean ltr = ribbonComp.getComponentOrientation().isLeftToRight(); if (ribbonComp.isSimpleWrapper()) { int finalMainWidth = Math.min(availableWidth, prefMainWidth); int offset = availableWidth - prefMainWidth; int topMain = ins.top + (availableHeight - finalHeight) / 2; int x = ltr ? ins.left : ribbonComp.getWidth() - ins.right; switch (horizAlignment) { case LEADING: if (ltr) { mainComp.setBounds(x, topMain, finalMainWidth, finalHeight); } else { mainComp.setBounds(x - finalMainWidth, topMain, finalMainWidth, finalHeight); } break; case TRAILING: if (ltr) { mainComp.setBounds(x + offset, topMain, finalMainWidth, finalHeight); } else { mainComp.setBounds(x - finalMainWidth - offset, topMain, finalMainWidth, finalHeight); } break; case CENTER: if (ltr) { mainComp.setBounds(x + offset / 2, topMain, finalMainWidth, finalHeight); } else { mainComp.setBounds(x - finalMainWidth - offset / 2, topMain, finalMainWidth, finalHeight); } break; case FILL: if (ltr) { mainComp.setBounds(x, topMain, availableWidth, finalHeight); } else { mainComp.setBounds(x - availableWidth, topMain, availableWidth, finalHeight); } break; } mainComp.doLayout(); } else { int x = ltr ? ins.left : ribbonComp.getWidth() - ins.right; if (isIconVisible(ribbonComponent.getDisplayPriority())) { if (ribbonComp.getIcon() != null) { // icon is painted separately int iconW = ribbonComp.getIcon().getIconWidth(); x = ltr ? x + iconW + gap : x - iconW - gap; } } if (isCaptionVisible(ribbonComponent.getDisplayPriority())) { captionLabel.setVisible(true); Dimension prefCaptionDim = captionLabel.getPreferredSize(); if (ltr) { captionLabel .setBounds( x, ins.top + (availableHeight - prefCaptionDim.height) / 2, prefCaptionDim.width, prefCaptionDim.height); x += (prefCaptionDim.width + gap); } else { captionLabel .setBounds( x - prefCaptionDim.width, ins.top + (availableHeight - prefCaptionDim.height) / 2, prefCaptionDim.width, prefCaptionDim.height); x -= (prefCaptionDim.width + gap); } } else { captionLabel.setVisible(false); } int topMain = ins.top + (availableHeight - finalHeight) / 2; int finalMainWidth = ltr ? Math.min(ribbonComp.getWidth() - ins.right - x, prefMainWidth) : Math.min( x - ins.left, prefMainWidth); int offset = ltr ? ribbonComp.getWidth() - ins.right - x - prefMainWidth : x - prefMainWidth - ins.left; switch (horizAlignment) { case LEADING: if (ltr) { mainComp.setBounds(x, topMain, finalMainWidth, finalHeight); } else { mainComp.setBounds(x - finalMainWidth, topMain, finalMainWidth, finalHeight); } break; case TRAILING: if (ltr) { mainComp.setBounds(x + offset, topMain, finalMainWidth, finalHeight); } else { mainComp.setBounds(x - finalMainWidth - offset, topMain, finalMainWidth, finalHeight); } break; case CENTER: if (ltr) { mainComp.setBounds(x + offset / 2, topMain, finalMainWidth, finalHeight); } else { mainComp.setBounds(x - finalMainWidth - offset / 2, topMain, finalMainWidth, finalHeight); } break; case FILL: if (ltr) { mainComp.setBounds(x, topMain, ribbonComp.getWidth() - ins.right - x, finalHeight); } else { mainComp.setBounds(ins.left, topMain, x - ins.left, finalHeight); } break; } mainComp.doLayout(); } } } @Override public void paint(Graphics g, JComponent c) { JRibbonComponent ribbonComp = (JRibbonComponent) c; if (isIconVisible(this.ribbonComponent.getDisplayPriority())) { Insets ins = ribbonComp.getInsets(); ResizableIcon icon = ribbonComp.isEnabled() ? ribbonComp.getIcon() : this.disabledIcon; if (icon != null) { int availableHeight = ribbonComp.getHeight() - ins.top - ins.bottom; int iconY = Math.max(0, ins.top + (availableHeight - icon.getIconHeight()) / 2); if (ribbonComp.getComponentOrientation().isLeftToRight()) { paintIcon(g, ribbonComp, icon, ins.left, iconY); } else { paintIcon(g, ribbonComp, icon, ribbonComp.getWidth() - ins.right - icon.getIconWidth(), iconY); } } } } protected void paintIcon(Graphics g, JRibbonComponent ribbonComp, Icon icon, int x, int y) { icon.paintIcon(ribbonComp, g, x, y); } protected int getLayoutGap() { return 4; } protected ResizableIcon createDisabledIcon() { return new FilteredResizableIcon(this.ribbonComponent.getIcon(), new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null)); } protected boolean isIconVisible(RibbonElementPriority displayPriority) { if (this.ribbonComponent.isSimpleWrapper()) return false; return (displayPriority == RibbonElementPriority.TOP) || (displayPriority == RibbonElementPriority.MEDIUM); } protected boolean isCaptionVisible(RibbonElementPriority displayPriority) { if (this.ribbonComponent.isSimpleWrapper()) return false; return (displayPriority == RibbonElementPriority.TOP); } @Override public Dimension getPreferredSize(RibbonElementPriority priority) { Insets ins = ribbonComponent.getInsets(); JComponent mainComponent = ribbonComponent.getMainComponent(); Dimension prefMain = mainComponent.getPreferredSize(); int width = ins.left; int height = prefMain.height; if (isIconVisible(priority)) { ResizableIcon icon = ribbonComponent.getIcon(); if (icon != null) { width += (icon.getIconWidth() + getLayoutGap()); height = Math.max(height, icon.getIconHeight()); } } if (isCaptionVisible(priority)) { Dimension prefCaption = captionLabel.getPreferredSize(); width += (prefCaption.width + getLayoutGap()); height = Math.max(height, prefCaption.height); } width += prefMain.width; width += ins.right; height += (ins.top + ins.bottom); return new Dimension(width, height); } } src/org/pushingpixels/flamingo/internal/ui/ribbon/BasicRibbonUI.java0000644000175000017500000020221511411155336024662 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.awt.event.*; import java.awt.geom.Arc2D; import java.awt.geom.GeneralPath; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.*; import javax.swing.plaf.basic.BasicGraphicsUtils; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager.PopupEvent; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizeSequencingPolicy; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonUI; import org.pushingpixels.flamingo.internal.ui.ribbon.appmenu.JRibbonApplicationMenuButton; import org.pushingpixels.flamingo.internal.utils.*; /** * Basic UI for ribbon {@link JRibbon}. * * @author Kirill Grouchnikov */ public class BasicRibbonUI extends RibbonUI { /** * Client property marking the ribbon component to indicate whether the task * bar and contextual task group headers should be shown on the title pane * of the window. This is only relevant for the {@link JRibbonFrame}. */ public static final String IS_USING_TITLE_PANE = "ribbon.internal.isUsingTitlePane"; private static final String JUST_MINIMIZED = "ribbon.internal.justMinimized"; /** * The associated ribbon. */ protected JRibbon ribbon; /** * Mouse wheel listener to switch between ribbon tasks. */ // protected MouseWheelListener mouseWheelListener; /** * Taskbar panel. */ protected JPanel taskBarPanel; protected JScrollablePanel bandScrollablePanel; protected JScrollablePanel taskToggleButtonsScrollablePanel; protected JRibbonApplicationMenuButton applicationMenuButton; protected JCommandButton helpButton; /** * Map of toggle buttons of all tasks. */ protected Map taskToggleButtons; /** * Button group for task toggle buttons. */ protected CommandToggleButtonGroup taskToggleButtonGroup; /** * Change listener. */ protected ChangeListener ribbonChangeListener; /** * Property change listener. */ protected PropertyChangeListener propertyChangeListener; protected ContainerListener ribbonContainerListener; protected ComponentListener ribbonComponentListener; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicRibbonUI(); } /** * Creates a new basic ribbon UI delegate. */ public BasicRibbonUI() { this.taskToggleButtons = new HashMap(); this.taskToggleButtonGroup = new CommandToggleButtonGroup(); this.taskToggleButtonGroup.setAllowsClearingSelection(false); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.ribbon = (JRibbon) c; installDefaults(); installComponents(); installListeners(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { uninstallListeners(); uninstallComponents(); uninstallDefaults(); this.ribbon = null; } /** * Installs listeners on the associated ribbon. */ protected void installListeners() { // this.mouseWheelListener = new MouseWheelListener() { // public void mouseWheelMoved(MouseWheelEvent e) { // handleMouseWheelEvent(e); // } // }; // this.taskToggleButtonsScrollablePanel.getView().addMouseWheelListener( // this.mouseWheelListener); // this.ribbonChangeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { syncRibbonState(); } }; this.ribbon.addChangeListener(this.ribbonChangeListener); this.propertyChangeListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("selectedTask".equals(evt.getPropertyName())) { RibbonTask old = (RibbonTask) evt.getOldValue(); final RibbonTask curr = (RibbonTask) evt.getNewValue(); if ((old != null) && (taskToggleButtons.get(old) != null)) { taskToggleButtons.get(old).getActionModel() .setSelected(false); } if ((curr != null) && (taskToggleButtons.get(curr) != null)) { taskToggleButtons.get(curr).getActionModel() .setSelected(true); } if (isShowingScrollsForTaskToggleButtons() && (curr != null)) { // scroll selected task as necessary so that it's // visible JRibbonTaskToggleButton toggleButton = taskToggleButtons .get(curr); if (toggleButton != null) { scrollAndRevealTaskToggleButton(toggleButton); } } // Special case for showing key tips of ribbon tasks. // When a ribbon task is selected with a key tip, its // showing and layout is deferred as a separate Runnable // on EDT. When the key chain for that task is created, // the command buttons are not at their final size yet // and no key tips are shown. // Here we schedule yet another Runnable // to recompute all keytips if the // originator is a task toggle button. SwingUtilities.invokeLater(new Runnable() { @Override public void run() { KeyTipManager ktm = KeyTipManager.defaultManager(); if (ktm.isShowingKeyTips()) { KeyTipManager.KeyTipChain chain = ktm .getCurrentlyShownKeyTipChain(); if (chain.chainParentComponent == taskToggleButtons .get(curr)) { ktm.refreshCurrentChain(); } } } }); } if ("applicationMenuRichTooltip".equals(evt.getPropertyName())) { syncApplicationMenuTips(); } if ("applicationMenuKeyTip".equals(evt.getPropertyName())) { syncApplicationMenuTips(); } if ("applicationMenu".equals(evt.getPropertyName())) { ribbon.revalidate(); ribbon.doLayout(); ribbon.repaint(); Window windowAncestor = SwingUtilities .getWindowAncestor(ribbon); if (windowAncestor instanceof JRibbonFrame) { FlamingoUtilities .updateRibbonFrameIconImages((JRibbonFrame) windowAncestor); } } if ("minimized".equals(evt.getPropertyName())) { PopupPanelManager.defaultManager().hidePopups(null); ribbon.revalidate(); ribbon.doLayout(); ribbon.repaint(); } } }; this.ribbon.addPropertyChangeListener(this.propertyChangeListener); this.ribbonContainerListener = new ContainerAdapter() { @Override public void componentAdded(ContainerEvent e) { if (isUsingTitlePane()) return; Component added = e.getComponent(); if (added != applicationMenuButton) { ribbon.setComponentZOrder(applicationMenuButton, ribbon .getComponentCount() - 1); } } }; this.ribbon.addContainerListener(this.ribbonContainerListener); this.ribbonComponentListener = new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { KeyTipManager.defaultManager().hideAllKeyTips(); } }; this.ribbon.addComponentListener(this.ribbonComponentListener); } /** * Uninstalls listeners from the associated ribbon. */ protected void uninstallListeners() { // this.taskToggleButtonsScrollablePanel.getView() // .removeMouseWheelListener(this.mouseWheelListener); // this.mouseWheelListener = null; // this.ribbon.removeChangeListener(this.ribbonChangeListener); this.ribbonChangeListener = null; this.ribbon.removePropertyChangeListener(this.propertyChangeListener); this.propertyChangeListener = null; this.ribbon.removeContainerListener(this.ribbonContainerListener); this.ribbonContainerListener = null; this.ribbon.removeComponentListener(this.ribbonComponentListener); this.ribbonComponentListener = null; } /** * Installs defaults on the associated ribbon. */ protected void installDefaults() { Border b = this.ribbon.getBorder(); if (b == null || b instanceof UIResource) { Border toSet = UIManager.getBorder("Ribbon.border"); if (toSet == null) toSet = new BorderUIResource.EmptyBorderUIResource(1, 2, 1, 2); this.ribbon.setBorder(toSet); } } /** * Uninstalls defaults from the associated ribbon. */ protected void uninstallDefaults() { } /** * Installs subcomponents on the associated ribbon. */ protected void installComponents() { // taskbar panel this.taskBarPanel = new TaskbarPanel(); this.taskBarPanel.setName("JRibbon Task Bar"); this.taskBarPanel.setLayout(createTaskbarLayoutManager()); this.ribbon.add(this.taskBarPanel); // band scrollable panel BandHostPanel bandHostPanel = createBandHostPanel(); bandHostPanel.setLayout(createBandHostPanelLayoutManager()); this.bandScrollablePanel = new JScrollablePanel( bandHostPanel, JScrollablePanel.ScrollType.HORIZONTALLY); this.bandScrollablePanel.setScrollOnRollover(false); this.ribbon.add(this.bandScrollablePanel); // task toggle buttons scrollable panel TaskToggleButtonsHostPanel taskToggleButtonsHostPanel = createTaskToggleButtonsHostPanel(); taskToggleButtonsHostPanel .setLayout(createTaskToggleButtonsHostPanelLayoutManager()); this.taskToggleButtonsScrollablePanel = new JScrollablePanel( taskToggleButtonsHostPanel, JScrollablePanel.ScrollType.HORIZONTALLY); this.taskToggleButtonsScrollablePanel.setScrollOnRollover(false); this.taskToggleButtonsScrollablePanel .addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { // need to repaint the entire ribbon since scrolling // the task toggle buttons affects the contour outline // of the ribbon ribbon.repaint(); } }); this.ribbon.add(this.taskToggleButtonsScrollablePanel); this.ribbon.setLayout(createLayoutManager()); this.syncRibbonState(); this.applicationMenuButton = new JRibbonApplicationMenuButton( this.ribbon); this.syncApplicationMenuTips(); this.ribbon.add(applicationMenuButton); Window windowAncestor = SwingUtilities.getWindowAncestor(this.ribbon); if (windowAncestor instanceof JRibbonFrame) { FlamingoUtilities .updateRibbonFrameIconImages((JRibbonFrame) windowAncestor); } } protected LayoutManager createTaskToggleButtonsHostPanelLayoutManager() { return new TaskToggleButtonsHostPanelLayout(); } protected TaskToggleButtonsHostPanel createTaskToggleButtonsHostPanel() { return new TaskToggleButtonsHostPanel(); } protected BandHostPanel createBandHostPanel() { return new BandHostPanel(); } protected LayoutManager createBandHostPanelLayoutManager() { return new BandHostPanelLayout(); } /** * Uninstalls subcomponents from the associated ribbon. */ protected void uninstallComponents() { this.taskBarPanel.removeAll(); this.taskBarPanel.setLayout(null); this.ribbon.remove(this.taskBarPanel); BandHostPanel bandHostPanel = this.bandScrollablePanel.getView(); bandHostPanel.removeAll(); bandHostPanel.setLayout(null); this.ribbon.remove(this.bandScrollablePanel); TaskToggleButtonsHostPanel taskToggleButtonsHostPanel = this.taskToggleButtonsScrollablePanel .getView(); taskToggleButtonsHostPanel.removeAll(); taskToggleButtonsHostPanel.setLayout(null); this.ribbon.remove(this.taskToggleButtonsScrollablePanel); this.ribbon.remove(this.applicationMenuButton); if (this.helpButton != null) this.ribbon.remove(this.helpButton); this.ribbon.setLayout(null); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void update(Graphics g, JComponent c) { Graphics2D g2d = (Graphics2D) g.create(); RenderingUtils.installDesktopHints(g2d); super.update(g2d, c); g2d.dispose(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void paint(Graphics g, JComponent c) { this.paintBackground(g); if (!ribbon.isMinimized()) { Insets ins = c.getInsets(); int extraHeight = getTaskToggleButtonHeight(); if (!this.isUsingTitlePane()) extraHeight += getTaskbarHeight(); this.paintTaskArea(g, 0, ins.top + extraHeight, c.getWidth(), c .getHeight() - extraHeight - ins.top - ins.bottom); } else { this.paintMinimizedRibbonSeparator(g); } } protected void paintMinimizedRibbonSeparator(Graphics g) { Color borderColor = FlamingoUtilities.getBorderColor(); g.setColor(borderColor); Insets ins = ribbon.getInsets(); g.drawLine(0, ribbon.getHeight() - ins.bottom, ribbon.getWidth(), ribbon.getHeight() - ins.bottom); } /** * Paints the ribbon background. * * @param g * Graphics context. */ protected void paintBackground(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(FlamingoUtilities.getColor(Color.lightGray, "Panel.background")); g2d.fillRect(0, 0, this.ribbon.getWidth(), this.ribbon.getHeight()); g2d.dispose(); } /** * Paints the task border. * * @param g * Graphics context. * @param x * Left X of the tasks band bounds. * @param y * Top Y of the tasks band bounds. * @param width * Width of the tasks band bounds. * @param height * Height of the tasks band bounds. */ protected void paintTaskArea(Graphics g, int x, int y, int width, int height) { if (ribbon.getTaskCount() == 0) return; JRibbonTaskToggleButton selectedTaskButton = this.taskToggleButtons .get(this.ribbon.getSelectedTask()); Rectangle selectedTaskButtonBounds = selectedTaskButton.getBounds(); Point converted = SwingUtilities.convertPoint(selectedTaskButton .getParent(), selectedTaskButtonBounds.getLocation(), this.ribbon); // System.out.println("Painted " + selectedTaskButtonBounds.x + "->" + // converted.x); Rectangle taskToggleButtonsViewportBounds = taskToggleButtonsScrollablePanel .getView().getParent().getBounds(); taskToggleButtonsViewportBounds.setLocation(SwingUtilities .convertPoint(taskToggleButtonsScrollablePanel, taskToggleButtonsViewportBounds.getLocation(), this.ribbon)); int startSelectedX = Math.max(converted.x + 1, (int) taskToggleButtonsViewportBounds.getMinX()); startSelectedX = Math.min(startSelectedX, (int) taskToggleButtonsViewportBounds.getMaxX()); int endSelectedX = Math.min(converted.x + selectedTaskButtonBounds.width - 1, (int) taskToggleButtonsViewportBounds.getMaxX()); endSelectedX = Math.max(endSelectedX, (int) taskToggleButtonsViewportBounds.getMinX()); Shape outerContour = FlamingoUtilities.getRibbonBorderOutline(x + 1, x + width - 3, startSelectedX, endSelectedX, converted.y, y, y + height, 2); Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(FlamingoUtilities.getBorderColor()); g2d.draw(outerContour); // check whether the currently selected task is a contextual task RibbonTask selected = this.ribbon.getSelectedTask(); RibbonContextualTaskGroup contextualGroup = selected .getContextualGroup(); if (contextualGroup != null) { // paint a small gradient directly below the task area Insets ins = this.ribbon.getInsets(); int topY = ins.top + getTaskbarHeight(); int bottomY = topY + 5; Color hueColor = contextualGroup.getHueColor(); Paint paint = new GradientPaint(0, topY, FlamingoUtilities .getAlphaColor(hueColor, (int) (255 * RibbonContextualTaskGroup.HUE_ALPHA)), 0, bottomY, FlamingoUtilities.getAlphaColor(hueColor, 0)); g2d.setPaint(paint); g2d.clip(outerContour); g2d.fillRect(0, topY, width, bottomY - topY + 1); } g2d.dispose(); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.ribbon.ui.RibbonUI#getContextualGroupTabBounds(org * .jvnet.flamingo.ribbon.RibbonContextualTaskGroup) */ @Override public Rectangle getContextualTaskGroupBounds( RibbonContextualTaskGroup group) { Rectangle rect = null; for (int j = 0; j < group.getTaskCount(); j++) { JRibbonTaskToggleButton button = taskToggleButtons.get(group .getTask(j)); if (rect == null) rect = button.getBounds(); else rect = rect.union(button.getBounds()); } int buttonGap = getTabButtonGap(); Point location = SwingUtilities.convertPoint( taskToggleButtonsScrollablePanel.getView(), rect.getLocation(), ribbon); return new Rectangle(location.x - buttonGap / 3, location.y - 1, rect.width + buttonGap * 2 / 3 - 1, rect.height + 1); } /** * Returns the layout gap for the bands in the associated ribbon. * * @return The layout gap for the bands in the associated ribbon. */ protected int getBandGap() { return 2; } /** * Returns the layout gap for the tab buttons in the associated ribbon. * * @return The layout gap for the tab buttons in the associated ribbon. */ protected int getTabButtonGap() { return 6; } /** * Invoked by installUI to create a layout manager object to * manage the {@link JRibbon}. * * @return a layout manager object */ protected LayoutManager createLayoutManager() { return new RibbonLayout(); } /** * Invoked by installUI to create a layout manager object to * manage the {@link JRibbon} taskbar. * * @return a layout manager object */ protected LayoutManager createTaskbarLayoutManager() { return new TaskbarLayout(); } /** * Returns the height of the taskbar area. * * @return The height of the taskbar area. */ public int getTaskbarHeight() { return 24; } /** * Returns the height of the task toggle button area. * * @return The height of the task toggle button area. */ public int getTaskToggleButtonHeight() { return 22; } /** * Layout for the ribbon. * * @author Kirill Grouchnikov */ private class RibbonLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { Insets ins = c.getInsets(); int maxPrefBandHeight = 0; boolean isRibbonMinimized = ribbon.isMinimized(); if (!isRibbonMinimized) { if (ribbon.getTaskCount() > 0) { RibbonTask selectedTask = ribbon.getSelectedTask(); for (AbstractRibbonBand ribbonBand : selectedTask .getBands()) { int bandPrefHeight = ribbonBand.getPreferredSize().height; Insets bandInsets = ribbonBand.getInsets(); maxPrefBandHeight = Math.max(maxPrefBandHeight, bandPrefHeight + bandInsets.top + bandInsets.bottom); } } } int extraHeight = getTaskToggleButtonHeight(); if (!isUsingTitlePane()) extraHeight += getTaskbarHeight(); int prefHeight = maxPrefBandHeight + extraHeight + ins.top + ins.bottom; // System.out.println("Ribbon pref = " + prefHeight); return new Dimension(c.getWidth(), prefHeight); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { // go over all ribbon bands and sum the width // of ribbon buttons (of collapsed state) Insets ins = c.getInsets(); int width = 0; int maxMinBandHeight = 0; int gap = getBandGap(); int extraHeight = getTaskToggleButtonHeight(); if (!isUsingTitlePane()) extraHeight += getTaskbarHeight(); if (ribbon.getTaskCount() > 0) { boolean isRibbonMinimized = ribbon.isMinimized(); // minimum is when all the tasks are collapsed RibbonTask selectedTask = ribbon.getSelectedTask(); for (AbstractRibbonBand ribbonBand : selectedTask.getBands()) { int bandPrefHeight = ribbonBand.getMinimumSize().height; Insets bandInsets = ribbonBand.getInsets(); RibbonBandUI bandUI = ribbonBand.getUI(); width += bandUI.getPreferredCollapsedWidth(); if (!isRibbonMinimized) { maxMinBandHeight = Math.max(maxMinBandHeight, bandPrefHeight + bandInsets.top + bandInsets.bottom); } } // add inter-band gaps width += gap * (selectedTask.getBandCount() - 1); } else { // fix for issue 44 (empty ribbon) width = 50; } return new Dimension(width, maxMinBandHeight + extraHeight + ins.top + ins.bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { // System.out.println("Ribbon real = " + c.getHeight()); Insets ins = c.getInsets(); int tabButtonGap = getTabButtonGap(); boolean ltr = ribbon.getComponentOrientation().isLeftToRight(); // the top row - task bar components int width = c.getWidth(); int taskbarHeight = getTaskbarHeight(); int y = ins.top; boolean isUsingTitlePane = isUsingTitlePane(); // handle taskbar only if it is not marked if (!isUsingTitlePane) { taskBarPanel.removeAll(); for (Component regComp : ribbon.getTaskbarComponents()) { taskBarPanel.add(regComp); } // taskbar takes all available width taskBarPanel.setBounds(ins.left, ins.top, width - ins.left - ins.right, taskbarHeight); y += taskbarHeight; } else { taskBarPanel.setBounds(0, 0, 0, 0); } int taskToggleButtonHeight = getTaskToggleButtonHeight(); int x = ltr ? ins.left : width - ins.right; // the application menu button int appMenuButtonSize = taskbarHeight + taskToggleButtonHeight; if (!isUsingTitlePane) { applicationMenuButton .setVisible(ribbon.getApplicationMenu() != null); if (ribbon.getApplicationMenu() != null) { if (ltr) { applicationMenuButton.setBounds(x, ins.top, appMenuButtonSize, appMenuButtonSize); } else { applicationMenuButton.setBounds(x - appMenuButtonSize, ins.top, appMenuButtonSize, appMenuButtonSize); } } } else { applicationMenuButton.setVisible(false); } x = ltr ? x + 2 : x - 2; if (FlamingoUtilities.getApplicationMenuButton(SwingUtilities .getWindowAncestor(ribbon)) != null) { x = ltr ? x + appMenuButtonSize : x - appMenuButtonSize; } // the help button if (helpButton != null) { Dimension preferred = helpButton.getPreferredSize(); if (ltr) { helpButton.setBounds(width - ins.right - preferred.width, y, preferred.width, preferred.height); } else { helpButton.setBounds(ins.left, y, preferred.width, preferred.height); } } // task buttons if (ltr) { int taskButtonsWidth = (helpButton != null) ? (helpButton .getX() - tabButtonGap - x) : (c.getWidth() - ins.right - x); taskToggleButtonsScrollablePanel.setBounds(x, y, taskButtonsWidth, taskToggleButtonHeight); } else { int taskButtonsWidth = (helpButton != null) ? (x - tabButtonGap - helpButton.getX() - helpButton.getWidth()) : (x - ins.left); taskToggleButtonsScrollablePanel.setBounds( x - taskButtonsWidth, y, taskButtonsWidth, taskToggleButtonHeight); } TaskToggleButtonsHostPanel taskToggleButtonsHostPanel = taskToggleButtonsScrollablePanel .getView(); int taskToggleButtonsHostPanelMinWidth = taskToggleButtonsHostPanel .getMinimumSize().width; taskToggleButtonsHostPanel.setPreferredSize(new Dimension( taskToggleButtonsHostPanelMinWidth, taskToggleButtonsScrollablePanel.getBounds().height)); taskToggleButtonsScrollablePanel.doLayout(); y += taskToggleButtonHeight; int extraHeight = taskToggleButtonHeight; if (!isUsingTitlePane) extraHeight += taskbarHeight; if (bandScrollablePanel.getParent() == ribbon) { if (!ribbon.isMinimized() && (ribbon.getTaskCount() > 0)) { // y += ins.top; Insets bandInsets = (ribbon.getSelectedTask() .getBandCount() == 0) ? new Insets(0, 0, 0, 0) : ribbon.getSelectedTask().getBand(0).getInsets(); bandScrollablePanel.setBounds(1 + ins.left, y + bandInsets.top, c.getWidth() - 2 * ins.left - 2 * ins.right - 1, c.getHeight() - extraHeight - ins.top - ins.bottom - bandInsets.top - bandInsets.bottom); // System.out.println("Scrollable : " // + bandScrollablePanel.getBounds()); BandHostPanel bandHostPanel = bandScrollablePanel.getView(); int bandHostPanelMinWidth = bandHostPanel.getMinimumSize().width; bandHostPanel.setPreferredSize(new Dimension( bandHostPanelMinWidth, bandScrollablePanel .getBounds().height)); bandScrollablePanel.doLayout(); bandHostPanel.doLayout(); } else { bandScrollablePanel.setBounds(0, 0, 0, 0); } } } } /** * Layout for the task bar. * * @author Kirill Grouchnikov */ private class TaskbarLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { Insets ins = c.getInsets(); int pw = 0; int gap = getBandGap(); for (Component regComp : ribbon.getTaskbarComponents()) { pw += regComp.getPreferredSize().width; pw += gap; } return new Dimension(pw + ins.left + ins.right, getTaskbarHeight() + ins.top + ins.bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { return this.preferredLayoutSize(c); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { Insets ins = c.getInsets(); int gap = getBandGap(); boolean ltr = c.getComponentOrientation().isLeftToRight(); if (ltr) { int x = ins.left + 1; if (applicationMenuButton.isVisible()) { x += (applicationMenuButton.getX() + applicationMenuButton .getWidth()); } for (Component regComp : ribbon.getTaskbarComponents()) { int pw = regComp.getPreferredSize().width; regComp.setBounds(x, ins.top + 1, pw, c.getHeight() - ins.top - ins.bottom - 2); x += (pw + gap); } } else { int x = c.getWidth() - ins.right - 1; if (applicationMenuButton.isVisible()) { x = applicationMenuButton.getX() - 1; } for (Component regComp : ribbon.getTaskbarComponents()) { int pw = regComp.getPreferredSize().width; regComp.setBounds(x - pw, ins.top + 1, pw, c.getHeight() - ins.top - ins.bottom - 2); x -= (pw + gap); } } } } /** * The taskbar panel that holds the {@link JRibbon#getTaskbarComponents()}. * * @author Kirill Grouchnikov */ private class TaskbarPanel extends JPanel { /** * Creates the new taskbar panel. */ public TaskbarPanel() { super(); this.setOpaque(false); this.setBorder(new EmptyBorder(1, 0, 1, 0)); } /* * (non-Javadoc) * * @see javax.swing.JComponent#paintComponent(java.awt.Graphics) */ @Override protected void paintComponent(Graphics g) { Shape contour = getOutline(this); Graphics2D g2d = (Graphics2D) g.create(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); RenderingUtils.installDesktopHints(g2d); if (contour != null) { g2d.setComposite(AlphaComposite.SrcOver.derive(0.6f)); g2d.setColor(FlamingoUtilities.getColor(Color.lightGray .brighter(), "Panel.background")); g2d.fill(contour); g2d.setColor(FlamingoUtilities.getBorderColor().darker()); g2d.draw(contour); } boolean ltr = getComponentOrientation().isLeftToRight(); int maxX = 0; int minX = getWidth(); if (this.getComponentCount() == 0) { maxX = 1; minX = getWidth() - 1; if (applicationMenuButton.isVisible()) { maxX += applicationMenuButton.getX() + applicationMenuButton.getWidth(); minX = applicationMenuButton.getX() - 1; } } else { for (int i = 0; i < this.getComponentCount(); i++) { Component taskBarComp = this.getComponent(i); maxX = Math.max(maxX, taskBarComp.getX() + taskBarComp.getWidth()); minX = Math.min(minX, taskBarComp.getX()); } } int height = getHeight(); if (ltr) { g2d.drawLine(maxX, height - 1, getWidth(), height - 1); } else { g2d.drawLine(0, height - 1, minX, height - 1); } int contourMaxX = (contour != null) ? (int) contour.getBounds2D() .getMaxX() + 6 : 6; int contourMinX = (contour != null) ? (int) contour.getBounds2D() .getMinX() - 6 : 6; // contextual task group headers if (!isShowingScrollsForTaskToggleButtons()) { g2d.setComposite(AlphaComposite.SrcOver); // the taskbar panel is not at the zero X coordinate of the // ribbon g2d.translate(-this.getBounds().x, 0); for (int i = 0; i < ribbon.getContextualTaskGroupCount(); i++) { RibbonContextualTaskGroup taskGroup = ribbon .getContextualTaskGroup(i); if (!ribbon.isVisible(taskGroup)) continue; Rectangle taskGroupBounds = getContextualTaskGroupBounds(taskGroup); Color hueColor = taskGroup.getHueColor(); Paint paint = new GradientPaint( 0, 0, FlamingoUtilities.getAlphaColor(hueColor, 0), 0, height, FlamingoUtilities .getAlphaColor( hueColor, (int) (255 * RibbonContextualTaskGroup.HUE_ALPHA))); // translucent gradient paint g2d.setPaint(paint); int startX = ltr ? taskGroupBounds.x : Math.min( contourMinX, taskGroupBounds.x); int width = ltr ? taskGroupBounds.x + taskGroupBounds.width - startX : Math.min(taskGroupBounds.x + taskGroupBounds.width, contourMinX) - startX; if (width > 0) { g2d.fillRect(startX, 0, width, height); // and a solid line at the bottom g2d.setColor(hueColor); g2d.drawLine(startX + 1, height - 1, startX + width, height - 1); // task group title g2d.setColor(FlamingoUtilities.getColor(Color.black, "Button.foreground")); FontMetrics fm = this.getFontMetrics(ribbon.getFont()); int yOffset = (height + fm.getHeight()) / 2 - fm.getDescent(); int availableTextWidth = width - 10; String titleToShow = taskGroup.getTitle(); if (fm.stringWidth(titleToShow) > availableTextWidth) { while (true) { if (titleToShow.length() == 0) break; if (fm.stringWidth(titleToShow + "...") <= availableTextWidth) break; titleToShow = titleToShow.substring(0, titleToShow.length() - 1); } titleToShow += "..."; } if (ltr) { BasicGraphicsUtils.drawString(g2d, titleToShow, -1, startX + 5, yOffset); } else { BasicGraphicsUtils.drawString(g2d, titleToShow, -1, startX + width - 5 - fm.stringWidth(titleToShow), yOffset); } // separator lines Color color = FlamingoUtilities.getBorderColor(); g2d.setPaint(new GradientPaint(0, 0, FlamingoUtilities .getAlphaColor(color, 0), 0, height, color)); // left line g2d.drawLine(startX, 0, startX, height); // right line g2d.drawLine(startX + width, 0, startX + width, height); } } } g2d.dispose(); } /** * Returns the outline of this taskbar panel. * * @param insets * Insets. * @return The outline of this taskbar panel. */ protected Shape getOutline(TaskbarPanel taskbarPanel) { double height = this.getHeight() - 1; boolean ltr = taskbarPanel.getComponentOrientation() .isLeftToRight(); if (this.getComponentCount() == 0) { if (applicationMenuButton.isVisible()) { // no taskbar components if (ltr) { int x = 1; if (applicationMenuButton.isVisible()) { x += applicationMenuButton.getX() + applicationMenuButton.getWidth(); } return new Arc2D.Double(x - 1 - 2 * height, 0, 2 * height, 2 * height, 0, 90, Arc2D.OPEN); } else { int x = taskbarPanel.getWidth() - 1; if (applicationMenuButton.isVisible()) { x = applicationMenuButton.getX() - 1; } return new Arc2D.Double(x + 1, 0, 2 * height, 2 * height, 90, 90, Arc2D.OPEN); } } else { return null; } } else { int minX = this.getWidth(); int maxX = 0; for (int i = 0; i < this.getComponentCount(); i++) { Component taskBarComp = this.getComponent(i); minX = Math.min(minX, taskBarComp.getX()); maxX = Math.max(maxX, taskBarComp.getX() + taskBarComp.getWidth()); } float radius = (float) height / 2.0f; GeneralPath outline = new GeneralPath(); if (ltr) { // top left corner if (applicationMenuButton.isVisible()) { outline.moveTo(minX + 5 - 2 * radius, 0); } else { outline.moveTo(minX - 1, 0); } // top right corner outline.lineTo(maxX, 0); // right arc outline.append(new Arc2D.Double(maxX - radius, 0, height, height, 90, -180, Arc2D.OPEN), true); // bottom left corner outline.lineTo(minX - 1, height); if (applicationMenuButton.isVisible()) { // left arc outline.append(new Arc2D.Double(minX - 1 - 2 * height, 0, 2 * height, 2 * height, 0, 90, Arc2D.OPEN), true); } else { outline.lineTo(minX - 1, 0); } } else { // top right corner if (applicationMenuButton.isVisible()) { outline.moveTo(maxX - 5 + 2 * radius, 0); } else { outline.moveTo(maxX - 1, 0); } // top left corner outline.lineTo(minX, 0); // left arc outline.append(new Arc2D.Double(minX - radius, 0, height, height, 90, 180, Arc2D.OPEN), true); // bottom right corner outline.lineTo(maxX - 1, height); if (applicationMenuButton.isVisible()) { outline.append(new Arc2D.Double(maxX - 1, 0, 2 * height, 2 * height, 180, -90, Arc2D.OPEN), true); } else { outline.lineTo(maxX + 1, 0); } } return outline; } } /* * (non-Javadoc) * * @see javax.swing.JComponent#getPreferredSize() */ @Override public Dimension getPreferredSize() { Dimension result = super.getPreferredSize(); return new Dimension(result.width + result.height / 2, result.height); } } protected static class BandHostPanel extends JPanel { } /** * Layout for the band host panel. * * @author Kirill Grouchnikov */ private class BandHostPanelLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { // Insets ins = c.getInsets(); int maxPrefBandHeight = 0; if (ribbon.getTaskCount() > 0) { RibbonTask selectedTask = ribbon.getSelectedTask(); for (AbstractRibbonBand ribbonBand : selectedTask.getBands()) { int bandPrefHeight = ribbonBand.getPreferredSize().height; Insets bandInsets = ribbonBand.getInsets(); maxPrefBandHeight = Math .max(maxPrefBandHeight, bandPrefHeight + bandInsets.top + bandInsets.bottom); } } return new Dimension(c.getWidth(), maxPrefBandHeight); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { // go over all ribbon bands and sum the width // of ribbon buttons (of collapsed state) // Insets ins = c.getInsets(); int width = 0; int maxMinBandHeight = 0; int gap = getBandGap(); // minimum is when all the tasks are collapsed RibbonTask selectedTask = ribbon.getSelectedTask(); // System.out.println(selectedTask.getTitle() + " min width"); for (AbstractRibbonBand ribbonBand : selectedTask.getBands()) { int bandPrefHeight = ribbonBand.getMinimumSize().height; Insets bandInsets = ribbonBand.getInsets(); RibbonBandUI bandUI = ribbonBand.getUI(); int preferredCollapsedWidth = bandUI .getPreferredCollapsedWidth() + bandInsets.left + bandInsets.right; width += preferredCollapsedWidth; // System.out.println("\t" + ribbonBand.getTitle() + ":" + // preferredCollapsedWidth); maxMinBandHeight = Math.max(maxMinBandHeight, bandPrefHeight // + bandInsets.top + bandInsets.bottom ); } // add inter-band gaps width += gap * (selectedTask.getBandCount() + 1); // System.out.println("\t" + gap + "*" + // (selectedTask.getBandCount() + 1)); // System.out.println(selectedTask.getTitle() + " min width:" + // width); // System.out.println("Returning min height of " + // maxMinBandHeight); return new Dimension(width, maxMinBandHeight); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { // System.err.println("Layout of band host panel " + c.getWidth() + // ":" + c.getHeight()); int bandGap = getBandGap(); // the top row - task bar components int x = 0; int y = 0; RibbonTask selectedTask = ribbon.getSelectedTask(); if (selectedTask == null) return; // check that the resize policies are still consistent for (AbstractRibbonBand band : selectedTask.getBands()) { FlamingoUtilities.checkResizePoliciesConsistency(band); } // start with the most "permissive" resize policy for each band for (AbstractRibbonBand band : selectedTask.getBands()) { List policies = band .getResizePolicies(); RibbonBandResizePolicy last = policies.get(0); band.setCurrentResizePolicy(last); } int availableBandHeight = c.getHeight(); int availableWidth = c.getWidth(); if (selectedTask.getBandCount() > 0) { RibbonBandResizeSequencingPolicy resizeSequencingPolicy = selectedTask .getResizeSequencingPolicy(); resizeSequencingPolicy.reset(); AbstractRibbonBand currToTakeFrom = resizeSequencingPolicy .next(); while (true) { // check whether all bands have the current resize // policy as their last (most restrictive) registered policy boolean noMore = true; for (AbstractRibbonBand band : selectedTask.getBands()) { RibbonBandResizePolicy currentResizePolicy = band .getCurrentResizePolicy(); List resizePolicies = band .getResizePolicies(); if (currentResizePolicy != resizePolicies .get(resizePolicies.size() - 1)) { noMore = false; break; } } if (noMore) break; // get the current preferred width of the bands int totalWidth = 0; // System.out.println("Iteration"); for (AbstractRibbonBand ribbonBand : selectedTask .getBands()) { RibbonBandResizePolicy currentResizePolicy = ribbonBand .getCurrentResizePolicy(); Insets ribbonBandInsets = ribbonBand.getInsets(); AbstractBandControlPanel controlPanel = ribbonBand .getControlPanel(); if (controlPanel == null) { controlPanel = ribbonBand.getPopupRibbonBand() .getControlPanel(); } Insets controlPanelInsets = controlPanel.getInsets(); int controlPanelGap = controlPanel.getUI() .getLayoutGap(); int ribbonBandHeight = availableBandHeight - ribbonBandInsets.top - ribbonBandInsets.bottom; int availableHeight = ribbonBandHeight - ribbonBand.getUI().getBandTitleHeight(); if (controlPanel != null) { availableHeight = availableHeight - controlPanelInsets.top - controlPanelInsets.bottom; } int preferredWidth = currentResizePolicy .getPreferredWidth(availableHeight, controlPanelGap) + ribbonBandInsets.left + ribbonBandInsets.right; totalWidth += preferredWidth + bandGap; // System.out.println("\t" // + ribbonBand.getTitle() // + ":" // + currentResizePolicy.getClass() // .getSimpleName() + ":" + preferredWidth // + " under " + availableHeight + " with " // + controlPanel.getComponentCount() // + " children"); } // System.out.println("\t:Total:" + totalWidth + "(" // + availableWidth + ")"); // System.out.println("\n"); if (totalWidth < availableWidth) break; // try to take from the currently rotating band List policies = currToTakeFrom .getResizePolicies(); int currPolicyIndex = policies.indexOf(currToTakeFrom .getCurrentResizePolicy()); if (currPolicyIndex == (policies.size() - 1)) { // nothing to take } else { currToTakeFrom.setCurrentResizePolicy(policies .get(currPolicyIndex + 1)); } currToTakeFrom = resizeSequencingPolicy.next(); } } boolean ltr = c.getComponentOrientation().isLeftToRight(); x = ltr ? 1 : c.getWidth() - 1; // System.out.println("Will get [" + availableWidth + "]:"); for (AbstractRibbonBand ribbonBand : selectedTask.getBands()) { Insets ribbonBandInsets = ribbonBand.getInsets(); RibbonBandResizePolicy currentResizePolicy = ribbonBand .getCurrentResizePolicy(); AbstractBandControlPanel controlPanel = ribbonBand .getControlPanel(); if (controlPanel == null) { controlPanel = ribbonBand.getPopupRibbonBand() .getControlPanel(); } Insets controlPanelInsets = controlPanel.getInsets(); int controlPanelGap = controlPanel.getUI().getLayoutGap(); int ribbonBandHeight = availableBandHeight; // - ribbonBandInsets.top - ribbonBandInsets.bottom; int availableHeight = ribbonBandHeight - ribbonBandInsets.top - ribbonBandInsets.bottom - ribbonBand.getUI().getBandTitleHeight(); if (controlPanelInsets != null) { availableHeight = availableHeight - controlPanelInsets.top - controlPanelInsets.bottom; } int requiredBandWidth = currentResizePolicy.getPreferredWidth( availableHeight, controlPanelGap) + ribbonBandInsets.left + ribbonBandInsets.right; if (ltr) { ribbonBand.setBounds(x, y, requiredBandWidth, ribbonBandHeight); } else { ribbonBand.setBounds(x - requiredBandWidth, y, requiredBandWidth, ribbonBandHeight); } // System.out.println("\t" + ribbonBand.getTitle() + ":" // + currentResizePolicy.getClass().getSimpleName() + ":" // + requiredBandWidth + " under " + ribbonBandHeight); if (ribbonBand.getHeight() > 0) { ribbonBand.doLayout(); } if (ltr) { x += (requiredBandWidth + bandGap); } else { x -= (requiredBandWidth + bandGap); } } // System.out.println(); } } protected class TaskToggleButtonsHostPanel extends JPanel { public static final String IS_SQUISHED = "flamingo.internal.ribbon.taskToggleButtonsHostPanel.isSquished"; @Override protected void paintComponent(Graphics g) { super.paintComponent(g); this.paintContextualTaskGroupsOutlines(g); if (Boolean.TRUE.equals(this.getClientProperty(IS_SQUISHED))) { this.paintTaskOutlines(g); } } protected void paintTaskOutlines(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); Color color = FlamingoUtilities.getBorderColor(); Paint paint = new GradientPaint(0, 0, FlamingoUtilities .getAlphaColor(color, 0), 0, getHeight(), color); g2d.setPaint(paint); Set tasksWithTrailingSeparators = new HashSet(); // add all regular tasks except the last for (int i = 0; i < ribbon.getTaskCount() - 1; i++) { RibbonTask task = ribbon.getTask(i); tasksWithTrailingSeparators.add(task); // System.out.println("Added " + task.getTitle()); } // add all tasks of visible contextual groups except last task in // each group for (int i = 0; i < ribbon.getContextualTaskGroupCount(); i++) { RibbonContextualTaskGroup group = ribbon .getContextualTaskGroup(i); if (ribbon.isVisible(group)) { for (int j = 0; j < group.getTaskCount() - 1; j++) { RibbonTask task = group.getTask(j); tasksWithTrailingSeparators.add(task); // System.out.println("Added " + task.getTitle()); } } } for (RibbonTask taskWithTrailingSeparator : tasksWithTrailingSeparators) { JRibbonTaskToggleButton taskToggleButton = taskToggleButtons .get(taskWithTrailingSeparator); Rectangle bounds = taskToggleButton.getBounds(); int x = bounds.x + bounds.width + getTabButtonGap() / 2 - 1; g2d.drawLine(x, 0, x, getHeight()); // System.out.println(taskWithTrailingSeparator.getTitle() + ":" // + x); } g2d.dispose(); } /** * Paints the outline of the contextual task groups. * * @param g * Graphics context. */ protected void paintContextualTaskGroupsOutlines(Graphics g) { for (int i = 0; i < ribbon.getContextualTaskGroupCount(); i++) { RibbonContextualTaskGroup group = ribbon .getContextualTaskGroup(i); if (!ribbon.isVisible(group)) continue; // go over all the tasks in this group and compute the union // of bounds of the matching tab buttons Rectangle rect = getContextualTaskGroupBounds(group); rect.setLocation(SwingUtilities.convertPoint(ribbon, rect .getLocation(), taskToggleButtonsScrollablePanel .getView())); this.paintContextualTaskGroupOutlines(g, group, rect); } } /** * Paints the outline of the specified contextual task group. * * @param g * Graphics context. * @param group * Contextual task group. * @param groupBounds * Contextual task group bounds. */ protected void paintContextualTaskGroupOutlines(Graphics g, RibbonContextualTaskGroup group, Rectangle groupBounds) { Graphics2D g2d = (Graphics2D) g.create(); Color color = FlamingoUtilities.getBorderColor(); Paint paint = new GradientPaint(0, groupBounds.y, color, 0, groupBounds.y + groupBounds.height, FlamingoUtilities .getAlphaColor(color, 0)); g2d.setPaint(paint); // left line int x = groupBounds.x; g2d.drawLine(x, groupBounds.y, x, groupBounds.y + groupBounds.height); // right line x = groupBounds.x + groupBounds.width; g2d.drawLine(x, groupBounds.y, x, groupBounds.y + groupBounds.height); g2d.dispose(); } // @Override // protected void paintComponent(Graphics g) { // //g.setColor(new Color(255, 200, 200)); // //g.fillRect(0, 0, getWidth(), getHeight()); // // g.setColor(Color.blue.darker()); // // g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); // //System.err.println(System.currentTimeMillis() + ": tt-repaint"); // } // // @Override // protected void paintBorder(Graphics g) { // } // // @Override // public void setBounds(int x, int y, int width, int height) { // System.out.println("Host : " + x + ":" + y + ":" + width + ":" // + height); // super.setBounds(x, y, width, height); // } } /** * Layout for the band host panel. * * @author Kirill Grouchnikov */ private class TaskToggleButtonsHostPanelLayout implements LayoutManager { /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { int tabButtonGap = getTabButtonGap(); int taskToggleButtonHeight = getTaskToggleButtonHeight(); int totalTaskButtonsWidth = 0; List visibleTasks = getCurrentlyShownRibbonTasks(); for (RibbonTask task : visibleTasks) { JRibbonTaskToggleButton tabButton = taskToggleButtons.get(task); int pw = tabButton.getPreferredSize().width; totalTaskButtonsWidth += (pw + tabButtonGap); } return new Dimension(totalTaskButtonsWidth, taskToggleButtonHeight); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { int tabButtonGap = getTabButtonGap(); int taskToggleButtonHeight = getTaskToggleButtonHeight(); int totalTaskButtonsWidth = 0; List visibleTasks = getCurrentlyShownRibbonTasks(); for (RibbonTask task : visibleTasks) { JRibbonTaskToggleButton tabButton = taskToggleButtons.get(task); int pw = tabButton.getMinimumSize().width; totalTaskButtonsWidth += (pw + tabButtonGap); } return new Dimension(totalTaskButtonsWidth, taskToggleButtonHeight); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { int y = 0; int tabButtonGap = getTabButtonGap(); int taskToggleButtonHeight = getTaskToggleButtonHeight(); int totalPrefWidth = 0; int totalMinWidth = 0; List visibleTasks = getCurrentlyShownRibbonTasks(); Map diffMap = new HashMap(); int totalDiff = 0; for (RibbonTask task : visibleTasks) { JRibbonTaskToggleButton tabButton = taskToggleButtons.get(task); int pw = tabButton.getPreferredSize().width; int mw = tabButton.getMinimumSize().width; diffMap.put(tabButton, pw - mw); totalDiff += (pw - mw); totalPrefWidth += pw; totalMinWidth += mw; } totalPrefWidth += tabButtonGap * visibleTasks.size(); totalMinWidth += tabButtonGap * visibleTasks.size(); boolean ltr = c.getComponentOrientation().isLeftToRight(); // do we have enough width? if (totalPrefWidth <= c.getWidth()) { // compute bounds for the tab buttons int x = ltr ? 0 : c.getWidth(); for (RibbonTask task : visibleTasks) { JRibbonTaskToggleButton tabButton = taskToggleButtons .get(task); int pw = tabButton.getPreferredSize().width; if (ltr) { tabButton.setBounds(x, y + 1, pw, taskToggleButtonHeight - 1); x += (pw + tabButtonGap); } else { tabButton.setBounds(x - pw, y + 1, pw, taskToggleButtonHeight - 1); x -= (pw + tabButtonGap); } tabButton.setActionRichTooltip(null); } ((JComponent) c).putClientProperty( TaskToggleButtonsHostPanel.IS_SQUISHED, null); } else { if (totalMinWidth > c.getWidth()) { throw new IllegalStateException( "Available width not enough to host minimized task tab buttons"); } int x = ltr ? 0 : c.getWidth(); // how much do we need to take from each toggle button? int toDistribute = totalPrefWidth - c.getWidth() + 2; for (RibbonTask task : visibleTasks) { JRibbonTaskToggleButton tabButton = taskToggleButtons .get(task); int pw = tabButton.getPreferredSize().width; int delta = (toDistribute * diffMap.get(tabButton) / totalDiff); int finalWidth = pw - delta; if (ltr) { tabButton.setBounds(x, y + 1, finalWidth, taskToggleButtonHeight - 1); x += (finalWidth + tabButtonGap); } else { tabButton.setBounds(x - finalWidth, y + 1, finalWidth, taskToggleButtonHeight - 1); x -= (finalWidth + tabButtonGap); } // show the tooltip with the full title RichTooltip tooltip = new RichTooltip(); tooltip.setTitle(task.getTitle()); tabButton.setActionRichTooltip(tooltip); } ((JComponent) c).putClientProperty( TaskToggleButtonsHostPanel.IS_SQUISHED, Boolean.TRUE); } } } protected void syncRibbonState() { // remove all existing ribbon bands BandHostPanel bandHostPanel = this.bandScrollablePanel.getView(); bandHostPanel.removeAll(); // remove all the existing task toggle buttons TaskToggleButtonsHostPanel taskToggleButtonsHostPanel = this.taskToggleButtonsScrollablePanel .getView(); taskToggleButtonsHostPanel.removeAll(); // remove the help button if (this.helpButton != null) { this.ribbon.remove(this.helpButton); this.helpButton = null; } // go over all visible ribbon tasks and create a toggle button // for each one of them List visibleTasks = this.getCurrentlyShownRibbonTasks(); final RibbonTask selectedTask = this.ribbon.getSelectedTask(); for (final RibbonTask task : visibleTasks) { final JRibbonTaskToggleButton taskToggleButton = new JRibbonTaskToggleButton( task); taskToggleButton.setKeyTip(task.getKeyTip()); // wire listener to select the task when the button is // selected taskToggleButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { scrollAndRevealTaskToggleButton(taskToggleButton); ribbon.setSelectedTask(task); // System.out.println("Button click on " // + task.getTitle() + ", ribbon minimized? " // + ribbon.isMinimized()); if (ribbon.isMinimized()) { if (Boolean.TRUE.equals(ribbon .getClientProperty(JUST_MINIMIZED))) { ribbon.putClientProperty(JUST_MINIMIZED, null); return; } // special case - do we have this task currently // shown in a popup? List popups = PopupPanelManager .defaultManager().getShownPath(); if (popups.size() > 0) { for (PopupPanelManager.PopupInfo popup : popups) { if (popup.getPopupOriginator() == taskToggleButton) { // hide all popups and return (hides // the task popup and does not // show any additional popup). PopupPanelManager.defaultManager() .hidePopups(null); return; } } } PopupPanelManager.defaultManager().hidePopups( null); ribbon.remove(bandScrollablePanel); int prefHeight = bandScrollablePanel.getView() .getPreferredSize().height; Insets ins = ribbon.getInsets(); prefHeight += ins.top + ins.bottom; AbstractRibbonBand band = (ribbon .getSelectedTask().getBandCount() > 0) ? ribbon .getSelectedTask().getBand(0) : null; if (band != null) { Insets bandIns = band.getInsets(); prefHeight += bandIns.top + bandIns.bottom; } // System.out.println(prefHeight // + ":" // + bandScrollablePanel.getView() // .getComponentCount()); JPopupPanel popupPanel = new BandHostPopupPanel( bandScrollablePanel, new Dimension( ribbon.getWidth(), prefHeight)); int x = ribbon.getLocationOnScreen().x; int y = ribbon.getLocationOnScreen().y + ribbon.getHeight(); // make sure that the popup stays in // bounds Rectangle scrBounds = ribbon .getGraphicsConfiguration().getBounds(); int pw = popupPanel.getPreferredSize().width; if ((x + pw) > (scrBounds.x + scrBounds.width)) { x = scrBounds.x + scrBounds.width - pw; } int ph = popupPanel.getPreferredSize().height; if ((y + ph) > (scrBounds.y + scrBounds.height)) { y = scrBounds.y + scrBounds.height - ph; } // get the popup and show it popupPanel.setPreferredSize(new Dimension( ribbon.getWidth(), prefHeight)); Popup popup = PopupFactory.getSharedInstance() .getPopup(taskToggleButton, popupPanel, x, y); PopupPanelManager.PopupListener tracker = new PopupPanelManager.PopupListener() { @Override public void popupShown(PopupEvent event) { JComponent originator = event .getPopupOriginator(); if (originator instanceof JRibbonTaskToggleButton) { bandScrollablePanel.doLayout(); bandScrollablePanel.repaint(); } } @Override public void popupHidden(PopupEvent event) { JComponent originator = event .getPopupOriginator(); if (originator instanceof JRibbonTaskToggleButton) { ribbon.add(bandScrollablePanel); PopupPanelManager.defaultManager() .removePopupListener(this); ribbon.revalidate(); ribbon.doLayout(); ribbon.repaint(); } } }; PopupPanelManager.defaultManager() .addPopupListener(tracker); PopupPanelManager.defaultManager().addPopup( taskToggleButton, popup, popupPanel); } } }); } }); // wire listener to toggle ribbon minimization on double // mouse click taskToggleButton.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if ((ribbon.getSelectedTask() == task) && (e.getClickCount() == 2)) { boolean wasMinimized = ribbon.isMinimized(); ribbon.setMinimized(!wasMinimized); if (!wasMinimized) { // fix for issue 69 - mark the ribbon as // "just minimized" to prevent the action handler // of the toggle button to show the ribbon in // popup mode ribbon.putClientProperty(JUST_MINIMIZED, Boolean.TRUE); } } } }); // set the background hue color on the tab buttons // of tasks in contextual groups if (task.getContextualGroup() != null) { taskToggleButton.setContextualGroupHueColor(task .getContextualGroup().getHueColor()); } taskToggleButton.putClientProperty( BasicCommandButtonUI.DONT_DISPOSE_POPUPS, Boolean.TRUE); this.taskToggleButtonGroup.add(taskToggleButton); taskToggleButtonsHostPanel.add(taskToggleButton); this.taskToggleButtons.put(task, taskToggleButton); } JRibbonTaskToggleButton toSelect = this.taskToggleButtons .get(selectedTask); if (toSelect != null) { toSelect.getActionModel().setSelected(true); } for (int i = 0; i < this.ribbon.getTaskCount(); i++) { RibbonTask task = this.ribbon.getTask(i); for (AbstractRibbonBand band : task.getBands()) { bandHostPanel.add(band); band.setVisible(selectedTask == task); } } for (int i = 0; i < this.ribbon.getContextualTaskGroupCount(); i++) { RibbonContextualTaskGroup taskGroup = this.ribbon .getContextualTaskGroup(i); for (int j = 0; j < taskGroup.getTaskCount(); j++) { RibbonTask task = taskGroup.getTask(j); for (AbstractRibbonBand band : task.getBands()) { bandHostPanel.add(band); band.setVisible(selectedTask == task); } } } ActionListener helpListener = this.ribbon.getHelpActionListener(); if (helpListener != null) { this.helpButton = new JCommandButton("", this.ribbon.getHelpIcon()); this.helpButton.setDisplayState(CommandButtonDisplayState.SMALL); this.helpButton.setCommandButtonKind(CommandButtonKind.ACTION_ONLY); this.helpButton.getActionModel().addActionListener(helpListener); this.ribbon.add(this.helpButton); } this.ribbon.revalidate(); this.ribbon.repaint(); } /** * Returns the list of currently shown ribbon tasks. This method is for * internal use only. * * @return The list of currently shown ribbon tasks. */ protected List getCurrentlyShownRibbonTasks() { List result = new ArrayList(); // add all regular tasks for (int i = 0; i < this.ribbon.getTaskCount(); i++) { RibbonTask task = this.ribbon.getTask(i); result.add(task); } // add all tasks of visible contextual groups for (int i = 0; i < this.ribbon.getContextualTaskGroupCount(); i++) { RibbonContextualTaskGroup group = this.ribbon .getContextualTaskGroup(i); if (this.ribbon.isVisible(group)) { for (int j = 0; j < group.getTaskCount(); j++) { RibbonTask task = group.getTask(j); result.add(task); } } } return result; } protected boolean isUsingTitlePane() { return Boolean.TRUE.equals(ribbon .getClientProperty(IS_USING_TITLE_PANE)); } protected void syncApplicationMenuTips() { this.applicationMenuButton.setPopupRichTooltip(this.ribbon .getApplicationMenuRichTooltip()); this.applicationMenuButton.setPopupKeyTip(this.ribbon .getApplicationMenuKeyTip()); } @Override public boolean isShowingScrollsForTaskToggleButtons() { return this.taskToggleButtonsScrollablePanel.isShowingScrollButtons(); } @Override public boolean isShowingScrollsForBands() { return this.bandScrollablePanel.isShowingScrollButtons(); } public Map getTaskToggleButtons() { return Collections.unmodifiableMap(taskToggleButtons); } protected static class BandHostPopupPanel extends JPopupPanel { /** * The main component of this popup panel. Can be * null. */ // protected Component component; public BandHostPopupPanel(Component component, Dimension originalSize) { // this.component = component; this.setLayout(new BorderLayout()); this.add(component, BorderLayout.CENTER); // System.out.println("Popup dim is " + originalSize); this.setPreferredSize(originalSize); this.setSize(originalSize); } } @Override public void handleMouseWheelEvent(MouseWheelEvent e) { // no mouse wheel scrolling when the ribbon is minimized if (ribbon.isMinimized()) return; // get the visible tasks final List visibleTasks = getCurrentlyShownRibbonTasks(); if (visibleTasks.size() == 0) return; int delta = e.getWheelRotation(); if (delta == 0) return; // find the index of the currently selected task int currSelectedTaskIndex = visibleTasks.indexOf(ribbon .getSelectedTask()); // compute the next task if (!ribbon.getComponentOrientation().isLeftToRight()) delta = -delta; int newSelectedTaskIndex = currSelectedTaskIndex + ((delta > 0) ? 1 : -1); if (newSelectedTaskIndex < 0) return; if (newSelectedTaskIndex >= visibleTasks.size()) return; final int indexToSet = newSelectedTaskIndex; SwingUtilities.invokeLater(new Runnable() { public void run() { ribbon .setCursor(Cursor .getPredefinedCursor(Cursor.WAIT_CURSOR)); ribbon.setSelectedTask(visibleTasks.get(indexToSet)); ribbon.setCursor(Cursor .getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } }); } protected void scrollAndRevealTaskToggleButton( final JRibbonTaskToggleButton taskToggleButton) { // scroll the viewport of the scrollable panel // so that the button is fully viewed. Point loc = SwingUtilities.convertPoint(taskToggleButton.getParent(), taskToggleButton.getLocation(), taskToggleButtonsScrollablePanel.getView()); taskToggleButtonsScrollablePanel.scrollToIfNecessary(loc.x, taskToggleButton.getWidth()); } } src/org/pushingpixels/flamingo/internal/ui/ribbon/JBandControlPanel.java0000644000175000017500000004043011413206054025541 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.util.*; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.UIResource; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.ribbon.*; /** * Control panel of a single {@link JRibbonBand}. This class is for internal use * only and should not be directly used by the applications. * * @author Kirill Grouchnikov */ public class JBandControlPanel extends AbstractBandControlPanel implements UIResource { public static class ControlPanelGroup { public String groupTitle; /** * Indication whether this control panel has galleries. */ private boolean hasGalleries; /** * Number of galleries in this control panel. */ private int galleryCount; /** * Mapping from priority to galleries. */ private Map> ribbonGalleries; /** * Mapping from gallery to priority. */ private Map ribbonGalleriesPriorities; /** * Mapping from priority to ribbon buttons. */ private Map> ribbonButtons; /** * Mapping from ribbon button to priority. */ private Map ribbonButtonsPriorities; private List coreComps; private Map coreCompRowSpans; public ControlPanelGroup(String groupTitle) { this.groupTitle = groupTitle; this.ribbonButtons = new HashMap>(); this.ribbonButtonsPriorities = new HashMap(); this.ribbonGalleries = new HashMap>(); this.ribbonGalleriesPriorities = new HashMap(); this.hasGalleries = false; this.galleryCount = 0; this.coreComps = new ArrayList(); this.coreCompRowSpans = new HashMap(); } public String getGroupTitle() { return groupTitle; } public void setGroupTitle(String newTitle) { if ((this.groupTitle == null) && (newTitle != null)) { throw new IllegalArgumentException( "Cannot set a title for an unnamed group"); } if ((this.groupTitle != null) && (newTitle == null)) { throw new IllegalArgumentException( "Cannot remove a title from a named group"); } this.groupTitle = newTitle; } public boolean isCoreContent() { return !this.coreComps.isEmpty(); } /** * Adds a new ribbon button to this control panel. * * @param ribbonButton * Ribbon button to add. * @param priority * Ribbon button priority. */ public synchronized void addCommandButton( AbstractCommandButton ribbonButton, RibbonElementPriority priority) { if (this.groupTitle != null) { throw new UnsupportedOperationException( "Can't add command buttons to ribbon band group with non-null title"); } if (this.isCoreContent()) { throw new UnsupportedOperationException( "Ribbon band groups do not support mixing JRibbonComponents and custom Flamingo components"); } if (!this.ribbonButtons.containsKey(priority)) { this.ribbonButtons.put(priority, new LinkedList()); } List al = this.ribbonButtons.get(priority); al.add(ribbonButton); this.ribbonButtonsPriorities.put(ribbonButton, priority); ribbonButton.setDisplayState(CommandButtonDisplayState.BIG); } /** * Adds a new in-ribbon gallery to this control panel. * * @param ribbonGallery * Ribbon gallery to add. * @param priority * Ribbon gallery priority. */ public synchronized void addRibbonGallery(JRibbonGallery ribbonGallery, RibbonElementPriority priority) { if (this.groupTitle != null) { throw new UnsupportedOperationException( "Can't add galleries to ribbon band group with non-null title"); } if (this.isCoreContent()) { throw new UnsupportedOperationException( "Ribbon band groups do not support mixing JRibbonComponents and custom Flamingo components"); } // check the name if (!this.ribbonGalleries.containsKey(priority)) { this.ribbonGalleries.put(priority, new LinkedList()); } List al = this.ribbonGalleries.get(priority); al.add(ribbonGallery); this.ribbonGalleriesPriorities.put(ribbonGallery, priority); ribbonGallery.setDisplayPriority(RibbonElementPriority.TOP); this.hasGalleries = true; this.galleryCount++; } /** * Sets new priority of a ribbon button in this control * panel. * * @param ribbonButton * Gallery button. * @param newPriority * New priority for the specified ribbon button. */ public synchronized void setPriority(JCommandButton ribbonButton, RibbonElementPriority newPriority) { RibbonElementPriority oldPriority = this.ribbonButtonsPriorities .get(ribbonButton); if (newPriority == oldPriority) return; this.ribbonButtons.get(oldPriority).remove(ribbonButton); if (!this.ribbonButtons.containsKey(newPriority)) { this.ribbonButtons.put(newPriority, new ArrayList()); } this.ribbonButtons.get(newPriority).add(ribbonButton); } /** * Sets new priority of an in-ribbon gallery in this * control panel. * * @param ribbonGallery * In-ribbon gallery. * @param newPriority * New priority for the specified in-ribbon gallery. */ public synchronized void setPriority(JRibbonGallery ribbonGallery, RibbonElementPriority newPriority) { RibbonElementPriority oldPriority = this.ribbonGalleriesPriorities .get(ribbonGallery); if (newPriority == oldPriority) return; this.ribbonGalleries.get(oldPriority).remove(ribbonGallery); if (!this.ribbonGalleries.containsKey(newPriority)) { this.ribbonGalleries.put(newPriority, new ArrayList()); } this.ribbonGalleries.get(newPriority).add(ribbonGallery); } public void addRibbonComponent(JRibbonComponent comp, int rowSpan) { if (!this.ribbonButtonsPriorities.isEmpty() || !this.ribbonGalleries.isEmpty()) { throw new UnsupportedOperationException( "Ribbon band groups do not support mixing JRibbonComponents and custom Flamingo components"); } comp.setOpaque(false); this.coreComps.add(comp); this.coreCompRowSpans.put(comp, rowSpan); } /** * Retrieves all ribbon buttons of specified priority from * this control panel. * * @param priority * Priority. * @return All ribbon buttons of specified priority from * this control panel. */ public List getRibbonButtons( RibbonElementPriority priority) { List result = this.ribbonButtons .get(priority); if (result == null) return EMPTY_GALLERY_BUTTONS_LIST; return result; } /** * Retrieves all in-ribbon galleries of specified priority from * this control panel. * * @param priority * Priority. * @return All in-ribbon galleries of specified priority from * this control panel. */ public List getRibbonGalleries( RibbonElementPriority priority) { List result = this.ribbonGalleries.get(priority); if (result == null) return EMPTY_RIBBON_GALLERIES_LIST; return result; } /** * Returns indication whether this control panel has * in-ribbon galleries. * * @return true if this control panel has * in-ribbon galleries, false otherwise. */ public boolean hasRibbonGalleries() { return this.hasGalleries; } /** * Returns the number of in-ribbon galleries in this * control panel. * * @return Number of in-ribbon galleries in this control * panel. */ public int getRibbonGalleriesCount() { return this.galleryCount; } public List getRibbonComps() { return this.coreComps; } public Map getRibbonCompsRowSpans() { return this.coreCompRowSpans; } } /** * Maps from gallery name to gallery. */ private Map galleryNameMap; private LinkedList controlPanelGroups; /** * Empty list of buttons. */ public static final List EMPTY_GALLERY_BUTTONS_LIST = new LinkedList(); /** * Empty list of galleries. */ public static final List EMPTY_RIBBON_GALLERIES_LIST = new LinkedList(); /** * The UI class ID string. */ public static final String uiClassID = "BandControlPanelUI"; /** * Creates a control panel for specified ribbon band. * * @param ribbonBand * Ribbon band. */ public JBandControlPanel() { super(); this.controlPanelGroups = new LinkedList(); this.galleryNameMap = new HashMap(); } /** * Sets the new UI delegate. * * @param ui * New UI delegate. */ public void setUI(BandControlPanelUI ui) { super.setUI(ui); } /* * (non-Javadoc) * * @see javax.swing.JPanel#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((BandControlPanelUI) UIManager.getUI(this)); } else { setUI(new BasicBandControlPanelUI()); } } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUI() */ @Override public BandControlPanelUI getUI() { return (BandControlPanelUI) ui; } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /** * Adds a new ribbon button to this control panel. * * @param ribbonButton * Ribbon button to add. * @param priority * Ribbon button priority. */ public synchronized void addCommandButton( AbstractCommandButton ribbonButton, RibbonElementPriority priority) { if (this.controlPanelGroups.size() == 0) this.startGroup(); this.controlPanelGroups.getLast().addCommandButton(ribbonButton, priority); super.add(ribbonButton); } /** * Adds a new in-ribbon gallery to this control panel. * * @param ribbonGallery * Ribbon gallery to add. * @param priority * Ribbon gallery priority. */ public synchronized void addRibbonGallery(JRibbonGallery ribbonGallery, RibbonElementPriority priority) { // check the name String galleryName = ribbonGallery.getName(); if ((galleryName == null) || (galleryName.isEmpty())) { throw new IllegalArgumentException( "Ribbon gallery name null or empty"); } if (this.galleryNameMap.containsKey(galleryName)) { throw new IllegalArgumentException( "Another riboon gallery with the same name already exists"); } if (this.controlPanelGroups.size() == 0) this.startGroup(); this.controlPanelGroups.getLast().addRibbonGallery(ribbonGallery, priority); this.galleryNameMap.put(galleryName, ribbonGallery); super.add(ribbonGallery); } /** * Sets new priority of a ribbon button in this control panel. * * @param ribbonButton * Gallery button. * @param newPriority * New priority for the specified ribbon button. */ public synchronized void setPriority(JCommandButton ribbonButton, RibbonElementPriority newPriority) { if (this.controlPanelGroups.size() == 0) this.startGroup(); this.controlPanelGroups.getLast() .setPriority(ribbonButton, newPriority); } /** * Sets new priority of an in-ribbon gallery in this control * panel. * * @param ribbonGallery * In-ribbon gallery. * @param newPriority * New priority for the specified in-ribbon gallery. */ public synchronized void setPriority(JRibbonGallery ribbonGallery, RibbonElementPriority newPriority) { if (this.controlPanelGroups.size() == 0) this.startGroup(); this.controlPanelGroups.getLast().setPriority(ribbonGallery, newPriority); } public void addRibbonComponent(JRibbonComponent comp) { this.addRibbonComponent(comp, 1); } public void addRibbonComponent(JRibbonComponent comp, int rowSpan) { if (this.controlPanelGroups.size() == 0) this.startGroup(); this.controlPanelGroups.getLast().addRibbonComponent(comp, rowSpan); super.add(comp); } public List getControlPanelGroups() { return Collections.unmodifiableList(this.controlPanelGroups); } public int getControlPanelGroupCount() { if (this.controlPanelGroups == null) return 1; return this.controlPanelGroups.size(); } public String getControlPanelGroupTitle(int controlPanelGroupIndex) { if (this.controlPanelGroups == null) return null; return this.controlPanelGroups.get(controlPanelGroupIndex).groupTitle; } public int startGroup() { return this.startGroup(null); } public int startGroup(String groupTitle) { ControlPanelGroup controlPanelGroup = new ControlPanelGroup(groupTitle); this.controlPanelGroups.addLast(controlPanelGroup); this.fireChanged(); return this.controlPanelGroups.size() - 1; } public void setGroupTitle(int groupIndex, String groupTitle) { this.controlPanelGroups.get(groupIndex).setGroupTitle(groupTitle); this.fireChanged(); } /** * Returns the ribbon gallery based on its name. * * @param galleryName * Ribbon gallery name. * @return Ribbon gallery. */ public JRibbonGallery getRibbonGallery(String galleryName) { return this.galleryNameMap.get(galleryName); } public void addChangeListener(ChangeListener l) { this.listenerList.add(ChangeListener.class, l); } public void removeChangeListener(ChangeListener l) { this.listenerList.remove(ChangeListener.class, l); } protected void fireChanged() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); ChangeEvent ce = new ChangeEvent(this); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { ((ChangeListener) listeners[i + 1]).stateChanged(ce); } } } public List getRibbonComponents(int groupIndex) { return Collections.unmodifiableList(this.controlPanelGroups.get( groupIndex).getRibbonComps()); } } src/org/pushingpixels/flamingo/internal/ui/ribbon/RibbonComponentUI.java0000644000175000017500000000424711415653300025606 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.Dimension; import java.awt.Point; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent; import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; /** * UI for extended components ({@link JRibbonComponent}). * * @author Kirill Grouchnikov */ public abstract class RibbonComponentUI extends ComponentUI { public abstract Point getKeyTipAnchorCenterPoint(); public abstract Dimension getPreferredSize(RibbonElementPriority priority); } src/org/pushingpixels/flamingo/internal/ui/ribbon/RibbonBandUI.java0000644000175000017500000000414011406771222024504 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; /** * UI for ribbon band ({@link JRibbonBand}). * * @author Kirill Grouchnikov */ public abstract class RibbonBandUI extends ComponentUI { public abstract int getPreferredCollapsedWidth(); public abstract int getBandTitleHeight(); public abstract void trackMouseCrossing(boolean isMouseIn); public abstract float getRolloverAmount(); } src/org/pushingpixels/flamingo/internal/ui/ribbon/JRibbonGallery.java0000644000175000017500000004324411412151074025115 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; import javax.swing.*; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; import org.pushingpixels.flamingo.api.ribbon.JRibbonBand.RibbonGalleryPopupCallback; /** * In-ribbon gallery. This class is for internal use only and should not be * directly used by the applications. * * @author Kirill Grouchnikov * @see JRibbonBand#addRibbonGallery(String, List, Map, int, int, * RibbonElementPriority) */ public class JRibbonGallery extends JComponent { /** * The buttons of this gallery. */ protected List buttons; /** * Button group for ensuring that only one button is selected. */ protected CommandToggleButtonGroup buttonSelectionGroup; /** * The current display priority of this in-ribbon gallery. */ protected RibbonElementPriority displayPriority; /** * Preferred widths for each possible display state (set in the user code * according to design preferences). */ protected Map preferredVisibleIconCount; /** * Gallery button groups. */ protected List>> buttonGroups; /** * Preferred maximum number of button columns for the popup panel. */ protected int preferredPopupMaxButtonColumns; /** * Preferred maximum number of visible button rows for the popup panel. */ protected int preferredPopupMaxVisibleButtonRows; /** * Indication whether the ribbon gallery is showing the popup panel. */ protected boolean isShowingPopupPanel; protected RibbonGalleryPopupCallback popupCallback; /** * The UI class ID string. */ public static final String uiClassID = "RibbonGalleryUI"; /** * Action listener wired to all the buttons in this gallery. If * {@link #toDismissOnButtonClick} is true, the listener * dismissed this gallery. */ protected ActionListener dismissActionListener; private String expandKeyTip; private CommandButtonDisplayState buttonDisplayState; /** * Creates new in-ribbon gallery. */ public JRibbonGallery() { this.buttons = new ArrayList(); this.buttonSelectionGroup = new CommandToggleButtonGroup(); this.buttonSelectionGroup .addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent evt) { if (CommandToggleButtonGroup.SELECTED_PROPERTY .equals(evt.getPropertyName())) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { firePropertyChange("selectedButton", evt .getOldValue(), evt.getNewValue()); } }); } } }); this.preferredVisibleIconCount = new HashMap(); // Initialize with some values. Application should provide real // widths using setPreferredWidth. for (RibbonElementPriority state : RibbonElementPriority.values()) this.preferredVisibleIconCount.put(state, 100); this.isShowingPopupPanel = false; this.buttonDisplayState = JRibbonBand.BIG_FIXED_LANDSCAPE; this.updateUI(); } /** * Sets the new UI delegate. * * @param ui * New UI delegate. */ public void setUI(RibbonGalleryUI ui) { super.setUI(ui); } /** * Resets the UI property to a value from the current look and feel. * * @see JComponent#updateUI */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((RibbonGalleryUI) UIManager.getUI(this)); } else { setUI(new BasicRibbonGalleryUI()); } // // if (this.popupPanel != null) // SwingUtilities.updateComponentTreeUI(this.popupPanel); } /** * Returns the UI object which implements the L&F for this component. * * @return a RibbonGalleryUI object * @see #setUI */ public RibbonGalleryUI getUI() { return (RibbonGalleryUI) ui; } /** * Returns the name of the UI class that implements the L&F for this * component. * * @return the string "RibbonGalleryUI" * @see JComponent#getUIClassID * @see UIDefaults#getUI */ @Override public String getUIClassID() { return uiClassID; } /** * Adds new gallery button to this in-ribbon gallery. * * @param buttonGroup * Button group. * @param button * Gallery button to add. */ private void addGalleryButton( StringValuePair> buttonGroup, JCommandToggleButton button) { String buttonGroupName = buttonGroup.getKey(); // find the index to add int indexToAdd = 0; for (int i = 0; i < this.buttonGroups.size(); i++) { StringValuePair> buttonGroupPair = this.buttonGroups .get(i); String currGroupName = buttonGroupPair.getKey(); indexToAdd += buttonGroupPair.getValue().size(); if ((currGroupName == null) && (buttonGroupName == null)) { break; } if (currGroupName.compareTo(buttonGroupName) == 0) { break; } } // System.out.println("Added " + button.getText() + " at " + // indexToAdd); this.buttons.add(indexToAdd, button); this.buttonSelectionGroup.add(button); buttonGroup.getValue().add(button); button.setDisplayState(this.buttonDisplayState); button.addActionListener(this.dismissActionListener); super.add(button); } /** * Removes an existing gallery button from this in-ribbon * gallery. * * @param button * Gallery button to remove. */ private void removeGalleryButton(JCommandToggleButton button) { this.buttons.remove(button); this.buttonSelectionGroup.remove(button); button.removeActionListener(this.dismissActionListener); super.remove(button); } /** * Set preferred width of this in-ribbon gallery for the * specified display state. * * @param state * Display state. * @param visibleButtonCount * Preferred width for the specified state. */ public void setPreferredVisibleButtonCount(RibbonElementPriority state, int visibleButtonCount) { this.preferredVisibleIconCount.put(state, visibleButtonCount); } /** * Returns the preferred width of this in-ribbon gallery for * the specified display state. * * @param state * Display state. * @param availableHeight * Available height in pixels. * @return The preferred width of this in-ribbon gallery for * the specified display state. */ public int getPreferredWidth(RibbonElementPriority state, int availableHeight) { int preferredVisibleButtonCount = this.preferredVisibleIconCount .get(state); BasicRibbonGalleryUI ui = (BasicRibbonGalleryUI) this.getUI(); return ui.getPreferredWidth(preferredVisibleButtonCount, availableHeight); } /** * Sets new display priority for this in-ribbon gallery. * * @param displayPriority * New display priority for this in-ribbon gallery. */ public void setDisplayPriority(RibbonElementPriority displayPriority) { this.displayPriority = displayPriority; } /** * Returns the current display priority for this in-ribbon * gallery. * * @return The current display priority for this in-ribbon * gallery. */ public RibbonElementPriority getDisplayPriority() { return this.displayPriority; } /** * Returns the number of button groups in this in-ribbon * gallery. * * @return The number of button groups in this in-ribbon * gallery. */ public int getButtonGroupCount() { return this.buttonGroups.size(); } /** * Returns the list of buttons in the specifed button group. * * @param buttonGroupName * Button group name. * @return The list of buttons in the specifed button group. */ public List getButtonGroup(String buttonGroupName) { for (StringValuePair> group : this.buttonGroups) { if (group.getKey().compareTo(buttonGroupName) == 0) return group.getValue(); } return null; } /** * Returns the number of gallery buttons in this in-ribbon * gallery. * * @return The number of gallery buttons in this in-ribbon * gallery. */ public int getButtonCount() { return this.buttons.size(); } /** * Returns the gallery button at specified index. * * @param index * Gallery button index. * @return Gallery button at specified index. */ public JCommandToggleButton getButtonAt(int index) { return this.buttons.get(index); } /** * Returns the currently selected gallery button. * * @return The currently selected gallery button. */ public JCommandToggleButton getSelectedButton() { return this.buttonSelectionGroup.getSelected(); } /** * Sets new value for the currently selected gallery button. * * @param selectedButton * New value for the currently selected gallery button. */ public void setSelectedButton(JCommandToggleButton selectedButton) { this.buttonSelectionGroup.setSelected(selectedButton, true); } /** * Returns the associated popup gallery. * * @return The associated popup gallery. */ public JCommandButtonPanel getPopupButtonPanel() { JCommandButtonPanel buttonPanel = new JCommandButtonPanel( this.buttonDisplayState); buttonPanel.setMaxButtonColumns(this.preferredPopupMaxButtonColumns); buttonPanel.setToShowGroupLabels(true); for (StringValuePair> buttonGroupEntry : this.buttonGroups) { String groupName = buttonGroupEntry.getKey(); if (groupName == null) { buttonPanel.setToShowGroupLabels(false); } buttonPanel.addButtonGroup(groupName); for (JCommandToggleButton button : buttonGroupEntry.getValue()) { // set the button to visible (the gallery hides the buttons // that don't fit the front row). button.setVisible(true); buttonPanel.addButtonToLastGroup(button); } } // just to make sure that the button panel will not try to add // the buttons to its own button group buttonPanel.setSingleSelectionMode(true); return buttonPanel; } /** * Sets indication whether the popup panel is showing. * * @param isShowingPopupPanel * Indication whether the popup panel is showing. */ public void setShowingPopupPanel(boolean isShowingPopupPanel) { this.isShowingPopupPanel = isShowingPopupPanel; if (!isShowingPopupPanel) { // populate the ribbon gallery back for (StringValuePair> buttonGroupEntry : this.buttonGroups) { for (JCommandToggleButton button : buttonGroupEntry.getValue()) { button.setDisplayState(this.buttonDisplayState); this.add(button); } } // and layout this.doLayout(); } } /** * Returns indication whether the popup panel is showing. * * @return true if the popup panel is showing, * false otherwise. */ public boolean isShowingPopupPanel() { return this.isShowingPopupPanel; } /** * Sets the button groups for this ribbon gallery. * * @param buttons * Button groups. */ public void setGroupMapping( List>> buttons) { this.buttonGroups = new ArrayList>>(); boolean hasGroupWithNullTitle = false; for (StringValuePair> buttonGroupPair : buttons) { if (buttonGroupPair.getKey() == null) { if (hasGroupWithNullTitle) { throw new IllegalArgumentException( "Can't have more than one ribbon gallery group with null name"); } hasGroupWithNullTitle = true; } // create the list of buttons for this group List buttonGroupCopy = new ArrayList(); // add it to the groups list StringValuePair> buttonGroupInfo = new StringValuePair>( buttonGroupPair.getKey(), buttonGroupCopy); this.buttonGroups.add(buttonGroupInfo); // add all the buttons to the control for (JCommandToggleButton button : buttonGroupPair.getValue()) { this.addGalleryButton(buttonGroupInfo, button); } } } /** * Adds toggle command buttons to the specified button group in this ribbon * gallery. * * @param buttonGroupName * Button group name. * @param buttons * Toggle command buttons to add to the specified button group. */ public void addRibbonGalleryButtons(String buttonGroupName, JCommandToggleButton... buttons) { for (StringValuePair> buttonGroup : this.buttonGroups) { if (buttonGroup.getKey().compareTo(buttonGroupName) == 0) { for (JCommandToggleButton button : buttons) { // buttonGroup.getValue().add(button); this.addGalleryButton(buttonGroup, button); } return; } } this.revalidate(); this.doLayout(); } /** * Removes the specified toggle command buttons from this ribbon gallery. * * @param buttons * Toggle command buttons to remove from this gallery. */ public void removeRibbonGalleryButtons(JCommandToggleButton... buttons) { for (StringValuePair> buttonGroup : this.buttonGroups) { for (Iterator it = buttonGroup.getValue() .iterator(); it.hasNext();) { JCommandToggleButton currButtonInGroup = it.next(); for (JCommandToggleButton toRemove : buttons) { if (toRemove == currButtonInGroup) { it.remove(); this.removeGalleryButton(toRemove); } } } } this.revalidate(); this.doLayout(); } /** * Sets the preferred dimension of the popup panel. * * @param preferredPopupMaxButtonColumns * Preferred maximum number of button columns for the popup * panel. * @param preferredPopupMaxVisibleButtonRows * Preferred maximum number of visible button rows for the popup * panel. */ public void setPreferredPopupPanelDimension( int preferredPopupMaxButtonColumns, int preferredPopupMaxVisibleButtonRows) { this.preferredPopupMaxButtonColumns = preferredPopupMaxButtonColumns; this.preferredPopupMaxVisibleButtonRows = preferredPopupMaxVisibleButtonRows; } public void setPopupCallback(RibbonGalleryPopupCallback popupCallback) { this.popupCallback = popupCallback; } public RibbonGalleryPopupCallback getPopupCallback() { return popupCallback; } public int getPreferredPopupMaxButtonColumns() { return preferredPopupMaxButtonColumns; } public int getPreferredPopupMaxVisibleButtonRows() { return preferredPopupMaxVisibleButtonRows; } public void setExpandKeyTip(String expandKeyTip) { String old = this.expandKeyTip; this.expandKeyTip = expandKeyTip; this.firePropertyChange("expandKeyTip", old, this.expandKeyTip); } public String getExpandKeyTip() { return expandKeyTip; } public CommandButtonDisplayState getButtonDisplayState() { return this.buttonDisplayState; } public void setButtonDisplayState( CommandButtonDisplayState buttonDisplayState) { if (this.getButtonCount() > 0) { throw new IllegalStateException( "Cannot change button display state on ribbon gallery with existing buttons"); } boolean isSupported = (buttonDisplayState == JRibbonBand.BIG_FIXED) || (buttonDisplayState == CommandButtonDisplayState.SMALL) || (buttonDisplayState == JRibbonBand.BIG_FIXED_LANDSCAPE); if (!isSupported) { throw new IllegalArgumentException("Display state " + buttonDisplayState.getDisplayName() + " is not supported in ribbon galleries"); } if (!buttonDisplayState.equals(this.buttonDisplayState)) { CommandButtonDisplayState old = this.buttonDisplayState; this.buttonDisplayState = buttonDisplayState; for (JCommandToggleButton button : this.buttons) button.setDisplayState(buttonDisplayState); this.firePropertyChange("buttonDisplayState", old, this.buttonDisplayState); } } } src/org/pushingpixels/flamingo/internal/ui/ribbon/JRibbonRootPane.java0000644000175000017500000000600611401230446025237 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.Event; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import javax.swing.*; import javax.swing.plaf.RootPaneUI; import org.pushingpixels.flamingo.api.ribbon.JRibbon; import org.pushingpixels.flamingo.api.ribbon.JRibbonFrame; /** * Root pane for the {@link JRibbonFrame}. * * @author Kirill Grouchnikov */ public class JRibbonRootPane extends JRootPane { /** * The UI class ID string. */ public static final String uiClassID = "RibbonRootPaneUI"; public static final int RIBBON_SPECIAL_LAYER = JLayeredPane.DEFAULT_LAYER + 50; public JRibbonRootPane() { InputMap inputMap = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); ActionMap actionMap = this.getActionMap(); actionMap.put("toggleMinimized", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { JRibbonFrame ribbonFrame = (JRibbonFrame) SwingUtilities .getWindowAncestor(JRibbonRootPane.this); JRibbon ribbon = ribbonFrame.getRibbon(); ribbon.setMinimized(!ribbon.isMinimized()); } }); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F1, Event.CTRL_MASK), "toggleMinimized"); } @Override public void updateUI() { setUI((RootPaneUI) UIManager.getUI(this)); } @Override public String getUIClassID() { if (UIManager.get(uiClassID) != null) return uiClassID; return "RootPaneUI"; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/CommandButtonLayoutManagerBigFixed.java0000644000175000017500000001333411401230446031111 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.*; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import javax.swing.JSeparator; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; public class CommandButtonLayoutManagerBigFixed implements CommandButtonLayoutManager { @Override public int getPreferredIconSize() { return 32; } @Override public Dimension getPreferredSize(AbstractCommandButton commandButton) { Insets borderInsets = commandButton.getInsets(); int bx = borderInsets.left + borderInsets.right; int by = borderInsets.top + borderInsets.bottom; FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont()); JSeparator jsep = new JSeparator(JSeparator.VERTICAL); int layoutVGap = FlamingoUtilities.getVLayoutGap(commandButton); // icon, label int fillTitleWidth = fm.stringWidth(commandButton.getText()); int widthFull = Math.max(this.getPreferredIconSize(), fillTitleWidth); int heightFull = by + this.getPreferredIconSize() + layoutVGap + jsep.getPreferredSize().width; if (commandButton.getText() != null) { heightFull += fm.getHeight(); } widthFull = Math.max(widthFull, heightFull); return new Dimension(bx + widthFull, heightFull); } @Override public void propertyChange(PropertyChangeEvent evt) { } @Override public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) { // center of the bottom edge return new Point(commandButton.getWidth() / 2, commandButton .getHeight()); } @Override public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g) { CommandButtonLayoutInfo result = new CommandButtonLayoutInfo(); result.actionClickArea = new Rectangle(0, 0, 0, 0); result.popupClickArea = new Rectangle(0, 0, 0, 0); Insets ins = commandButton.getInsets(); result.iconRect = new Rectangle(); result.popupActionRect = new Rectangle(); int width = commandButton.getWidth(); int height = commandButton.getHeight(); int x = ins.left; int y = ins.top; FontMetrics fm = g.getFontMetrics(); int labelHeight = fm.getAscent() + fm.getDescent(); JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton) .getCommandButtonKind() : JCommandButton.CommandButtonKind.ACTION_ONLY; result.isTextInActionArea = false; if (buttonKind == JCommandButton.CommandButtonKind.ACTION_ONLY) { result.actionClickArea.x = 0; result.actionClickArea.y = 0; result.actionClickArea.width = width; result.actionClickArea.height = height; result.isTextInActionArea = true; } if (buttonKind == JCommandButton.CommandButtonKind.POPUP_ONLY) { result.popupClickArea.x = 0; result.popupClickArea.y = 0; result.popupClickArea.width = width; result.popupClickArea.height = height; } JSeparator jsep = new JSeparator(JSeparator.VERTICAL); // int layoutGap = FlamingoUtilities.getLayoutGap(commandButton); ResizableIcon buttonIcon = commandButton.getIcon(); if (commandButton.getText() == null) { y = ins.top + (height - ins.top - ins.bottom - buttonIcon .getIconHeight()) / 2; } result.iconRect.x = (width - buttonIcon.getIconWidth()) / 2; result.iconRect.y = y; result.iconRect.width = buttonIcon.getIconWidth(); result.iconRect.height = buttonIcon.getIconHeight(); y += buttonIcon.getIconHeight(); y += jsep.getPreferredSize().width; TextLayoutInfo lineLayoutInfo = new TextLayoutInfo(); lineLayoutInfo.text = commandButton.getText(); lineLayoutInfo.textRect = new Rectangle(); int labelWidth = (int) fm.getStringBounds(commandButton.getText(), g) .getWidth(); lineLayoutInfo.textRect.x = ins.left + (width - labelWidth - ins.left - ins.right) / 2; lineLayoutInfo.textRect.y = y; lineLayoutInfo.textRect.width = labelWidth; lineLayoutInfo.textRect.height = labelHeight; result.textLayoutInfoList = new ArrayList(); result.textLayoutInfoList.add(lineLayoutInfo); return result; } } src/org/pushingpixels/flamingo/internal/ui/ribbon/JRibbonTaskToggleButton.java0000644000175000017500000000767111401516352026764 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.ribbon; import java.awt.Color; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.JCommandToggleButton; import org.pushingpixels.flamingo.api.ribbon.RibbonTask; /** * Toggle button for ribbon tasks. This class is for internal use only and * should not be directly used by the applications. * * @author Kirill Grouchnikov */ public class JRibbonTaskToggleButton extends JCommandToggleButton { /** * The UI class ID string. */ public static final String uiClassID = "RibbonTaskToggleButtonUI"; /** * Color of the matching contextual task group. Can be null if * the associated task is not contextual. */ private Color contextualGroupHueColor; private String keyTip; private RibbonTask ribbonTask; /** * Creates a new toggle button. * * @param text */ public JRibbonTaskToggleButton(RibbonTask ribbonTask) { super(ribbonTask.getTitle()); } /* * (non-Javadoc) * * @see javax.swing.JToggleButton#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI(UIManager.getUI(this)); } else { setUI(new BasicRibbonTaskToggleButtonUI()); } } /* * (non-Javadoc) * * @see javax.swing.JToggleButton#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /** * Returns the hue color of the matching contextual task group if the * associated task is contextual. * * @return The hue color of the matching contextual task group if the * associated task is contextual, null otherwise. */ public Color getContextualGroupHueColor() { return this.contextualGroupHueColor; } public RibbonTask getRibbonTask() { return this.ribbonTask; } /** * Sets the hue color of the matching contextual task group on this button. * * @param contextualGroupHueColor * The hue color of the matching contextual task group. */ public void setContextualGroupHueColor(Color contextualGroupHueColor) { Color old = this.contextualGroupHueColor; this.contextualGroupHueColor = contextualGroupHueColor; this.firePropertyChange("contextualGroupHueColor", old, this.contextualGroupHueColor); } public void setKeyTip(String keyTip) { this.keyTip = keyTip; } public String getKeyTip() { return keyTip; } } src/org/pushingpixels/flamingo/internal/ui/bcb/0000755000175000017500000000000011413206054020651 5ustar tonytonysrc/org/pushingpixels/flamingo/internal/ui/bcb/BreadcrumbBarUI.java0000644000175000017500000000410111401230444024436 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. in All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.bcb; import javax.swing.plaf.ComponentUI; import org.pushingpixels.flamingo.api.bcb.JBreadcrumbBar; /** * UI for breadcrumb bar ({@link JBreadcrumbBar}). * * @author Topologi * @author Kirill Grouchnikov */ public abstract class BreadcrumbBarUI extends ComponentUI { }src/org/pushingpixels/flamingo/internal/ui/bcb/BasicBreadcrumbBarUI.java0000644000175000017500000005042511413206054025415 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. in All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.bcb; import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import org.pushingpixels.flamingo.api.bcb.*; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonPopupOrientationKind; import org.pushingpixels.flamingo.api.common.icon.EmptyResizableIcon; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.model.PopupButtonModel; import org.pushingpixels.flamingo.api.common.popup.*; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Basic UI for breadcrumb bar ({@link JBreadcrumbBar}). * * @author Topologi * @author Kirill Grouchnikov * @author Pawel Hajda */ public class BasicBreadcrumbBarUI extends BreadcrumbBarUI { /** * The associated breadcrumb bar. */ protected JBreadcrumbBar breadcrumbBar; protected JPanel mainPanel; protected JScrollablePanel scrollerPanel; protected ComponentListener componentListener; protected JCommandButton dummy; /** * Contains the item path. */ protected LinkedList modelStack; protected LinkedList buttonStack; protected BreadcrumbPathListener pathListener; private AtomicInteger atomicCounter; /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent c) { return new BasicBreadcrumbBarUI(); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent) */ @Override public void installUI(JComponent c) { this.breadcrumbBar = (JBreadcrumbBar) c; this.modelStack = new LinkedList(); this.buttonStack = new LinkedList(); installDefaults(this.breadcrumbBar); installComponents(this.breadcrumbBar); installListeners(this.breadcrumbBar); c.setLayout(createLayoutManager()); if (this.breadcrumbBar.getCallback() != null) { SwingWorker, Void> worker = new SwingWorker, Void>() { @Override protected List doInBackground() throws Exception { return breadcrumbBar.getCallback().getPathChoices(null); } @Override protected void done() { try { pushChoices(new BreadcrumbItemChoices(null, get())); } catch (Exception exc) { } } }; worker.execute(); } this.dummy = new JCommandButton("Dummy", new EmptyResizableIcon(16)); this.dummy.setDisplayState(CommandButtonDisplayState.MEDIUM); this.dummy .setCommandButtonKind(CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent) */ @Override public void uninstallUI(JComponent c) { c.setLayout(null); uninstallListeners((JBreadcrumbBar) c); uninstallComponents((JBreadcrumbBar) c); uninstallDefaults((JBreadcrumbBar) c); this.breadcrumbBar = null; } protected void installDefaults(JBreadcrumbBar bar) { Font currFont = bar.getFont(); if ((currFont == null) || (currFont instanceof UIResource)) { Font font = FlamingoUtilities.getFont(null, "BreadcrumbBar.font", "Button.font", "Panel.font"); bar.setFont(font); } } protected void installComponents(JBreadcrumbBar bar) { this.mainPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); this.mainPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); this.mainPanel.setOpaque(false); this.scrollerPanel = new JScrollablePanel(this.mainPanel, JScrollablePanel.ScrollType.HORIZONTALLY); bar.add(this.scrollerPanel, BorderLayout.CENTER); } protected void installListeners(final JBreadcrumbBar bar) { this.atomicCounter = new AtomicInteger(0); this.componentListener = new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { updateComponents(); } }; bar.addComponentListener(this.componentListener); this.pathListener = new BreadcrumbPathListener() { private SwingWorker pathChangeWorker; @Override public void breadcrumbPathEvent(BreadcrumbPathEvent event) { final int indexOfFirstChange = event.getIndexOfFirstChange(); if ((this.pathChangeWorker != null) && !this.pathChangeWorker.isDone()) { this.pathChangeWorker.cancel(true); } this.pathChangeWorker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { atomicCounter.incrementAndGet(); synchronized (BasicBreadcrumbBarUI.this) { // remove stack elements after the first change if (indexOfFirstChange == 0) { modelStack.clear(); } else { int toLeave = indexOfFirstChange * 2 + 1; while (modelStack.size() > toLeave) modelStack.removeLast(); } } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { updateComponents(); } }); if (indexOfFirstChange == 0) { List rootChoices = breadcrumbBar .getCallback().getPathChoices(null); BreadcrumbItemChoices bic = new BreadcrumbItemChoices( null, rootChoices); if (!this.isCancelled()) { publish(bic); } } List items = breadcrumbBar.getModel() .getItems(); if (items != null) { for (int itemIndex = indexOfFirstChange; itemIndex < items .size(); itemIndex++) { if (this.isCancelled()) break; BreadcrumbItem item = items.get(itemIndex); publish(item); // now check if it has any children List subPath = new ArrayList(); for (int j = 0; j <= itemIndex; j++) { subPath.add(items.get(j)); } BreadcrumbItemChoices bic = new BreadcrumbItemChoices( item, breadcrumbBar.getCallback() .getPathChoices(subPath)); if ((bic.getChoices() != null) && (bic.getChoices().length > 0)) { // add the selector - the current item has // children publish(bic); } } } return null; } @Override protected void process(List chunks) { if (chunks != null) { for (Object chunk : chunks) { if (this.isCancelled() || atomicCounter.get() > 1) break; if (chunk instanceof BreadcrumbItemChoices) { pushChoices((BreadcrumbItemChoices) chunk, false); } if (chunk instanceof BreadcrumbItem) { pushChoice((BreadcrumbItem) chunk, false); } } } updateComponents(); } @Override protected void done() { atomicCounter.decrementAndGet(); } }; pathChangeWorker.execute(); } }; this.breadcrumbBar.getModel().addPathListener(this.pathListener); } protected void uninstallDefaults(JBreadcrumbBar bar) { } protected void uninstallComponents(JBreadcrumbBar bar) { this.mainPanel.removeAll(); this.buttonStack.clear(); bar.remove(this.scrollerPanel); } protected void uninstallListeners(JBreadcrumbBar bar) { bar.removeComponentListener(this.componentListener); this.componentListener = null; this.breadcrumbBar.getModel().removePathListener(this.pathListener); this.pathListener = null; } /** * Invoked by installUI to create a layout manager object to * manage the {@link JBreadcrumbBar}. * * @return a layout manager object * * @see BreadcrumbBarLayout */ protected LayoutManager createLayoutManager() { return new BreadcrumbBarLayout(); } /** * Layout for the breadcrumb bar. * * @author Kirill Grouchnikov * @author Topologi */ protected class BreadcrumbBarLayout implements LayoutManager { /** * Creates new layout manager. */ public BreadcrumbBarLayout() { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, * java.awt.Component) */ public void addLayoutComponent(String name, Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public void removeLayoutComponent(Component c) { } /* * (non-Javadoc) * * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container) */ public Dimension preferredLayoutSize(Container c) { // The height of breadcrumb bar is // computed based on the preferred height of a command // button in MEDIUM state. int buttonHeight = dummy.getPreferredSize().height; Insets ins = c.getInsets(); return new Dimension(c.getWidth(), buttonHeight + ins.top + ins.bottom); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container) */ public Dimension minimumLayoutSize(Container c) { int buttonHeight = dummy.getPreferredSize().height; return new Dimension(10, buttonHeight); } /* * (non-Javadoc) * * @see java.awt.LayoutManager#layoutContainer(java.awt.Container) */ public void layoutContainer(Container c) { int width = c.getWidth(); int height = c.getHeight(); scrollerPanel.setBounds(0, 0, width, height); } } protected synchronized void updateComponents() { if (!this.breadcrumbBar.isVisible()) return; this.mainPanel.removeAll(); buttonStack.clear(); // update the ui for (int i = 0; i < modelStack.size(); i++) { Object element = modelStack.get(i); if (element instanceof BreadcrumbItemChoices) { BreadcrumbItemChoices bic = (BreadcrumbItemChoices) element; if (buttonStack.isEmpty()) { JCommandButton button = new JCommandButton(""); button.setCommandButtonKind(CommandButtonKind.POPUP_ONLY); configureBreadcrumbButton(button); configurePopupAction(button, bic); configurePopupRollover(button); buttonStack.add(button); } else { JCommandButton button = buttonStack.getLast(); int oldW = button.getPreferredSize().width; button .setCommandButtonKind(CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); configurePopupAction(button, bic); configurePopupRollover(button); } } else if (element instanceof BreadcrumbItem) { BreadcrumbItem bi = (BreadcrumbItem) element; JCommandButton button = new JCommandButton(bi.getKey()); configureBreadcrumbButton(button); configureMainAction(button, bi); final Icon icon = bi.getIcon(); if (icon != null) { button.setIcon(new ResizableIcon() { int iw = icon.getIconWidth(); int ih = icon.getIconHeight(); @Override public void paintIcon(Component c, Graphics g, int x, int y) { int dx = (iw - icon.getIconWidth()) / 2; int dy = (ih - icon.getIconHeight()) / 2; icon.paintIcon(c, g, x + dx, y + dy); } @Override public int getIconWidth() { return iw; } @Override public int getIconHeight() { return ih; } @Override public void setDimension(Dimension newDimension) { iw = newDimension.width; ih = newDimension.height; } }); } if (i > 0) { BreadcrumbItemChoices lastBic = (BreadcrumbItemChoices) modelStack .get(i - 1); BreadcrumbItem[] choices = lastBic.getChoices(); if (choices != null) { for (int j = 0; j < choices.length; j++) { if (bi.getKey().equals(choices[j].getKey())) { lastBic.setSelectedIndex(j); break; } } } } buttonStack.addLast(button); } } for (JCommandButton jcb : buttonStack) { this.mainPanel.add(jcb); } this.scrollerPanel.revalidate(); this.scrollerPanel.repaint(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // scroll to the last element in the breadcrumb bar scrollerPanel.scrollToIfNecessary( mainPanel.getPreferredSize().width, 0); scrollerPanel.repaint(); } }); } private void configureMainAction(JCommandButton button, final BreadcrumbItem bi) { button.getActionModel().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { BreadcrumbBarModel barModel = breadcrumbBar.getModel(); int itemIndex = barModel.indexOf(bi); int toLeave = (itemIndex < 0) ? 0 : itemIndex + 1; barModel.setCumulative(true); while (barModel.getItemCount() > toLeave) { barModel.removeLast(); } barModel.setCumulative(false); } }); } }); } private void configurePopupAction(JCommandButton button, final BreadcrumbItemChoices bic) { button.setPopupCallback(new PopupPanelCallback() { @Override public JPopupPanel getPopupPanel(JCommandButton commandButton) { JCommandPopupMenu popup = new JCommandPopupMenu(); for (int i = 0; i < bic.getChoices().length; i++) { final BreadcrumbItem bi = bic.getChoices()[i]; JCommandMenuButton menuButton = new JCommandMenuButton(bi .getKey(), null); final Icon icon = bi.getIcon(); if (icon != null) { menuButton.setIcon(new ResizableIcon() { int iw = icon.getIconWidth(); int ih = icon.getIconHeight(); @Override public void paintIcon(Component c, Graphics g, int x, int y) { int dx = (iw - icon.getIconWidth()) / 2; int dy = (ih - icon.getIconHeight()) / 2; icon.paintIcon(c, g, x + dx, y + dy); } @Override public int getIconWidth() { return iw; } @Override public int getIconHeight() { return ih; } @Override public void setDimension(Dimension newDimension) { iw = newDimension.width; ih = newDimension.height; } }); } if (i == bic.getSelectedIndex()) { menuButton.setFont(menuButton.getFont().deriveFont( Font.BOLD)); } final int biIndex = i; menuButton.getActionModel().addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { if (bi == null) return; BreadcrumbBarModel barModel = breadcrumbBar .getModel(); barModel.setCumulative(true); int itemIndex = barModel .indexOf(bic.getAncestor()); int toLeave = ((bic.getAncestor() == null) || (itemIndex < 0)) ? 0 : itemIndex + 1; while (barModel.getItemCount() > toLeave) { barModel.removeLast(); } barModel.addLast(bi); bic.setSelectedIndex(biIndex); barModel.setCumulative(false); } }); } }); popup.addMenuButton(menuButton); menuButton.setCursor(Cursor .getPredefinedCursor(Cursor.HAND_CURSOR)); } popup.setMaxVisibleMenuButtons(10); return popup; } }); } private void configurePopupRollover(final JCommandButton button) { button.getPopupModel().addChangeListener(new ChangeListener() { boolean rollover = button.getPopupModel().isRollover(); @Override public void stateChanged(ChangeEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { boolean isRollover = button.getPopupModel() .isRollover(); if (isRollover == rollover) return; if (isRollover) { // does any *other* button show popup? for (JCommandButton bcbButton : buttonStack) { if (bcbButton == button) continue; if (bcbButton.getPopupModel().isPopupShowing()) { // scroll to view scrollerPanel.scrollToIfNecessary(button .getBounds().x, button.getWidth()); // simulate click on the popup area // of *this* button button.doPopupClick(); } } } rollover = isRollover; } }); } }); } private void configureBreadcrumbButton(final JCommandButton button) { button.setDisplayState(CommandButtonDisplayState.MEDIUM); button .setPopupOrientationKind(CommandButtonPopupOrientationKind.SIDEWARD); button.setHGapScaleFactor(0.75); button.getPopupModel().addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { PopupButtonModel model = button.getPopupModel(); boolean displayDownwards = model.isRollover() || model.isPopupShowing(); CommandButtonPopupOrientationKind popupOrientationKind = displayDownwards ? CommandButtonPopupOrientationKind.DOWNWARD : CommandButtonPopupOrientationKind.SIDEWARD; button.setPopupOrientationKind(popupOrientationKind); } }); } /** * Pushes a choice to the top position of the stack. If the current top is * already a {@link BreadcrumbItemChoices}, replace it. * * @param bic * The choice item to push. * @return The item that has been pushed. */ protected Object pushChoices(BreadcrumbItemChoices bic) { return pushChoices(bic, true); } /** * Pushes a choice to the top position of the stack. If the current top is * already a {@link BreadcrumbItemChoices}, replace it. * * @param bic * The choice item to push. * @param toUpdateUI * Indication whether the bar should be repainted. * @return The item that has been pushed. */ protected synchronized Object pushChoices(BreadcrumbItemChoices bic, boolean toUpdateUI) { if (bic == null) return null; if (modelStack.size() % 2 == 1) { modelStack.pop(); } modelStack.addLast(bic); if (toUpdateUI) { updateComponents(); } return bic; } /** * Pushes an item to the top position of the stack. If the current top is * already a {@link BreadcrumbItemChoices}, replace it. * * @param bi * The item to push. * @param toUpdateUI * Indication whether the bar should be repainted. * @return The item that has been pushed. */ protected synchronized Object pushChoice(BreadcrumbItem bi, boolean toUpdateUI) { assert (bi != null); Object result; // synchronized (stack) { if (!modelStack.isEmpty() && modelStack.size() % 2 == 0) { modelStack.pop(); } bi.setIndex(modelStack.size()); modelStack.addLast(bi); // } return bi; } }src/org/pushingpixels/flamingo/internal/ui/bcb/BreadcrumbItemChoices.java0000644000175000017500000000730111401230444025675 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.ui.bcb; import java.util.List; import javax.swing.Icon; import org.pushingpixels.flamingo.api.bcb.BreadcrumbItem; import org.pushingpixels.flamingo.api.common.StringValuePair; /** * This is the model for the popup that is shown by clicking on the path * selector. */ final class BreadcrumbItemChoices { /** * Contains all possible choices. */ private BreadcrumbItem[] choices; /** * The ancestor item. This can be null only for the root * choices element. */ private BreadcrumbItem ancestor; /** * The index of this element. */ private int selectedIndex = 0; public BreadcrumbItemChoices(BreadcrumbItem ancestor, List> entries) { this.ancestor = ancestor; this.choices = new BreadcrumbItem[entries.size()]; int index = 0; for (StringValuePair pair : entries) { this.choices[index] = new BreadcrumbItem(pair.getKey(), pair .getValue()); this.choices[index].setIcon((Icon) pair.get("icon")); index++; } this.selectedIndex = -1; } /** * Returns the 0-based index of the first {@link BreadcrumbItem} whose * display name matches the specified string. * * @param s * String. * @return The 0-based index of the first {@link BreadcrumbItem} whose * display name matches the specified string. */ public int getPosition(String s) { assert (s != null && s.length() > 0); for (int i = 0; i < choices.length; i++) { BreadcrumbItem it = choices[i]; if (s.equals(it.getKey())) return i; } return -1; } public void setSelectedIndex(int index) { this.selectedIndex = index; } public int getSelectedIndex() { return this.selectedIndex; } /** * Returns the item array of truehis element. * * @return The item array of truehis element. */ public BreadcrumbItem[] getChoices() { return choices; } public BreadcrumbItem getAncestor() { return ancestor; } } src/org/pushingpixels/flamingo/internal/utils/0000755000175000017500000000000011426041026020646 5ustar tonytonysrc/org/pushingpixels/flamingo/internal/utils/FlamingoUtilities.java0000644000175000017500000005005611425577230025161 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.*; import java.awt.geom.GeneralPath; import java.awt.image.BufferedImage; import java.util.List; import javax.swing.*; import javax.swing.plaf.*; import org.pushingpixels.flamingo.api.common.AbstractCommandButton; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy; import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel; import org.pushingpixels.flamingo.internal.ui.ribbon.JRibbonTaskToggleButton; import org.pushingpixels.flamingo.internal.ui.ribbon.appmenu.JRibbonApplicationMenuButton; /** * Helper utilities for Flamingo project. This class is for internal use only. * * @author Kirill Grouchnikov */ public class FlamingoUtilities { /** * Gets the component font. * * @param comp * Component. * @param keys * {@link UIManager} keys. * @return If the component is not null, its font is returned. * Otherwise the first entry in {@link UIManager} which is a * {@link Font} is returned. */ public static FontUIResource getFont(Component comp, String... keys) { if (comp != null) { Font compFont = comp.getFont(); if ((compFont != null) && !(compFont instanceof UIResource)) { return new FontUIResource(compFont); } } for (String key : keys) { Font font = UIManager.getFont(key); if (font != null) { if (font instanceof UIResource) return (FontUIResource) font; else return new FontUIResource(font); } } return null; } /** * Gets the color based on the specified {@link UIManager} keys. * * @param defaultColor * Default color to return if none of the {@link UIManager} keys * are present. * @param keys * {@link UIManager} keys. * @return The first entry in {@link UIManager} which is a color. If none, * then the default color is returned. */ public static Color getColor(Color defaultColor, String... keys) { for (String key : keys) { Color color = UIManager.getColor(key); if (color != null) return color; } return new ColorUIResource(defaultColor); } /** * Returns a ribbon band expand icon. * * @return Ribbon band expand icon. */ public static ResizableIcon getRibbonBandExpandIcon( AbstractRibbonBand ribbonBand) { boolean ltr = ribbonBand.getComponentOrientation().isLeftToRight(); return new ArrowResizableIcon(9, ltr ? SwingConstants.EAST : SwingConstants.WEST); } /** * Returns a popup action icon for the specific command button. */ public static ResizableIcon getCommandButtonPopupActionIcon( JCommandButton commandButton) { JCommandButton.CommandButtonPopupOrientationKind popupOrientationKind = ((JCommandButton) commandButton) .getPopupOrientationKind(); switch (popupOrientationKind) { case DOWNWARD: return new ArrowResizableIcon.CommandButtonPopupIcon(9, SwingConstants.SOUTH); case SIDEWARD: return new ArrowResizableIcon.CommandButtonPopupIcon( 9, commandButton.getComponentOrientation().isLeftToRight() ? SwingConstants.EAST : SwingConstants.WEST); } return null; } /** * Creates a thumbnail of the specified width. * * @param image * The original image. * @param requestedThumbWidth * The width of the resulting thumbnail. * @return Thumbnail of the specified width. * @author Romain Guy */ public static BufferedImage createThumbnail(BufferedImage image, int requestedThumbWidth) { float ratio = (float) image.getWidth() / (float) image.getHeight(); int width = image.getWidth(); BufferedImage thumb = image; do { width /= 2; if (width < requestedThumbWidth) { width = requestedThumbWidth; } BufferedImage temp = new BufferedImage(width, (int) (width / ratio), BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = temp.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null); g2.dispose(); thumb = temp; } while (width != requestedThumbWidth); return thumb; } /** * Returns the outline of the ribbon border. * * @param startX * The starting X of the ribbon area. * @param endX * The ending X of the ribbon area. * @param startSelectedX * The starting X of the toggle tab button of the selected task. * @param endSelectedX * The ending X of the toggle tab button of the selected task. * @param topY * The top Y of the ribbon area. * @param bandTopY * The top Y of the ribbon band area. * @param bottomY * The bottom Y of the ribbon area. * @param radius * Corner radius. * @return The outline of the ribbon border. */ public static GeneralPath getRibbonBorderOutline(int startX, int endX, int startSelectedX, int endSelectedX, int topY, int bandTopY, int bottomY, float radius) { int height = bottomY - topY; GeneralPath result = new GeneralPath(); float radius3 = (float) (radius / (1.5 * Math.pow(height, 0.5))); // start in the top left corner at the end of the curve result.moveTo(startX + radius, bandTopY); // move to the bottom start of the selected tab and curve up result.lineTo(startSelectedX - radius, bandTopY); // result.quadTo(startSelectedX - radius3, bandTopY - radius3, // startSelectedX, bandTopY - radius); // move to the top start of the selected tab and curve right // result.lineTo(startSelectedX, topY + radius); // result.quadTo(startSelectedX + radius3, topY + radius3, // startSelectedX // + radius, topY); // move to the top end of the selected tab and curve down // result.lineTo(endSelectedX - radius - 1, topY); // result.quadTo(endSelectedX + radius3 - 1, topY + radius3, // endSelectedX - 1, topY + radius); // move to the bottom end of the selected tab and curve right // result.lineTo(endSelectedX - 1, bandTopY - radius); // result.quadTo(endSelectedX + radius3 - 1, bandTopY - radius3, // endSelectedX + radius - 1, bandTopY); result.moveTo(endSelectedX + radius - 1, bandTopY); // move to the top right corner and curve down result.lineTo(endX - radius - 1, bandTopY); result.quadTo(endX - radius3 - 1, bandTopY + radius3, endX - 1, bandTopY + radius); // move to the bottom right corner and curve left result.lineTo(endX - 1, bottomY - radius - 1); result.quadTo(endX - radius3 - 1, bottomY - 1 - radius3, endX - radius - 1, bottomY - 1); // move to the bottom left corner and curve up result.lineTo(startX + radius, bottomY - 1); result.quadTo(startX + radius3, bottomY - 1 - radius3, startX, bottomY - radius - 1); // move to the top left corner and curve right result.lineTo(startX, bandTopY + radius); result.quadTo(startX + radius3, bandTopY + radius3, startX + radius, bandTopY); return result; } /** * Returns the clip area of a task toggle button in ribbon component. * * @param width * Toggle tab button width. * @param height * Toggle tab button height. * @param radius * Toggle tab button corner radius. * @return Clip area of a toggle tab button in ribbon component. */ public static GeneralPath getRibbonTaskToggleButtonOutline(int width, int height, float radius) { GeneralPath result = new GeneralPath(); float radius3 = (float) (radius / (1.5 * Math.pow(height, 0.5))); // start at the bottom left result.moveTo(0, height); // move to the top start and curve right result.lineTo(0, radius); result.quadTo(radius3, radius3, radius, 0); // move to the top end and curve down result.lineTo(width - radius - 1, 0); result.quadTo(width + radius3 - 1, radius3, width - 1, radius); // move to the bottom right end result.lineTo(width - 1, height); // move to the bottom left end result.lineTo(0, height); return result; } /** * Returns the outline of in-ribbon gallery. * * @param startX * Start X of the in-ribbon gallery. * @param endX * End X of the in-ribbon gallery. * @param topY * Top Y of the in-ribbon gallery. * @param bottomY * Bottom Y of the in-ribbon gallery. * @param radius * Corner radius. * @return The outline of in-ribbon gallery. */ public static GeneralPath getRibbonGalleryOutline(int startX, int endX, int topY, int bottomY, float radius) { int height = bottomY - topY; GeneralPath result = new GeneralPath(); float radius3 = (float) (radius / (1.5 * Math.pow(height, 0.5))); // start in the top left corner at the end of the curve result.moveTo(startX + radius, topY); // move to the top right corner and curve down result.lineTo(endX - radius - 1, topY); result.quadTo(endX - radius3 - 1, topY + radius3, endX - 1, topY + radius); // move to the bottom right corner and curve left result.lineTo(endX - 1, bottomY - radius - 1); result.quadTo(endX - radius3 - 1, bottomY - 1 - radius3, endX - radius - 1, bottomY - 1); // move to the bottom left corner and curve up result.lineTo(startX + radius, bottomY - 1); result.quadTo(startX + radius3, bottomY - 1 - radius3, startX, bottomY - radius - 1); // move to the top left corner and curve right result.lineTo(startX, topY + radius); result.quadTo(startX + radius3, topY + radius3, startX + radius, topY); return result; } /** * Clips string based on specified font metrics and available width (in * pixels). Returns the clipped string, which contains the beginning and the * end of the input string separated by ellipses (...) in case the string is * too long to fit into the specified width, and the origianl string * otherwise. * * @param metrics * Font metrics. * @param availableWidth * Available width in pixels. * @param fullText * String to clip. * @return The clipped string, which contains the beginning and the end of * the input string separated by ellipses (...) in case the string * is too long to fit into the specified width, and the origianl * string otherwise. */ public static String clipString(FontMetrics metrics, int availableWidth, String fullText) { if (metrics.stringWidth(fullText) <= availableWidth) return fullText; String ellipses = "..."; int ellipsesWidth = metrics.stringWidth(ellipses); if (ellipsesWidth > availableWidth) return ""; String starter = ""; int w = fullText.length(); String prevText = ""; for (int i = 0; i < w; i++) { String newStarter = starter + fullText.charAt(i); String newText = newStarter + ellipses; if (metrics.stringWidth(newText) <= availableWidth) { starter = newStarter; prevText = newText; continue; } return prevText; } return fullText; } /** * Retrieves transparent image of specified dimension. * * @param width * Image width. * @param height * Image height. * @return Transparent image of specified dimension. */ public static BufferedImage getBlankImage(int width, int height) { GraphicsEnvironment e = GraphicsEnvironment .getLocalGraphicsEnvironment(); GraphicsDevice d = e.getDefaultScreenDevice(); GraphicsConfiguration c = d.getDefaultConfiguration(); BufferedImage compatibleImage = c.createCompatibleImage(width, height, Transparency.TRANSLUCENT); return compatibleImage; } /** * Returns the alpha version of the specified color. * * @param color * Original color. * @param alpha * Alpha channel value. * @return Alpha version of the specified color. */ public static Color getAlphaColor(Color color, int alpha) { return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); } public static int getHLayoutGap(AbstractCommandButton commandButton) { Font font = commandButton.getFont(); if (font == null) font = UIManager.getFont("Button.font"); return (int) Math.ceil(commandButton.getHGapScaleFactor() * (font.getSize() - 4) / 4); } public static int getVLayoutGap(AbstractCommandButton commandButton) { Font font = commandButton.getFont(); if (font == null) font = UIManager.getFont("Button.font"); return (int) Math.ceil(commandButton.getVGapScaleFactor() * (font.getSize() - 4) / 4); } public static boolean hasPopupAction(AbstractCommandButton commandButton) { if (commandButton instanceof JCommandButton) { JCommandButton jcb = (JCommandButton) commandButton; return jcb.getCommandButtonKind().hasPopup(); } return false; } public static void updateRibbonFrameIconImages(JRibbonFrame ribbonFrame) { JRibbonApplicationMenuButton appMenuButton = getApplicationMenuButton(ribbonFrame); if (appMenuButton == null) { return; } ResizableIcon appIcon = ribbonFrame.getApplicationIcon(); if (appIcon != null) { appMenuButton.setIcon(appIcon); } } public static JRibbonApplicationMenuButton getApplicationMenuButton( Component comp) { if (comp instanceof JRibbonApplicationMenuButton) return (JRibbonApplicationMenuButton) comp; if (comp instanceof Container) { Container cont = (Container) comp; for (int i = 0; i < cont.getComponentCount(); i++) { JRibbonApplicationMenuButton result = getApplicationMenuButton(cont .getComponent(i)); if ((result != null) && result.isVisible()) return result; } } return null; } public static void renderSurface(Graphics g, Container c, Rectangle rect, boolean toSimulateRollover, boolean hasTopBorder, boolean hasBottomBorder) { CellRendererPane buttonRendererPane = new CellRendererPane(); JButton rendererButton = new JButton(""); rendererButton.getModel().setRollover(toSimulateRollover); buttonRendererPane.setBounds(rect.x, rect.y, rect.width, rect.height); Graphics2D g2d = (Graphics2D) g.create(); g2d.clipRect(rect.x, rect.y, rect.width, rect.height); buttonRendererPane.paintComponent(g2d, rendererButton, c, rect.x - rect.width / 2, rect.y - rect.height / 2, 2 * rect.width, 2 * rect.height, true); g2d.setColor(FlamingoUtilities.getBorderColor()); if (hasTopBorder) { g2d.drawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y); } if (hasBottomBorder) { g2d.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width - 1, rect.y + rect.height - 1); } g2d.dispose(); } /** * Returns lighter version of the specified color. * * @param color * Color. * @param diff * Difference factor (values closer to 1.0 will produce results * closer to white color). * @return Lighter version of the specified color. */ public static Color getLighterColor(Color color, double diff) { int r = color.getRed() + (int) (diff * (255 - color.getRed())); int g = color.getGreen() + (int) (diff * (255 - color.getGreen())); int b = color.getBlue() + (int) (diff * (255 - color.getBlue())); return new Color(r, g, b); } public static Color getBorderColor() { return FlamingoUtilities.getColor(Color.gray, "TextField.inactiveForeground", "Button.disabledText", "ComboBox.disabledForeground"); } public static boolean isShowingMinimizedRibbonInPopup(JRibbon ribbon) { List popups = PopupPanelManager .defaultManager().getShownPath(); if (popups.size() == 0) return false; for (PopupPanelManager.PopupInfo popup : popups) { JComponent originator = popup.getPopupOriginator(); if (originator instanceof JRibbonTaskToggleButton) { return (ribbon == SwingUtilities.getAncestorOfClass( JRibbon.class, originator)); } } return false; } public static boolean isShowingMinimizedRibbonInPopup( JRibbonTaskToggleButton taskToggleButton) { List popups = PopupPanelManager .defaultManager().getShownPath(); if (popups.size() == 0) return false; for (PopupPanelManager.PopupInfo popup : popups) { JComponent originator = popup.getPopupOriginator(); if (originator == taskToggleButton) return true; } return false; } public static void checkResizePoliciesConsistency( AbstractRibbonBand ribbonBand) { Insets ins = ribbonBand.getInsets(); AbstractBandControlPanel controlPanel = ribbonBand.getControlPanel(); if (controlPanel == null) return; int height = controlPanel.getPreferredSize().height + ribbonBand.getUI().getBandTitleHeight() + ins.top + ins.bottom; List resizePolicies = ribbonBand .getResizePolicies(); checkResizePoliciesConsistencyBase(ribbonBand); for (int i = 0; i < (resizePolicies.size() - 1); i++) { RibbonBandResizePolicy policy1 = resizePolicies.get(i); RibbonBandResizePolicy policy2 = resizePolicies.get(i + 1); int width1 = policy1.getPreferredWidth(height, 4); int width2 = policy2.getPreferredWidth(height, 4); if (width1 < width2) { // create the trace message StringBuilder builder = new StringBuilder(); builder.append("Inconsistent preferred widths\n"); builder.append("Ribbon band '" + ribbonBand.getTitle() + "' has the following resize policies\n"); for (int j = 0; j < resizePolicies.size(); j++) { RibbonBandResizePolicy policy = resizePolicies.get(j); int width = policy.getPreferredWidth(height, 4); builder.append("\t" + policy.getClass().getName() + " with preferred width " + width + "\n"); } builder.append(policy1.getClass().getName() + " with pref width " + width1 + " is followed by resize policy " + policy2.getClass().getName() + " with larger pref width\n"); throw new IllegalStateException(builder.toString()); } } } public static void checkResizePoliciesConsistencyBase( AbstractRibbonBand ribbonBand) { List resizePolicies = ribbonBand .getResizePolicies(); if (resizePolicies.size() == 0) { throw new IllegalStateException("Resize policy list is empty"); } for (int i = 0; i < resizePolicies.size(); i++) { RibbonBandResizePolicy policy = resizePolicies.get(i); boolean isIcon = policy instanceof IconRibbonBandResizePolicy; if (isIcon && (i < (resizePolicies.size() - 1))) { throw new IllegalStateException( "Icon resize policy must be the last in the list"); } } } } src/org/pushingpixels/flamingo/internal/utils/RenderingUtils.java0000644000175000017500000001021011401230444024436 0ustar tonytony/* * Copyright (c) 2001-2006 JGoodies Karsten Lentzsch. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of JGoodies Karsten Lentzsch nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.*; import java.awt.print.PrinterGraphics; import java.util.*; /** * Utilities to install desktop rendering hints for correctly rasterizing texts. * * @author Kirill Grouchnikov */ public class RenderingUtils { /** * Desktop property name that points to a collection of rendering hints. See * documentation for more details. */ private static final String PROP_DESKTOPHINTS = "awt.font.desktophints"; /** * Installs desktop hints on the specified graphics context. * * @param g2 * Graphics context. * @return Map of old rendering hints. */ public static Map installDesktopHints(Graphics2D g2) { Map oldRenderingHints = null; Map desktopHints = desktopHints(g2); if (desktopHints != null && !desktopHints.isEmpty()) { oldRenderingHints = new HashMap(desktopHints.size()); RenderingHints.Key key; for (Iterator i = desktopHints.keySet().iterator(); i.hasNext();) { key = (RenderingHints.Key) i.next(); oldRenderingHints.put(key, g2.getRenderingHint(key)); } g2.addRenderingHints(desktopHints); } return oldRenderingHints; } /** * Returns the desktop hints for the specified graphics context. * * @param g2 * Graphics context. * @return The desktop hints for the specified graphics context. */ private static Map desktopHints(Graphics2D g2) { if (isPrinting(g2)) { return null; } Toolkit toolkit = Toolkit.getDefaultToolkit(); GraphicsDevice device = g2.getDeviceConfiguration().getDevice(); Map desktopHints = (Map) toolkit.getDesktopProperty(PROP_DESKTOPHINTS + '.' + device.getIDstring()); if (desktopHints == null) { desktopHints = (Map) toolkit.getDesktopProperty(PROP_DESKTOPHINTS); } // It is possible to get a non-empty map but with disabled AA. if (desktopHints != null) { Object aaHint = desktopHints .get(RenderingHints.KEY_TEXT_ANTIALIASING); if ((aaHint == RenderingHints.VALUE_TEXT_ANTIALIAS_OFF) || (aaHint == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT)) { desktopHints = null; } } return desktopHints; } /** * Checks whether the specified graphics context is a print context. * * @param g * Graphics context. * @return true if the specified graphics context is a print * context. */ private static boolean isPrinting(Graphics g) { return g instanceof PrintGraphics || g instanceof PrinterGraphics; } } src/org/pushingpixels/flamingo/internal/utils/ColorShiftFilter.java0000644000175000017500000000727211401230444024740 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.Color; import java.awt.image.BufferedImage; /** * Image filter that shifts the colors of the original image. * * @author Kirill Grouchnikov */ public class ColorShiftFilter extends AbstractFilter { /** * Red component of the shift color. */ int rShift; /** * Green component of the shift color. */ int gShift; /** * Blue component of the shift color. */ int bShift; /** * Shift amount in 0.0-1.0 range. */ double hueShiftAmount; /** * Creates a new color shift filter. * * @param shiftColor * Shift color. * @param shiftAmount * Shift amount in 0.0-1.0 range. */ public ColorShiftFilter(Color shiftColor, double shiftAmount) { this.rShift = shiftColor.getRed(); this.gShift = shiftColor.getGreen(); this.bShift = shiftColor.getBlue(); this.hueShiftAmount = shiftAmount; } /* * (non-Javadoc) * * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, * java.awt.image.BufferedImage) */ @Override public BufferedImage filter(BufferedImage src, BufferedImage dst) { if (dst == null) { dst = createCompatibleDestImage(src, null); } int width = src.getWidth(); int height = src.getHeight(); int[] pixels = new int[width * height]; getPixels(src, 0, 0, width, height, pixels); shiftColor(pixels); setPixels(dst, 0, 0, width, height, pixels); return dst; } /** * Color-shifts all the pixels in the specified pixel array. * * @param pixels * Pixel array for color-shifting. */ private void shiftColor(int[] pixels) { for (int i = 0; i < pixels.length; i++) { int argb = pixels[i]; int r = (argb >>> 16) & 0xFF; int g = (argb >>> 8) & 0xFF; int b = (argb >>> 0) & 0xFF; int nr = (int) (this.hueShiftAmount * this.rShift + (1.0 - this.hueShiftAmount) * r); int ng = (int) (this.hueShiftAmount * this.gShift + (1.0 - this.hueShiftAmount) * g); int nb = (int) (this.hueShiftAmount * this.bShift + (1.0 - this.hueShiftAmount) * b); pixels[i] = (argb & 0xFF000000) | nr << 16 | ng << 8 | nb; } } } src/org/pushingpixels/flamingo/internal/utils/ArrowResizableIcon.java0000644000175000017500000001354211401230444025257 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.*; import java.awt.geom.GeneralPath; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; /** * Helper implementation of {@link ResizableIcon} that draws an arrow. * * @author Kirill Grouchnikov */ public class ArrowResizableIcon implements ResizableIcon { /** * Initial dimension. */ private Dimension initialDim; /** * The current icon width. */ protected int width; /** * The current icon height. */ protected int height; /** * Arrow direction. One of {@link SwingConstants#SOUTH}, * {@link SwingConstants#NORTH}, {@link SwingConstants#EAST} or * {@link SwingConstants#WEST}. */ protected int direction; /** * Creates a new arrow resizable icon. * * @param initialDim * Initial icon dimension. * @param direction * Arrow direction. Must be one of {@link SwingConstants#SOUTH}, * {@link SwingConstants#NORTH}, {@link SwingConstants#EAST} or * {@link SwingConstants#WEST}. */ public ArrowResizableIcon(Dimension initialDim, int direction) { this.initialDim = initialDim; this.width = initialDim.width; this.height = initialDim.height; this.direction = direction; } /** * Creates a new arrow resizable icon. * * @param initialDim * Initial icon dimension. * @param direction * Arrow direction. Must be one of {@link SwingConstants#SOUTH}, * {@link SwingConstants#NORTH}, {@link SwingConstants#EAST} or * {@link SwingConstants#WEST}. */ public ArrowResizableIcon(int initialDim, int direction) { this(new Dimension(initialDim, initialDim), direction); } public void revertToOriginalDimension() { this.width = initialDim.width; this.height = initialDim.height; } public void setDimension(Dimension newDimension) { this.width = newDimension.width; this.height = newDimension.height; } public int getIconHeight() { return this.height; } public int getIconWidth() { return this.width; } protected boolean toPaintEnabled(Component c) { return c.isEnabled(); } public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D graphics = (Graphics2D) g.create(); float strokeWidth = this.width / 7.0f; if (strokeWidth < 1.0f) strokeWidth = 1.0f; Stroke stroke = new BasicStroke(strokeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); graphics.setStroke(stroke); GeneralPath gp = new GeneralPath(); switch (direction) { case SwingUtilities.SOUTH: gp.moveTo(0, 2); gp.lineTo((float) 0.5 * (width - 1), height - 2); gp.lineTo(width - 1, 2); break; case SwingUtilities.NORTH: gp.moveTo(0, height - 2); gp.lineTo((float) 0.5 * (width - 1), 2); gp.lineTo(width - 1, height - 2); break; case SwingUtilities.EAST: gp.moveTo(2, 0); gp.lineTo(width - 2, (float) 0.5 * (height - 1)); gp.lineTo(2, height - 1); break; case SwingUtilities.WEST: gp.moveTo(width - 2, 0); gp.lineTo(2, (float) 0.5 * (height - 1)); gp.lineTo(width - 2, height - 1); break; } graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics.translate(x, y + 1); Color dropColor = this.toPaintEnabled(c) ? new Color(255, 255, 255, 196) : new Color(255, 255, 255, 32); graphics.setColor(dropColor); graphics.draw(gp); graphics.translate(0, -1); Color arrowColor = this.toPaintEnabled(c) ? Color.black : Color.gray; graphics.setColor(arrowColor); if (this.width < 9) { graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); graphics.draw(gp); } graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics.draw(gp); graphics.dispose(); } public static class CommandButtonPopupIcon extends ArrowResizableIcon { public CommandButtonPopupIcon(int initialDim, int direction) { super(initialDim, direction); } @Override protected boolean toPaintEnabled(Component c) { JCommandButton jcb = (JCommandButton) c; return jcb.isEnabled() && jcb.getPopupModel().isEnabled(); } } } src/org/pushingpixels/flamingo/internal/utils/KeyTipManager.java0000644000175000017500000005202311426362746024232 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.annotation.*; import java.util.*; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import javax.swing.*; import javax.swing.FocusManager; import javax.swing.event.EventListenerList; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager.PopupInfo; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.internal.ui.ribbon.*; import org.pushingpixels.flamingo.internal.ui.ribbon.appmenu.JRibbonApplicationMenuButton; import org.pushingpixels.flamingo.internal.ui.ribbon.appmenu.JRibbonApplicationMenuPopupPanel; public class KeyTipManager { boolean isShowingKeyTips; List keyTipChains; protected EventListenerList listenerList; protected BlockingQueue processingQueue; protected ProcessingThread processingThread; private JRibbonFrame rootOwner; private Component focusOwner; private static final KeyTipManager instance = new KeyTipManager(); public interface KeyTipLinkTraversal { public KeyTipChain getNextChain(); } public static interface KeyTipListener extends EventListener { public void keyTipsShown(KeyTipEvent event); public void keyTipsHidden(KeyTipEvent event); } public static class KeyTipEvent extends AWTEvent { public KeyTipEvent(Object source, int id) { super(source, id); } } /** * Annotation to mark a command button that shows UI content with associated * keytips on clicking its action area. Can be used to associate keytips * with menu command buttons in the popup menu shown when the ribbon gallery * is expanded. * * @author Kirill Grouchnikov */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public static @interface HasNextKeyTipChain { } public class KeyTipLink { public String keyTipString; public JComponent comp; public Point prefAnchorPoint; public ActionListener onActivated; public KeyTipLinkTraversal traversal; public boolean enabled; } public class KeyTipChain { private List links; public int keyTipLookupIndex; public JComponent chainParentComponent; private KeyTipLinkTraversal parent; public KeyTipChain(JComponent chainParentComponent) { this.chainParentComponent = chainParentComponent; this.links = new ArrayList(); this.keyTipLookupIndex = 0; } public void addLink(KeyTipLink link) { this.links.add(link); } } public static KeyTipManager defaultManager() { return instance; } private KeyTipManager() { this.isShowingKeyTips = false; this.keyTipChains = new ArrayList(); this.listenerList = new EventListenerList(); this.processingQueue = new LinkedBlockingQueue(); this.processingThread = new ProcessingThread(); this.processingThread.start(); } public boolean isShowingKeyTips() { return !this.keyTipChains.isEmpty(); } public void hideAllKeyTips() { if (this.keyTipChains.isEmpty()) return; this.keyTipChains.clear(); this.fireKeyTipsHidden(rootOwner); repaintWindows(); // try restoring the focus owner if still relevant this.tryRestoringFocusOwner(); } private void tryRestoringFocusOwner() { if (focusOwner != null) { if (focusOwner.isDisplayable() && focusOwner.isShowing()) { focusOwner.requestFocus(); } } } public void showRootKeyTipChain(JRibbonFrame ribbonFrame) { if (!this.keyTipChains.isEmpty()) { throw new IllegalStateException( "Can't call this method when key tip chains are present"); } // store the current focus owner focusOwner = FocusManager.getCurrentManager().getFocusOwner(); // and transfer the focus to the ribbon frame itself. If the focus // is cleared, no key events will be dispatched to our window. ribbonFrame.requestFocus(); rootOwner = ribbonFrame; final JRibbon ribbon = ribbonFrame.getRibbon(); // root chain - application menu button, // taskbar panel components and task toggle buttons KeyTipChain root = new KeyTipChain(ribbon); // application menu button final JRibbonApplicationMenuButton appMenuButton = FlamingoUtilities .getApplicationMenuButton(ribbonFrame); if ((appMenuButton != null) && (ribbon.getApplicationMenuKeyTip() != null)) { final KeyTipLink appMenuButtonLink = new KeyTipLink(); appMenuButtonLink.comp = appMenuButton; appMenuButtonLink.keyTipString = ribbon.getApplicationMenuKeyTip(); appMenuButtonLink.prefAnchorPoint = appMenuButton.getUI() .getKeyTipAnchorCenterPoint(); appMenuButtonLink.onActivated = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { appMenuButton.doPopupClick(); } }; appMenuButtonLink.enabled = true; appMenuButtonLink.traversal = new KeyTipLinkTraversal() { @Override public KeyTipChain getNextChain() { // System.out.println("Get next chain"); // collect key tips of all controls in the relevant popup // panel List popups = PopupPanelManager.defaultManager() .getShownPath(); if (popups.size() > 0) { PopupInfo last = popups.get(popups.size() - 1); if (last.getPopupOriginator() == appMenuButton) { JPopupPanel popupPanel = last.getPopupPanel(); KeyTipChain chain = new KeyTipChain(popupPanel); chain.parent = appMenuButtonLink.traversal; populateChain(last.getPopupPanel(), chain); // popupPanel.putClientProperty(KEYTIP_MANAGER, // KeyTipManager.this); return chain; } } return null; } }; root.addLink(appMenuButtonLink); } // taskbar panel components for (Component taskbarComp : ribbon.getTaskbarComponents()) { if (taskbarComp instanceof AbstractCommandButton) { AbstractCommandButton cb = (AbstractCommandButton) taskbarComp; KeyTipLink actionLink = getCommandButtonActionLink(cb); if (actionLink != null) { root.addLink(actionLink); } if (taskbarComp instanceof JCommandButton) { JCommandButton jcb = (JCommandButton) taskbarComp; KeyTipLink popupLink = getCommandButtonPopupLink(jcb); if (popupLink != null) { root.addLink(popupLink); } } } } // task toggle buttons RibbonUI ui = ribbon.getUI(); if (ui instanceof BasicRibbonUI) { for (Map.Entry ttbEntry : ((BasicRibbonUI) ui) .getTaskToggleButtons().entrySet()) { final RibbonTask task = ttbEntry.getKey(); final JRibbonTaskToggleButton taskToggleButton = ttbEntry .getValue(); String keyTip = task.getKeyTip(); if (keyTip != null) { final KeyTipLink taskToggleButtonLink = new KeyTipLink(); taskToggleButtonLink.comp = taskToggleButton; taskToggleButtonLink.keyTipString = keyTip; taskToggleButtonLink.prefAnchorPoint = new Point( taskToggleButton.getWidth() / 2, taskToggleButton .getHeight()); taskToggleButtonLink.onActivated = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { taskToggleButton.doActionClick(); } }; taskToggleButtonLink.enabled = true; taskToggleButtonLink.traversal = new KeyTipLinkTraversal() { @Override public KeyTipChain getNextChain() { KeyTipChain taskChain = new KeyTipChain( taskToggleButton); // collect key tips of all controls from all task // bands for (AbstractRibbonBand band : task.getBands()) populateChain(band, taskChain); taskChain.parent = taskToggleButtonLink.traversal; return taskChain; } }; root.addLink(taskToggleButtonLink); } } } this.keyTipChains.add(root); this.fireKeyTipsShown(ribbonFrame); ribbonFrame.repaint(); } public Collection getCurrentlyShownKeyTips() { if (this.keyTipChains.isEmpty()) return Collections.emptyList(); return Collections.unmodifiableCollection(this.keyTipChains .get(this.keyTipChains.size() - 1).links); } public KeyTipChain getCurrentlyShownKeyTipChain() { if (this.keyTipChains.isEmpty()) return null; return this.keyTipChains.get(this.keyTipChains.size() - 1); } public void showPreviousChain() { if (this.keyTipChains.isEmpty()) return; this.keyTipChains.remove(this.keyTipChains.size() - 1); // was last? if (!this.isShowingKeyTips()) { // try restoring focus owner this.tryRestoringFocusOwner(); } repaintWindows(); } private void addCommandButtonLinks(Component c, KeyTipChain chain) { AbstractCommandButton cb = (AbstractCommandButton) c; KeyTipLink actionLink = getCommandButtonActionLink(cb); if (actionLink != null) { chain.addLink(actionLink); } if (c instanceof JCommandButton) { JCommandButton jcb = (JCommandButton) c; KeyTipLink popupLink = getCommandButtonPopupLink(jcb); if (popupLink != null) { chain.addLink(popupLink); } } } private void populateChain(final Component c, final KeyTipChain chain) { if (c instanceof AbstractCommandButton) { Rectangle compBounds = c.getBounds(); if (c.isVisible() && c.isShowing()) { if ((compBounds.height > 0) && (compBounds.width > 0)) addCommandButtonLinks(c, chain); else SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Rectangle compBounds = c.getBounds(); if ((compBounds.height > 0) && (compBounds.width > 0)) addCommandButtonLinks(c, chain); } }); } } if (c instanceof JRibbonComponent) { JRibbonComponent rc = (JRibbonComponent) c; KeyTipLink link = getRibbonComponentLink(rc); if (link != null) { chain.addLink(link); } } if (c instanceof Container) { Container cont = (Container) c; for (int i = 0; i < cont.getComponentCount(); i++) { populateChain(cont.getComponent(i), chain); } } } private KeyTipLink getCommandButtonActionLink(final AbstractCommandButton cb) { if (cb.getActionKeyTip() != null) { final KeyTipLink link = new KeyTipLink(); link.comp = cb; link.keyTipString = cb.getActionKeyTip(); link.prefAnchorPoint = cb.getUI().getKeyTipAnchorCenterPoint(); link.onActivated = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { cb.doActionClick(); } }; link.enabled = cb.getActionModel().isEnabled(); if (cb.getClass().isAnnotationPresent( KeyTipManager.HasNextKeyTipChain.class)) { link.traversal = new KeyTipLinkTraversal() { @Override public KeyTipChain getNextChain() { // collect key tips of all controls in the relevant // popup panel List popups = PopupPanelManager .defaultManager().getShownPath(); if (popups.size() > 0) { PopupInfo last = popups.get(popups.size() - 1); JPopupPanel popupPanel = last.getPopupPanel(); KeyTipChain chain = new KeyTipChain(popupPanel); populateChain(last.getPopupPanel(), chain); chain.parent = link.traversal; return chain; } return null; } }; } else { link.traversal = null; } return link; } return null; } private KeyTipLink getRibbonComponentLink(final JRibbonComponent rc) { if (rc.getKeyTip() != null) { KeyTipLink link = new KeyTipLink(); link.comp = rc; link.keyTipString = rc.getKeyTip(); link.prefAnchorPoint = rc.getUI().getKeyTipAnchorCenterPoint(); link.onActivated = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JComponent mainComponent = rc.getMainComponent(); if (mainComponent instanceof AbstractButton) { ((AbstractButton) mainComponent).doClick(); } else { if (mainComponent instanceof JComboBox) { ((JComboBox) mainComponent).showPopup(); } else { if (mainComponent instanceof JSpinner) { JComponent editor = ((JSpinner) mainComponent) .getEditor(); editor.requestFocusInWindow(); } else { mainComponent.requestFocusInWindow(); } } } } }; link.enabled = rc.getMainComponent().isEnabled(); link.traversal = null; return link; } return null; } private KeyTipLink getCommandButtonPopupLink(final JCommandButton cb) { if (cb.getPopupKeyTip() != null) { final KeyTipLink link = new KeyTipLink(); link.comp = cb; link.keyTipString = cb.getPopupKeyTip(); link.prefAnchorPoint = cb.getUI().getKeyTipAnchorCenterPoint(); link.onActivated = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (cb instanceof JCommandMenuButton) { ((JCommandMenuButton) cb).doActionRollover(); } cb.doPopupClick(); } }; link.enabled = cb.getPopupModel().isEnabled(); link.traversal = new KeyTipLinkTraversal() { @Override public KeyTipChain getNextChain() { // System.out.println("Get next chain"); // collect key tips of all controls in the relevant popup // panel List popups = PopupPanelManager.defaultManager() .getShownPath(); if (popups.size() > 0) { PopupInfo last = popups.get(popups.size() - 1); // if (last.getPopupOriginator() == cb) { JPopupPanel popupPanel = last.getPopupPanel(); // special case - application menu if (popupPanel instanceof JRibbonApplicationMenuPopupPanel) { JRibbonApplicationMenuPopupPanel appMenuPopupPanel = (JRibbonApplicationMenuPopupPanel) popupPanel; // check whether there are entries at level 2 JPanel level1 = appMenuPopupPanel.getPanelLevel1(); JPanel level2 = appMenuPopupPanel.getPanelLevel2(); if (level2.getComponentCount() > 0) { KeyTipChain chain = new KeyTipChain(level2); populateChain(level2, chain); chain.parent = link.traversal; return chain; } else { KeyTipChain chain = new KeyTipChain(level1); populateChain(level1, chain); chain.parent = link.traversal; return chain; } } else { KeyTipChain chain = new KeyTipChain(popupPanel); populateChain(last.getPopupPanel(), chain); chain.parent = link.traversal; return chain; } // popupPanel.putClientProperty(KEYTIP_MANAGER, // KeyTipManager.this); // } } return null; } }; return link; } return null; } public void handleKeyPress(char keyChar) { this.processingQueue.add(keyChar); } private class ProcessingThread extends Thread { public ProcessingThread() { super(); this.setName("KeyTipManager processing thread"); this.setDaemon(true); } @Override public void run() { while (true) { try { final char keyChar = processingQueue.take(); SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { processNextKeyPress(keyChar); } }); } catch (Throwable t) { t.printStackTrace(); } } } } private void processNextKeyPress(char keyChar) { if (this.keyTipChains.isEmpty()) return; KeyTipChain currChain = this.keyTipChains .get(this.keyTipChains.size() - 1); // go over the key tip links and see if there is an exact match for (final KeyTipLink link : currChain.links) { String keyTipString = link.keyTipString; if ((Character.toLowerCase(keyTipString .charAt(currChain.keyTipLookupIndex)) == Character .toLowerCase(keyChar)) && (keyTipString.length() == (currChain.keyTipLookupIndex + 1))) { // exact match if (link.enabled) { link.onActivated.actionPerformed(new ActionEvent(link.comp, ActionEvent.ACTION_PERFORMED, "keyTipActivated")); if (link.traversal != null) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final KeyTipChain next = link.traversal .getNextChain(); if (next != null) { KeyTipChain prev = (keyTipChains.isEmpty() ? null : keyTipChains.get(keyTipChains .size() - 1)); keyTipChains.add(next); repaintWindows(); if (prev != null) { // force repaint of all menu buttons for (KeyTipLink link : prev.links) { if (link.comp instanceof JCommandMenuButton) link.comp.repaint(); } } } } }); } else { // match found and activated, and no further // traversal - dismiss all key tip chains hideAllKeyTips(); } } return; } } // go over the key tip links and look for key tips that have // the specified character as the prefix if (currChain.keyTipLookupIndex == 0) { KeyTipChain secondary = new KeyTipChain( currChain.chainParentComponent); secondary.keyTipLookupIndex = 1; for (KeyTipLink link : currChain.links) { String keyTipString = link.keyTipString; if ((Character.toLowerCase(keyTipString .charAt(currChain.keyTipLookupIndex)) == Character .toLowerCase(keyChar)) && (keyTipString.length() == 2)) { KeyTipLink secondaryLink = new KeyTipLink(); secondaryLink.comp = link.comp; secondaryLink.enabled = link.enabled; secondaryLink.keyTipString = link.keyTipString; secondaryLink.onActivated = link.onActivated; secondaryLink.prefAnchorPoint = link.prefAnchorPoint; secondaryLink.traversal = link.traversal; secondary.addLink(secondaryLink); } } if (secondary.links.size() > 0) { this.keyTipChains.add(secondary); } repaintWindows(); return; } } private void repaintWindows() { for (Window window : Window.getWindows()) { window.repaint(); } List popups = PopupPanelManager.defaultManager() .getShownPath(); for (PopupPanelManager.PopupInfo popup : popups) { JPopupPanel popupPanel = popup.getPopupPanel(); popupPanel.paintImmediately(new Rectangle(0, 0, popupPanel .getWidth(), popupPanel.getHeight())); } } public void addKeyTipListener(KeyTipListener keyTipListener) { this.listenerList.add(KeyTipListener.class, keyTipListener); } public void removeKeyTipListener(KeyTipListener keyTipListener) { this.listenerList.remove(KeyTipListener.class, keyTipListener); } protected void fireKeyTipsShown(JRibbonFrame ribbonFrame) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); KeyTipEvent e = new KeyTipEvent(ribbonFrame, 0); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == KeyTipListener.class) { ((KeyTipListener) listeners[i + 1]).keyTipsShown(e); } } } protected void fireKeyTipsHidden(JRibbonFrame ribbonFrame) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); KeyTipEvent e = new KeyTipEvent(ribbonFrame, 0); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == KeyTipListener.class) { ((KeyTipListener) listeners[i + 1]).keyTipsHidden(e); } } } public void refreshCurrentChain() { KeyTipChain curr = this.keyTipChains.get(this.keyTipChains.size() - 1); if (curr.parent == null) return; KeyTipChain refreshed = curr.parent.getNextChain(); this.keyTipChains.remove(this.keyTipChains.size() - 1); this.keyTipChains.add(refreshed); repaintWindows(); } }src/org/pushingpixels/flamingo/internal/utils/AbstractFilter.java0000644000175000017500000001352411401230444024424 0ustar tonytony/* * $Id: AbstractFilter.java 657 2010-05-29 02:06:24Z kirillcool $ * * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). * * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * Copyright (c) 2006 Romain Guy * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.*; /** *

* Provides an abstract implementation of the BufferedImageOp * interface. This class can be used to created new image filters based on * BufferedImageOp. *

* * @author Romain Guy */ public abstract class AbstractFilter implements BufferedImageOp { /* * (non-Javadoc) * * @see * java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) */ public Rectangle2D getBounds2D(BufferedImage src) { return new Rectangle(0, 0, src.getWidth(), src.getHeight()); } /* * (non-Javadoc) * * @see * java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image * .BufferedImage, java.awt.image.ColorModel) */ public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) { if (destCM == null) { destCM = src.getColorModel(); } return new BufferedImage(destCM, destCM.createCompatibleWritableRaster( src.getWidth(), src.getHeight()), destCM.isAlphaPremultiplied(), null); } /* * (non-Javadoc) * * @see java.awt.image.BufferedImageOp#getPoint2D(java.awt.geom.Point2D, * java.awt.geom.Point2D) */ public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { return (Point2D) srcPt.clone(); } /* * (non-Javadoc) * * @see java.awt.image.BufferedImageOp#getRenderingHints() */ public RenderingHints getRenderingHints() { return null; } /** * Returns an array of integer pixels in the default RGB color model * (TYPE_INT_ARGB) and default sRGB color space, from a portion of the image * data. * * @param img * Image. * @param x * The starting X coordinate * @param y * The starting Y coordinate * @param w * Width of region. * @param h * Height of region. * @param pixels * If not null, the pixels are written here. * @return Array or RGB pixels. */ protected int[] getPixels(BufferedImage img, int x, int y, int w, int h, int[] pixels) { if (w == 0 || h == 0) { return new int[0]; } if (pixels == null) { pixels = new int[w * h]; } else if (pixels.length < w * h) { throw new IllegalArgumentException( "pixels array must have a length" + " >= w*h"); } int imageType = img.getType(); if (imageType == BufferedImage.TYPE_INT_ARGB || imageType == BufferedImage.TYPE_INT_RGB) { Raster raster = img.getRaster(); return (int[]) raster.getDataElements(x, y, w, h, pixels); } // Unmanages the image return img.getRGB(x, y, w, h, pixels, 0, w); } /** *

* Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on an image of type * different from BufferedImage.TYPE_INT_ARGB and * BufferedImage.TYPE_INT_RGB will unmanage the image. *

* * @param img * the destination image * @param x * the x location at which to start storing pixels * @param y * the y location at which to start storing pixels * @param w * the width of the rectangle of pixels to store * @param h * the height of the rectangle of pixels to store * @param pixels * an array of pixels, stored as integers * @throws IllegalArgumentException * is pixels is non-null and of length < w*h */ protected void setPixels(BufferedImage img, int x, int y, int w, int h, int[] pixels) { if (pixels == null || w == 0 || h == 0) { return; } else if (pixels.length < w * h) { throw new IllegalArgumentException( "pixels array must have a length" + " >= w*h"); } int imageType = img.getType(); if (imageType == BufferedImage.TYPE_INT_ARGB || imageType == BufferedImage.TYPE_INT_RGB) { WritableRaster raster = img.getRaster(); raster.setDataElements(x, y, w, h, pixels); } else { // Unmanages the image img.setRGB(x, y, w, h, pixels, 0, w); } } } src/org/pushingpixels/flamingo/internal/utils/ButtonSizingUtils.java0000644000175000017500000001042611401230444025171 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.*; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; public class ButtonSizingUtils { private static ButtonSizingUtils instance; private Insets outsets; private Insets toggleOutsets; public static synchronized ButtonSizingUtils getInstance() { if (instance == null) instance = new ButtonSizingUtils(); return instance; } private ButtonSizingUtils() { this.outsets = this.syncOutsets(new JButton("")); this.toggleOutsets = this.syncOutsets(new JToggleButton("")); UIManager.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("lookAndFeel".equals(evt.getPropertyName())) { outsets = syncOutsets(new JButton("")); toggleOutsets = syncOutsets(new JToggleButton("")); } } }); } private Insets syncOutsets(AbstractButton renderer) { JPanel panel = new JPanel(null); renderer.putClientProperty("JButton.buttonStyle", "square"); renderer.setFocusable(false); renderer.setOpaque(false); panel.add(renderer); renderer.setBounds(0, 0, 100, 50); GraphicsEnvironment e = GraphicsEnvironment .getLocalGraphicsEnvironment(); GraphicsDevice d = e.getDefaultScreenDevice(); GraphicsConfiguration c = d.getDefaultConfiguration(); BufferedImage compatibleImage = c.createCompatibleImage(100, 50, Transparency.TRANSLUCENT); renderer.paint(compatibleImage.getGraphics()); // analyze top int top = 0; for (int i = 0; i < 25; i++) { int rgba = compatibleImage.getRGB(50, i); int alpha = (rgba >>> 24) & 0xFF; if (alpha == 255) { top = i; break; } } // analyze bottom int bottom = 0; for (int i = 49; i > 25; i--) { int rgba = compatibleImage.getRGB(50, i); int alpha = (rgba >>> 24) & 0xFF; if (alpha == 255) { bottom = 49 - i; break; } } // analyze left int left = 0; for (int i = 0; i < 50; i++) { int rgba = compatibleImage.getRGB(i, 25); int alpha = (rgba >>> 24) & 0xFF; if (alpha == 255) { left = i; break; } } // analyze right int right = 0; for (int i = 99; i > 50; i--) { int rgba = compatibleImage.getRGB(i, 25); int alpha = (rgba >>> 24) & 0xFF; if (alpha == 255) { right = 99 - i; break; } } return new Insets(top, left, bottom, right); } public Insets getOutsets() { return new Insets(outsets.top, outsets.left, outsets.bottom, outsets.right); } public Insets getToggleOutsets() { return new Insets(toggleOutsets.top, toggleOutsets.left, toggleOutsets.bottom, toggleOutsets.right); } } src/org/pushingpixels/flamingo/internal/utils/DoubleArrowResizableIcon.java0000644000175000017500000001303511401230444026407 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.*; import java.awt.geom.GeneralPath; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; /** * Helper implementation of {@link ResizableIcon} that draws a double arrow. * * @author Kirill Grouchnikov */ public class DoubleArrowResizableIcon implements ResizableIcon { /** * Initial dimension. */ private Dimension initialDim; /** * The width of the rendered image. */ protected int width; /** * The height of the rendered image. */ protected int height; /** * Arrow direction. One of {@link SwingConstants#SOUTH}, * {@link SwingConstants#NORTH}, {@link SwingConstants#EAST} or * {@link SwingConstants#WEST}. */ protected int direction; /** * Creates a new double arrow resizable icon. * * @param initialDim * Initial icon dimension. * @param direction * Arrow direction. Currently only {@link SwingConstants#SOUTH} * is supported. */ public DoubleArrowResizableIcon(Dimension initialDim, int direction) { this.initialDim = initialDim; this.width = initialDim.width; this.height = initialDim.height; this.direction = direction; } /** * Creates a new double arrow resizable icon. * * @param initialDim * Initial icon dimension. * @param direction * Arrow direction. Currently only {@link SwingConstants#SOUTH} * is supported. */ public DoubleArrowResizableIcon(int initialDim, int direction) { this(new Dimension(initialDim, initialDim), direction); } public void revertToOriginalDimension() { this.width = initialDim.width; this.height = initialDim.height; } public void setDimension(Dimension newDimension) { this.width = newDimension.width; this.height = newDimension.height; } public int getIconHeight() { return this.height; } public int getIconWidth() { return this.width; } protected boolean toPaintEnabled(Component c) { return c.isEnabled(); } public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D graphics = (Graphics2D) g.create(); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Color arrowColor = this.toPaintEnabled(c) ? Color.black : Color.gray; graphics.setColor(arrowColor); Stroke stroke = new BasicStroke(this.width / 8.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); graphics.setStroke(stroke); graphics.translate(x, y); GeneralPath gp = new GeneralPath(); int arrowHeight = height / 2; int arrowWidth = width / 2; int deltaH = (height + 2) / 3; int deltaW = (width + 2) / 3; switch (direction) { case SwingUtilities.NORTH: gp.moveTo(0, height - 1); gp.lineTo((float) 0.5 * (width - 1), height - 1 - arrowHeight); gp.lineTo(width - 1, height - 1); gp.moveTo(0, height - 1 - deltaH); gp.lineTo((float) 0.5 * (width - 1), height - 1 - arrowHeight - deltaH); gp.lineTo(width - 1, height - 1 - deltaH); break; case SwingUtilities.SOUTH: gp.moveTo(0, 0); gp.lineTo((float) 0.5 * (width - 1), arrowHeight); gp.lineTo(width - 1, 0); gp.moveTo(0, deltaH); gp.lineTo((float) 0.5 * (width - 1), arrowHeight + deltaH); gp.lineTo(width - 1, deltaH); break; case SwingUtilities.EAST: gp.moveTo(0, 0); gp.lineTo(arrowWidth, (float) 0.5 * (height - 1)); gp.lineTo(0, height - 1); gp.moveTo(deltaW, 0); gp.lineTo(arrowWidth + deltaW, (float) 0.5 * (height - 1)); gp.lineTo(deltaW, height - 1); break; case SwingUtilities.WEST: gp.moveTo(width - 1, 0); gp.lineTo(width - 1 - arrowWidth, (float) 0.5 * (height - 1)); gp.lineTo(width - 1, height - 1); gp.moveTo(width - 1 - deltaW, 0); gp.lineTo(width - 1 - arrowWidth - deltaW, (float) 0.5 * (height - 1)); gp.lineTo(width - 1 - deltaW, height - 1); break; } graphics.draw(gp); graphics.dispose(); } } src/org/pushingpixels/flamingo/internal/utils/KeyTipRenderingUtilities.java0000644000175000017500000001621311401230444026450 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.internal.utils; import java.awt.*; import java.awt.font.LineMetrics; import java.awt.geom.RoundRectangle2D; import java.util.Collection; import javax.swing.*; import org.pushingpixels.flamingo.api.common.CommandButtonLayoutManager; import org.pushingpixels.flamingo.api.common.JCommandMenuButton; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonPopupOrientationKind; public class KeyTipRenderingUtilities { private static int INSETS = 3; public static Dimension getPrefSize(FontMetrics fm, String keyTip) { int prefWidth = fm.stringWidth(keyTip) + 2 * INSETS + 1; int prefHeight = fm.getHeight() + INSETS - 1; return new Dimension(prefWidth, prefHeight); } public static void renderKeyTip(Graphics g, Container c, Rectangle rect, String keyTip, boolean toPaintEnabled) { CellRendererPane buttonRendererPane = new CellRendererPane(); JButton rendererButton = new JButton(""); rendererButton.setEnabled(toPaintEnabled); buttonRendererPane.setBounds(rect.x, rect.y, rect.width, rect.height); Graphics2D g2d = (Graphics2D) g.create(); g2d.setComposite(AlphaComposite.SrcOver.derive(toPaintEnabled ? 1.0f : 0.5f)); Shape clip = g2d.getClip(); RoundRectangle2D.Double roundRect = new RoundRectangle2D.Double(rect.x, rect.y, rect.width - 1, rect.height - 1, 6, 6); g2d.clip(roundRect); buttonRendererPane.paintComponent(g2d, rendererButton, c, rect.x - rect.width / 2, rect.y - rect.height / 2, 2 * rect.width, 2 * rect.height, true); g2d.setClip(clip); g2d.setColor(FlamingoUtilities.getBorderColor()); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.draw(roundRect); g2d.setColor(FlamingoUtilities.getColor(Color.black, "Button.foreground")); Font font = UIManager.getFont("Button.font"); font = font.deriveFont(font.getSize() + 1.0f); g2d.setFont(font); int strWidth = g2d.getFontMetrics().stringWidth(keyTip); //int strHeight = g2d.getFontMetrics().getHeight(); g2d.translate(rect.x, rect.y); LineMetrics lineMetrics = g2d.getFontMetrics().getLineMetrics(keyTip, g2d); int strHeight = (int)lineMetrics.getHeight(); g2d.drawString(keyTip, (rect.width - strWidth + 1) / 2, (rect.height + strHeight) / 2 - g2d.getFontMetrics().getDescent() + 1); g2d.dispose(); } public static void renderMenuButtonKeyTips(Graphics g, JCommandMenuButton menuButton, CommandButtonLayoutManager layoutManager) { Collection currLinks = KeyTipManager .defaultManager().getCurrentlyShownKeyTips(); if (currLinks == null) return; boolean found = false; for (KeyTipManager.KeyTipLink link : currLinks) { found = (link.comp == menuButton); if (found) break; } if (!found) return; // System.out.println("Painting key tip for " + menuButton.getText()); String actionKeyTip = menuButton.getActionKeyTip(); String popupKeyTip = menuButton.getPopupKeyTip(); CommandButtonLayoutManager.CommandButtonLayoutInfo layoutInfo = layoutManager .getLayoutInfo(menuButton, g); Point prefCenter = menuButton.getUI().getKeyTipAnchorCenterPoint(); if ((layoutInfo.iconRect.width > 0) && (actionKeyTip != null)) { Dimension pref = KeyTipRenderingUtilities.getPrefSize(g .getFontMetrics(), actionKeyTip); KeyTipRenderingUtilities.renderKeyTip(g, menuButton, new Rectangle( prefCenter.x - pref.width / 2, Math.min(prefCenter.y - pref.height / 2, layoutInfo.actionClickArea.y + layoutInfo.actionClickArea.height - pref.height), pref.width, pref.height), actionKeyTip, menuButton .getActionModel().isEnabled()); } if ((layoutInfo.popupClickArea.width > 0) && (popupKeyTip != null)) { Dimension pref = KeyTipRenderingUtilities.getPrefSize(g .getFontMetrics(), popupKeyTip); if (menuButton.getPopupOrientationKind() == CommandButtonPopupOrientationKind.SIDEWARD) { if (menuButton.getCommandButtonKind() != CommandButtonKind.POPUP_ONLY) { // vertically aligned with the action keytip along // the right edge KeyTipRenderingUtilities.renderKeyTip(g, menuButton, new Rectangle(layoutInfo.popupClickArea.x + layoutInfo.popupClickArea.width - pref.width - 4, Math.min(prefCenter.y - pref.height / 2, layoutInfo.actionClickArea.y + layoutInfo.actionClickArea.height - pref.height), pref.width, pref.height), popupKeyTip, menuButton .getPopupModel().isEnabled()); } else { KeyTipRenderingUtilities .renderKeyTip( g, menuButton, new Rectangle( prefCenter.x - pref.width / 2, Math .min( prefCenter.y - pref.height / 2, layoutInfo.popupClickArea.y + layoutInfo.popupClickArea.height - pref.height), pref.width, pref.height), popupKeyTip, menuButton.getPopupModel() .isEnabled()); } } else { // horizontally centered along the bottom edge KeyTipRenderingUtilities .renderKeyTip( g, menuButton, new Rectangle( (layoutInfo.popupClickArea.x + layoutInfo.popupClickArea.width - pref.width) / 2, layoutInfo.popupClickArea.y + layoutInfo.popupClickArea.height - pref.height, pref.width, pref.height), popupKeyTip, menuButton .getPopupModel().isEnabled()); } } } } src/org/pushingpixels/flamingo/api/0000755000175000017500000000000011427105010016436 5ustar tonytonysrc/org/pushingpixels/flamingo/api/common/0000755000175000017500000000000011401230444017730 5ustar tonytonysrc/org/pushingpixels/flamingo/api/common/ProgressListener.java0000644000175000017500000000403011401230444024102 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.util.EventListener; /** * Contract for parties interested to listen on progress events. * * @author Kirill Grouchnikov * @see ProgressEvent */ public interface ProgressListener extends EventListener { /** * Fired when progress has been made in the source process. * * @param evt * Progress event. */ void onProgress(ProgressEvent evt); } src/org/pushingpixels/flamingo/api/common/AbstractFileViewPanel.java0000644000175000017500000002700411401230444024754 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.Dimension; import java.io.InputStream; import java.util.*; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.pushingpixels.flamingo.api.common.icon.EmptyResizableIcon; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; /** * Panel that hosts file-related command buttons with progress indication and * cancellation capabilities. * * @author Kirill Grouchnikov * @param * Type tag. */ public abstract class AbstractFileViewPanel extends JCommandButtonPanel { /** * Maps from file name to the buttons. */ protected Map buttonMap; /** * Progress listener to report back on loaded images. */ protected ProgressListener progressListener; /** * Contains the buttons with completely loaded images. */ protected Set loadedSet; /** * The main worker that loads the images off EDT. */ private SwingWorker mainWorker; /** * Information on the specific file. Depending on the actual type of the * file repository, the property map will have different keys. * * @author Kirill Grouchnikov */ public static class Leaf { /** * Leaf name. */ protected String leafName; /** * Stream with the contents of the leaf file. */ protected InputStream leafStream; /** * Leaf property map. */ protected Map leafProps; /** * Creates a new leaf. * * @param leafName * Leaf name. * @param leafStream * Stream with the contents of the leaf file. */ public Leaf(String leafName, InputStream leafStream) { this.leafName = leafName; this.leafStream = leafStream; this.leafProps = new HashMap(); } /** * Returns the leaf name. * * @return Leaf name. */ public String getLeafName() { return leafName; } /** * Returns the stream with the contents of the leaf file. * * @return Stream with the contents of the leaf file. */ public InputStream getLeafStream() { return leafStream; } /** * Returns the leaf property with the specified name. * * @param propName * Property name. * @return Leaf property with the specified name. */ public Object getLeafProp(String propName) { return this.leafProps.get(propName); } /** * Sets the leaf property with the specified name. * * @param propName * Property name. * @param propValue * Property value. */ public void setLeafProp(String propName, Object propValue) { this.leafProps.put(propName, propValue); } /** * Returns the map of all the properties of this leaf. * * @return Unmodifiable view of the map of all the properties of this * leaf. */ public Map getLeafProps() { return Collections.unmodifiableMap(this.leafProps); } } /** * Creates a new panel. * * @param startingDimension * Initial dimension for icons. * @param progressListener * Progress listener to report back on loaded icons. */ public AbstractFileViewPanel(int startingDimension, ProgressListener progressListener) { super(startingDimension); this.buttonMap = new HashMap(); this.progressListener = progressListener; this.loadedSet = new HashSet(); this.setToShowGroupLabels(false); } /** * Creates a new panel. * * @param startingState * Initial state for icons. * @param progressListener * Progress listener to report back on loaded icons. */ public AbstractFileViewPanel(CommandButtonDisplayState startingState, ProgressListener progressListener) { super(startingState); this.buttonMap = new HashMap(); this.progressListener = progressListener; this.loadedSet = new HashSet(); this.setToShowGroupLabels(false); } /** * Sets the current entries to show. The current contents of the panel are * discarded. For each matching entry determined by the * {@link #toShowFile(StringValuePair)} call, a new {@link JCommandButton} * hosting an the matching implementation of {@link ResizableIcon} is added * to the panel. * * @param leafs * Information on the entries to show in the panel. */ public void setFolder(final java.util.List> leafs) { this.removeAllGroups(); this.addButtonGroup(""); this.buttonMap.clear(); int fileCount = 0; final Map newButtons = new HashMap(); for (StringValuePair leaf : leafs) { String name = leaf.getKey(); if (!toShowFile(leaf)) continue; int initialSize = currDimension; if (initialSize < 0) initialSize = currState.getPreferredIconSize(); JCommandButton button = new JCommandButton(name, new EmptyResizableIcon(initialSize)); button.setHorizontalAlignment(SwingUtilities.LEFT); button.setDisplayState(this.currState); if (this.currState == CommandButtonDisplayState.FIT_TO_ICON) button.updateCustomDimension(currDimension); this.addButtonToLastGroup(button); newButtons.put(name, button); buttonMap.put(name, button); fileCount++; } this.doLayout(); this.repaint(); final int totalCount = fileCount; this.mainWorker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { if ((totalCount > 0) && (progressListener != null)) { progressListener.onProgress(new ProgressEvent( AbstractFileViewPanel.this, 0, totalCount, 0)); } for (final StringValuePair leafPair : leafs) { if (isCancelled()) break; final String name = leafPair.getKey(); if (!toShowFile(leafPair)) continue; InputStream stream = getLeafContent(leafPair.getValue()); Leaf leaf = new Leaf(name, stream); leaf.setLeafProp("source", leafPair.getValue()); for (Map.Entry propEntry : leafPair .getProps().entrySet()) { leaf.setLeafProp(propEntry.getKey(), propEntry .getValue()); } publish(leaf); } return null; } @Override protected void process(List leaves) { for (final Leaf leaf : leaves) { final String name = leaf.getLeafName(); InputStream stream = leaf.getLeafStream(); Dimension dim = new Dimension(currDimension, currDimension); final ResizableIcon icon = getResizableIcon(leaf, stream, currState, dim); if (icon == null) continue; final JCommandButton commandButton = newButtons.get(name); commandButton.setIcon(icon); if (icon instanceof AsynchronousLoading) { ((AsynchronousLoading) icon) .addAsynchronousLoadListener(new AsynchronousLoadListener() { public void completed(boolean success) { synchronized (AbstractFileViewPanel.this) { if (loadedSet .contains(commandButton)) return; loadedSet.add(commandButton); // loadedCount++; if (progressListener != null) { progressListener .onProgress(new ProgressEvent( AbstractFileViewPanel.this, 0, totalCount, loadedSet .size())); if (loadedSet.size() == totalCount) { progressListener .onProgress(new ProgressEvent( AbstractFileViewPanel.this, 0, totalCount, totalCount)); } } } } }); } configureCommandButton(leaf, commandButton, icon); commandButton.setDisplayState(currState); if (currState == CommandButtonDisplayState.FIT_TO_ICON) commandButton.updateCustomDimension(currDimension); } } }; mainWorker.execute(); } /** * Returns the number of loaded icons. * * @return The number of loaded icons. */ public int getLoadedIconCount() { return this.loadedSet.size(); } /** * Cancels the pending processing. */ public void cancelMainWorker() { if (this.mainWorker == null) return; if (this.mainWorker.isDone() || this.mainWorker.isCancelled()) return; this.mainWorker.cancel(false); } /** * Returns the button map. * * @return Unmodifiable view on the button map. */ public Map getButtonMap() { return Collections.unmodifiableMap(buttonMap); } /** * Returns indication whether the specified file should be shown on this * panel. * * @param pair * Information on the file. * @return true if the specified file should be shown on this * panel, false otherwise. */ protected abstract boolean toShowFile(StringValuePair pair); /** * Returns the icon for the specified parameters. * * @param leaf * Information on the file. * @param stream * Input stream with the file contents. * @param state * Icon state. * @param dimension * Icon dimension. * @return File icon. */ protected abstract ResizableIcon getResizableIcon(Leaf leaf, InputStream stream, CommandButtonDisplayState state, Dimension dimension); /** * Configures the specified command button. Can be used to wire additional * behavior, such as tooltips or action listeners if the specific view panel * implementation requires it. * * @param leaf * Information on the file "behind" the button. * @param button * Button to configure. * @param icon * Button icon. */ protected abstract void configureCommandButton(Leaf leaf, JCommandButton button, ResizableIcon icon); /** * Returns the input stream with the file contents. * * @param leaf * Leaf (file behind a command button on this panel). * @return Input stream with the file contents. */ protected abstract InputStream getLeafContent(T leaf); } src/org/pushingpixels/flamingo/api/common/RichTooltip.java0000644000175000017500000002265211401230444023042 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.Image; import java.util.*; /** * Rich tooltip for command buttons. * *

* In its most basic form, the rich tooltip has a title and one (possible * multiline) description text: *

* *
 * +--------------------------------+
 * | Title                          |
 * |        Some description text   |
 * +--------------------------------+
 * 
* *

* The {@link #addDescriptionSection(String)} can be used to add multiple * sections to the description: *

* *
 * +--------------------------------+
 * | Title                          |
 * |        First multiline         |
 * |        description section     |
 * |                                |
 * |        Second multiline        |
 * |        description section     |
 * |                                |
 * |        Third multiline         |
 * |        description section     |
 * +--------------------------------+
 * 
* *

* The {@link #setMainImage(Image)} can be used to place an image below the * title and to the left of the description sections: *

* *
 * +--------------------------------+
 * | Title                          |
 * | *******  First multiline       |
 * | *image*  description section   |
 * | *******                        |
 * |          Second multiline      |
 * |          description section   |
 * +--------------------------------+
 * 
* *

* The {@link #addFooterSection(String)} can be used to add (possibly) multiple * footer sections that will be shown below a horizontal separator: *

* *
 * +--------------------------------+
 * | Title                          |
 * |        First multiline         |
 * |        description section     |
 * |                                |
 * |        Second multiline        |
 * |        description section     |
 * |--------------------------------|
 * | A multiline footer section     |
 * | placed below a separator       |
 * +--------------------------------+
 * 
* *

* The {@link #setFooterImage(Image)} can be used to place an image to the left * of the footer sections: *

* *
 * +--------------------------------+
 * | Title                          |
 * |        First multiline         |
 * |        description section     |
 * |                                |
 * |        Second multiline        |
 * |        description section     |
 * |--------------------------------|
 * | *******  A multiline           |
 * | *image*  footer section        |
 * | *******                        |
 * +--------------------------------+
 * 
* *

* Here is a fully fledged rich tooltip that shows all these APIs in action: *

* *
 * +--------------------------------+
 * | Title                          |
 * | *******  First multiline       |
 * | *image*  description section   |
 * | *******                        |
 * |          Second multiline      |
 * |          description section   |
 * |--------------------------------|
 * | *******  First multiline       |
 * | *image*  footer section        |
 * | *******                        |
 * |          Second multiline      |
 * |          footer section        |
 * +--------------------------------+
 * 
* * @author Kirill Grouchnikov */ public class RichTooltip { /** * The main title of this tooltip. * * @see #RichTooltip(String, String) * @see #setTitle(String) * @see #getTitle() */ protected String title; /** * The main image of this tooltip. Can be null. * * @see #getMainImage() * @see #setMainImage(Image) */ protected Image mainImage; /** * The description sections of this tooltip. * * @see #RichTooltip(String, String) * @see #addDescriptionSection(String) * @see #getDescriptionSections() */ protected List descriptionSections; /** * The footer image of this tooltip. Can be null. * * @see #getFooterImage() * @see #setFooterImage(Image) */ protected Image footerImage; /** * The footer sections of this tooltip. Can be empty. * * @see #addFooterSection(String) * @see #getFooterSections() */ protected List footerSections; /** * Creates an empty tooltip. */ public RichTooltip() { } /** * Creates a tooltip with the specified title and description section. * * @param title * Tooltip title. * @param descriptionSection * Tooltip main description section. */ public RichTooltip(String title, String descriptionSection) { this.setTitle(title); this.addDescriptionSection(descriptionSection); } /** * Sets the title for this tooltip. * * @param title * The new tooltip title. */ public void setTitle(String title) { this.title = title; } /** * Sets the main image for this tooltip. * * @param image * The main image for this tooltip. * @see #getMainImage() * @see #addDescriptionSection(String) */ public void setMainImage(Image image) { this.mainImage = image; } /** * Adds the specified description section to this tooltip. * * @param section * The description section to add. * @see #getDescriptionSections() * @see #setMainImage(Image) * @see #setTitle(String) */ public void addDescriptionSection(String section) { if (this.descriptionSections == null) { this.descriptionSections = new LinkedList(); } this.descriptionSections.add(section); } /** * Sets the footer image for this tooltip. * * @param image * The footer image for this tooltip. * @see #getFooterImage() * @see #addFooterSection(String) */ public void setFooterImage(Image image) { this.footerImage = image; } /** * Adds the specified footer section to this tooltip. * * @param section * The footer section to add. * @see #getFooterSections() * @see #setFooterImage(Image) */ public void addFooterSection(String section) { if (this.footerSections == null) { this.footerSections = new LinkedList(); } this.footerSections.add(section); } /** * Returns the main title of this tooltip. * * @return The main title of this tooltip. * @see #RichTooltip(String, String) * @see #setTitle(String) */ public String getTitle() { return this.title; } /** * Returns the main image of this tooltip. Can return null. * * @return The main image of this tooltip. * @see #setMainImage(Image) * @see #getDescriptionSections() */ public Image getMainImage() { return this.mainImage; } /** * Returns an unmodifiable list of description sections of this tooltip. * Guaranteed to return a non-null list. * * @return An unmodifiable list of description sections of this tooltip. * @see #RichTooltip(String, String) * @see #addDescriptionSection(String) * @see #getTitle() * @see #getMainImage() */ @SuppressWarnings("unchecked") public List getDescriptionSections() { if (this.descriptionSections == null) return Collections.EMPTY_LIST; return Collections.unmodifiableList(this.descriptionSections); } /** * Returns the footer image of this tooltip. Can return null. * * @return The footer image of this tooltip. * @see #setFooterImage(Image) * @see #getFooterSections() */ public Image getFooterImage() { return this.footerImage; } /** * Returns an unmodifiable list of footer sections of this tooltip. * Guaranteed to return a non-null list. * * @return An unmodifiable list of footer sections of this tooltip. * @see #addFooterSection(String) * @see #getFooterImage() */ @SuppressWarnings("unchecked") public List getFooterSections() { if (this.footerSections == null) return Collections.EMPTY_LIST; return Collections.unmodifiableList(this.footerSections); } } src/org/pushingpixels/flamingo/api/common/StringValuePair.java0000644000175000017500000000404111401230444023651 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; /** * Key-value pair with a {@link String} key. * * @author Kirill Grouchnikov * @param * Value class. */ public class StringValuePair extends KeyValuePair { /** * Creates a new pair. * * @param key * Pair key. * @param value * Pair value. */ public StringValuePair(String key, T value) { super(key, value); } } src/org/pushingpixels/flamingo/api/common/RolloverActionListener.java0000644000175000017500000000365511401230444025254 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.event.ActionListener; /** * Rollover action listener. Is used to associate application logic with * rollover of {@link JCommandMenuButton} component. * * @author Kirill Grouchnikov */ public interface RolloverActionListener extends ActionListener { } src/org/pushingpixels/flamingo/api/common/AsynchronousLoading.java0000644000175000017500000000533311401230444024570 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.util.concurrent.ExecutorService; import org.pushingpixels.flamingo.api.svg.SvgBatikResizableIcon; /** * This interface is used for asynchronously-loaded contents. For example, the * {@link SvgBatikResizableIcon} uses the {@link ExecutorService} to load the * SVG image in the background. When the image is loaded, the component that * contains this image ({@link JCommandButton} for example) is notified to * repaint itself. * * @author Kirill Grouchnikov. */ public interface AsynchronousLoading { /** * Adds listener on the asynchronous loading events. * * @param l * Listener to add. */ public void addAsynchronousLoadListener(AsynchronousLoadListener l); /** * Removes listener on the asynchronous loading events. * * @param l * Listener to remove. */ public void removeAsynchronousLoadListener(AsynchronousLoadListener l); /** * Returns indication whether the content is still loading. * * @return true if the content is still loading, * false otherwise. */ public boolean isLoading(); } src/org/pushingpixels/flamingo/api/common/RichToolTipManager.java0000644000175000017500000002671611401230444024302 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.*; import java.awt.event.*; import java.util.List; import javax.swing.*; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand; import org.pushingpixels.flamingo.internal.ui.common.JRichTooltipPanel; public class RichToolTipManager extends MouseAdapter implements MouseMotionListener { private Timer initialDelayTimer; private Timer dismissTimer; private RichTooltip richTooltip; private JTrackableComponent insideComponent; private MouseEvent mouseEvent; final static RichToolTipManager sharedInstance = new RichToolTipManager(); private Popup tipWindow; private JRichTooltipPanel tip; private boolean tipShowing = false; private static final String TRACKED_FOR_RICH_TOOLTIP = "flamingo.internal.trackedForRichTooltip"; public static abstract class JTrackableComponent extends JComponent { public abstract RichTooltip getRichTooltip(MouseEvent mouseEvent); } RichToolTipManager() { initialDelayTimer = new Timer(750, new InitialDelayTimerAction()); initialDelayTimer.setRepeats(false); dismissTimer = new Timer(20000, new DismissTimerAction()); dismissTimer.setRepeats(false); } /** * Specifies the initial delay value. * * @param milliseconds * the number of milliseconds to delay (after the cursor has * paused) before displaying the tooltip * @see #getInitialDelay */ public void setInitialDelay(int milliseconds) { initialDelayTimer.setInitialDelay(milliseconds); } /** * Returns the initial delay value. * * @return an integer representing the initial delay value, in milliseconds * @see #setInitialDelay */ public int getInitialDelay() { return initialDelayTimer.getInitialDelay(); } /** * Specifies the dismissal delay value. * * @param milliseconds * the number of milliseconds to delay before taking away the * tooltip * @see #getDismissDelay */ public void setDismissDelay(int milliseconds) { dismissTimer.setInitialDelay(milliseconds); } /** * Returns the dismissal delay value. * * @return an integer representing the dismissal delay value, in * milliseconds * @see #setDismissDelay */ public int getDismissDelay() { return dismissTimer.getInitialDelay(); } void showTipWindow(MouseEvent mouseEvent) { if (insideComponent == null || !insideComponent.isShowing()) return; Dimension size; Point screenLocation = insideComponent.getLocationOnScreen(); Point location = new Point(); GraphicsConfiguration gc; gc = insideComponent.getGraphicsConfiguration(); Rectangle sBounds = gc.getBounds(); Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc); // Take into account screen insets, decrease viewport sBounds.x += screenInsets.left; sBounds.y += screenInsets.top; sBounds.width -= (screenInsets.left + screenInsets.right); sBounds.height -= (screenInsets.top + screenInsets.bottom); // Just to be paranoid hideTipWindow(); tip = new JRichTooltipPanel(insideComponent.getRichTooltip(mouseEvent)); tip .applyComponentOrientation(insideComponent .getComponentOrientation()); size = tip.getPreferredSize(); AbstractRibbonBand ribbonBand = (AbstractRibbonBand) SwingUtilities .getAncestorOfClass(AbstractRibbonBand.class, insideComponent); boolean ltr = tip.getComponentOrientation().isLeftToRight(); boolean isInRibbonBand = (ribbonBand != null); if (isInRibbonBand) { // display directly below or above ribbon band location.x = ltr ? screenLocation.x : screenLocation.x + insideComponent.getWidth() - size.width; Point bandLocationOnScreen = ribbonBand.getLocationOnScreen(); location.y = bandLocationOnScreen.y + ribbonBand.getHeight() + 4; if ((location.y + size.height) > (sBounds.y + sBounds.height)) { location.y = bandLocationOnScreen.y - size.height; } } else { // display directly below or above it location.x = ltr ? screenLocation.x : screenLocation.x + insideComponent.getWidth() - size.width; location.y = screenLocation.y + insideComponent.getHeight(); if ((location.y + size.height) > (sBounds.y + sBounds.height)) { location.y = screenLocation.y - size.height; } } // Tweak the X location to not overflow the screen if (location.x < sBounds.x) { location.x = sBounds.x; } else if (location.x - sBounds.x + size.width > sBounds.width) { location.x = sBounds.x + Math.max(0, sBounds.width - size.width); } PopupFactory popupFactory = PopupFactory.getSharedInstance(); tipWindow = popupFactory.getPopup(insideComponent, tip, location.x, location.y); tipWindow.show(); dismissTimer.start(); tipShowing = true; } void hideTipWindow() { if (tipWindow != null) { tipWindow.hide(); tipWindow = null; tipShowing = false; tip = null; dismissTimer.stop(); } } /** * Returns a shared ToolTipManager instance. * * @return a shared ToolTipManager object */ public static RichToolTipManager sharedInstance() { return sharedInstance; } /** * Registers a component for tooltip management. *

* This will register key bindings to show and hide the tooltip text only if * component has focus bindings. This is done so that * components that are not normally focus traversable, such as * JLabel, are not made focus traversable as a result of * invoking this method. * * @param comp * a JComponent object to add * @see JComponent#isFocusTraversable */ public void registerComponent(JTrackableComponent comp) { if (Boolean.TRUE.equals(comp .getClientProperty(TRACKED_FOR_RICH_TOOLTIP))) return; comp.addMouseListener(this); // commandButton.addMouseMotionListener(moveBeforeEnterListener); comp.putClientProperty(TRACKED_FOR_RICH_TOOLTIP, Boolean.TRUE); } /** * Removes a component from tooltip control. * * @param comp * a JComponent object to remove */ public void unregisterComponent(JTrackableComponent comp) { comp.removeMouseListener(this); comp.putClientProperty(TRACKED_FOR_RICH_TOOLTIP, null); } @Override public void mouseEntered(MouseEvent event) { initiateToolTip(event); } private void initiateToolTip(MouseEvent event) { JTrackableComponent component = (JTrackableComponent) event.getSource(); // component.removeMouseMotionListener(moveBeforeEnterListener); Point location = event.getPoint(); // ensure tooltip shows only in proper place if (location.x < 0 || location.x >= component.getWidth() || location.y < 0 || location.y >= component.getHeight()) { return; } // do not show tooltips on components in popup panels that are not // in the last shown one List popups = PopupPanelManager .defaultManager().getShownPath(); if (popups.size() > 0) { JPopupPanel popupPanel = popups.get(popups.size() - 1) .getPopupPanel(); boolean ignore = true; Component c = component; while (c != null) { if (c == popupPanel) { ignore = false; break; } c = c.getParent(); } if (ignore) return; } if (insideComponent != null) { initialDelayTimer.stop(); } // A component in an unactive internal frame is sent two // mouseEntered events, make sure we don't end up adding // ourselves an extra time. component.removeMouseMotionListener(this); component.addMouseMotionListener(this); insideComponent = component; mouseEvent = event; initialDelayTimer.start(); } @Override public void mouseExited(MouseEvent event) { initialDelayTimer.stop(); if (insideComponent != null) { insideComponent.removeMouseMotionListener(this); } insideComponent = null; richTooltip = null; mouseEvent = null; hideTipWindow(); } @Override public void mousePressed(MouseEvent event) { hideTipWindow(); initialDelayTimer.stop(); insideComponent = null; mouseEvent = null; } @Override public void mouseDragged(MouseEvent event) { } @Override public void mouseMoved(MouseEvent event) { if (tipShowing) { checkForTipChange(event); } else { // Lazily lookup the values from within insideTimerAction insideComponent = (JTrackableComponent) event.getSource(); mouseEvent = event; richTooltip = null; initialDelayTimer.restart(); } } private void checkForTipChange(MouseEvent event) { JTrackableComponent component = (JTrackableComponent) event.getSource(); RichTooltip newTooltip = component.getRichTooltip(event); // is it different? boolean isDifferent = (richTooltip != newTooltip); if (isDifferent) { hideTipWindow(); if (newTooltip != null) { richTooltip = newTooltip; initialDelayTimer.restart(); } } } protected class InitialDelayTimerAction implements ActionListener { public void actionPerformed(ActionEvent e) { if (insideComponent != null && insideComponent.isShowing()) { // Lazy lookup if (richTooltip == null && mouseEvent != null) { richTooltip = insideComponent.getRichTooltip(mouseEvent); } if (richTooltip != null) { boolean showRichTooltip = true; // check that no visible popup is originating in this // component for (PopupPanelManager.PopupInfo pi : PopupPanelManager .defaultManager().getShownPath()) { if (pi.getPopupOriginator() == insideComponent) { showRichTooltip = false; break; } } if (showRichTooltip) { showTipWindow(mouseEvent); } } else { insideComponent = null; richTooltip = null; mouseEvent = null; hideTipWindow(); } } } } protected class DismissTimerAction implements ActionListener { public void actionPerformed(ActionEvent e) { hideTipWindow(); initialDelayTimer.stop(); insideComponent = null; mouseEvent = null; } } } src/org/pushingpixels/flamingo/api/common/JCommandMenuButton.java0000644000175000017500000001106211401230444024304 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.event.ActionEvent; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.popup.JCommandPopupMenu; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandMenuButtonUI; import org.pushingpixels.flamingo.internal.ui.common.CommandButtonUI; /** * A command button that can be placed in {@link JCommandPopupMenu}s and in the * primary / secondary panels of the ribbon application menu. * * @author Kirill Grouchnikov * @see JCommandPopupMenu#addMenuButton(JCommandMenuButton) */ public class JCommandMenuButton extends JCommandButton { /** * The UI class ID string. */ public static final String uiClassID = "CommandMenuButtonUI"; /** * Creates a new command menu button. * * @param title * Command menu button title. * @param icon * Command menu button icon. */ public JCommandMenuButton(String title, ResizableIcon icon) { super(title, icon); } /** * Adds a rollover action listener that will be called when the rollover * state of this button becomes active. * * @param l * The rollover action listener to add. * @see #removeRolloverActionListener(RolloverActionListener) */ public void addRolloverActionListener(RolloverActionListener l) { this.listenerList.add(RolloverActionListener.class, l); } /** * Removes the specified rollover action listener. * * @param l * The listener to remove. * @see #addRolloverActionListener(RolloverActionListener) */ public void removeRolloverActionListener(RolloverActionListener l) { this.listenerList.remove(RolloverActionListener.class, l); } /* * (non-Javadoc) * * @see javax.swing.JComponent#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /* * (non-Javadoc) * * @see javax.swing.JComponent#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((CommandButtonUI) UIManager.getUI(this)); } else { setUI(BasicCommandMenuButtonUI.createUI(this)); } } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.JCommandButton#canHaveBothKeyTips() */ @Override boolean canHaveBothKeyTips() { return true; } /** * Programmatically perform a "rollover" on the action area. This does the * same thing as if the user had moved the mouse over the action area of the * button. */ public void doActionRollover() { ActionEvent ae = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, this.getActionModel().getActionCommand()); // Guaranteed to return a non-null array RolloverActionListener[] listeners = this .getListeners(RolloverActionListener.class); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 1; i >= 0; i--) { (listeners[i]).actionPerformed(ae); } } } src/org/pushingpixels/flamingo/api/common/model/0000755000175000017500000000000011401230444021030 5ustar tonytonysrc/org/pushingpixels/flamingo/api/common/model/ActionButtonModel.java0000644000175000017500000000525011401230444025267 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.model; import javax.swing.ButtonModel; import org.pushingpixels.flamingo.api.common.AbstractCommandButton; /** * Model for the action area of {@link AbstractCommandButton} component. * * @author Kirill Grouchnikov */ public interface ActionButtonModel extends ButtonModel { /** * Sets indication whether the associated actions should be fired on mouse * press instead of mouse release. * * @param toFireActionOnPress * if true, the associated actions will be fired on * mouse press, otherwise the associated actions will be fired on * mouse release. */ public void setFireActionOnPress(boolean toFireActionOnPress); /** * Returns indication whether the associated actions should be fired on * mouse press instead of mouse release. * * @return true if the associated actions are fired on mouse * press, false if the associated actions are fired on * mouse release. */ public boolean isFireActionOnPress(); }src/org/pushingpixels/flamingo/api/common/model/ActionToggleButtonModel.java0000644000175000017500000001002011401230444026420 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.model; import java.awt.AWTEvent; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import javax.swing.JToggleButton.ToggleButtonModel; import org.pushingpixels.flamingo.api.common.JCommandToggleButton; /** * Extension of the default toggle button model that supports the * {@link ActionButtonModel} interface. This is the default core action model * set on {@link JCommandToggleButton}s. * * @author Kirill Grouchnikov */ public class ActionToggleButtonModel extends ToggleButtonModel implements ActionButtonModel { /** * Indication whether the action is fired on mouse press (as opposed to * mouse release). */ protected boolean toFireActionOnPress; /** * Creates a new model. * * @param toFireActionOnPress * If true, the action will be fired on mouse press, * if false, the action will be fired on mouse * release. */ public ActionToggleButtonModel(boolean toFireActionOnPress) { this.toFireActionOnPress = toFireActionOnPress; } @Override public boolean isFireActionOnPress() { return this.toFireActionOnPress; } @Override public void setFireActionOnPress(boolean toFireActionOnPress) { this.toFireActionOnPress = toFireActionOnPress; } @Override public void setPressed(boolean b) { if ((isPressed() == b) || !isEnabled()) { return; } if (isArmed()) { // change selection prior to firing the action event if (!this.isFireActionOnPress()) { if (!b) { setSelected(!this.isSelected()); } } else { if (b) { setSelected(!this.isSelected()); } } } if (b) { stateMask |= PRESSED; } else { stateMask &= ~PRESSED; } fireStateChanged(); boolean toFireAction = false; if (this.isFireActionOnPress()) { toFireAction = isPressed() && isArmed(); } else { toFireAction = !isPressed() && isArmed(); } if (toFireAction) { int modifiers = 0; AWTEvent currentEvent = EventQueue.getCurrentEvent(); if (currentEvent instanceof InputEvent) { modifiers = ((InputEvent) currentEvent).getModifiers(); } else if (currentEvent instanceof ActionEvent) { modifiers = ((ActionEvent) currentEvent).getModifiers(); } fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand(), EventQueue.getMostRecentEventTime(), modifiers)); } } } src/org/pushingpixels/flamingo/api/common/model/ActionRepeatableButtonModel.java0000644000175000017500000001666011401230444027263 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.model; import java.awt.AWTEvent; import java.awt.EventQueue; import java.awt.event.*; import javax.swing.DefaultButtonModel; import javax.swing.Timer; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; /** * Extension of the default button model that supports the * {@link ActionButtonModel} interface and repeated invocation of action * listeners on mouse rollover. This is the default core action model set on * {@link JCommandButton}s. * * @author Kirill Grouchnikov */ public class ActionRepeatableButtonModel extends DefaultButtonModel implements ActionButtonModel { /** * The button behind the model. */ private JCommandButton commandButton; /** * Timer for the auto-repeat action mode. */ protected Timer autoRepeatTimer; /** * Indication whether the action is fired on mouse press (as opposed to * mouse release). */ protected boolean toFireActionOnPress; /** * Creates a new button model. * * @param commandButton * The associated command button. */ public ActionRepeatableButtonModel(JCommandButton commandButton) { super(); this.commandButton = commandButton; this.toFireActionOnPress = false; } /* * (non-Javadoc) * * @see javax.swing.DefaultButtonModel#setPressed(boolean) */ @Override public void setPressed(boolean b) { if ((isPressed() == b) || !isEnabled()) { return; } if (b) { stateMask |= PRESSED; } else { stateMask &= ~PRESSED; } boolean toFireFirstAction = isArmed(); // if the button is in auto-repeat action mode, the action // starts firing on press-down and not on press-up if (commandButton.isAutoRepeatAction() || isFireActionOnPress()) toFireFirstAction = isPressed() && toFireFirstAction; else toFireFirstAction = !isPressed() && toFireFirstAction; // no action on popup only command buttons if (commandButton.getCommandButtonKind() == CommandButtonKind.POPUP_ONLY) toFireFirstAction = false; if (this.commandButton.isFireActionOnRollover()) { // the action is invoked on rollover toFireFirstAction = false; } int modifiers = 0; AWTEvent currentEvent = EventQueue.getCurrentEvent(); if (currentEvent instanceof InputEvent) { modifiers = ((InputEvent) currentEvent).getModifiers(); } else if (currentEvent instanceof ActionEvent) { modifiers = ((ActionEvent) currentEvent).getModifiers(); } if (toFireFirstAction) { fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand(), EventQueue.getMostRecentEventTime(), modifiers)); if (commandButton.isAutoRepeatAction()) { // start timer this.startActionTimer(modifiers); } } // we need to stop timer when the non-action-on-rollover button // gets pressed=false and it is in auto-repeat mode if (!this.commandButton.isFireActionOnRollover()) { if (this.commandButton.isAutoRepeatAction() && !b) { this.stopActionTimer(); } } fireStateChanged(); } @Override public void setRollover(boolean b) { if ((isRollover() == b) || !isEnabled()) { return; } if (b) { stateMask |= ROLLOVER; } else { stateMask &= ~ROLLOVER; } if (this.commandButton.isFireActionOnRollover()) { if (b && !this.isActionTimerRunning() && (this.commandButton.getCommandButtonKind() != CommandButtonKind.POPUP_ONLY)) { // action-on-rollover non-popup-only button that gained // rollover and the action timer is not running - fire the // first event and start the action timer. int modifiers = 0; AWTEvent currentEvent = EventQueue.getCurrentEvent(); if (currentEvent instanceof InputEvent) { modifiers = ((InputEvent) currentEvent).getModifiers(); } else if (currentEvent instanceof ActionEvent) { modifiers = ((ActionEvent) currentEvent).getModifiers(); } fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand(), EventQueue.getMostRecentEventTime(), modifiers)); if (commandButton.isAutoRepeatAction()) { // start timer this.startActionTimer(modifiers); } } if (!b) { this.stopActionTimer(); } } fireStateChanged(); } /** * Stop the action timer. */ private void stopActionTimer() { if (this.autoRepeatTimer != null) this.autoRepeatTimer.stop(); } /** * Checks whether the action timer is running. * * @return true if the action timer is running, * false otherwise. */ private boolean isActionTimerRunning() { if (this.autoRepeatTimer == null) return false; return this.autoRepeatTimer.isRunning(); } /** * Starts the action timer, passing the specified modifiers to the action * event that will be fired in a loop. * * @param modifiers * Modifiers for the action event to be fired. */ private void startActionTimer(final int modifiers) { this.autoRepeatTimer = new Timer(this.commandButton .getAutoRepeatSubsequentInterval(), new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (!isEnabled() || !commandButton.isVisible() || !commandButton.isDisplayable()) { // stop the timer when the button becomes // disabled, invisible or undisplayable autoRepeatTimer.stop(); return; } fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand(), EventQueue.getMostRecentEventTime(), modifiers)); } }); this.autoRepeatTimer.setInitialDelay(this.commandButton .getAutoRepeatInitialInterval()); this.autoRepeatTimer.start(); } @Override public boolean isFireActionOnPress() { return this.toFireActionOnPress; } @Override public void setFireActionOnPress(boolean toFireActionOnPress) { this.toFireActionOnPress = toFireActionOnPress; } }src/org/pushingpixels/flamingo/api/common/model/PopupButtonModel.java0000644000175000017500000000536011401230444025157 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.model; import javax.swing.ButtonModel; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.common.PopupActionListener; /** * Model for the popup area of {@link JCommandButton} component. * * @author Kirill Grouchnikov */ public interface PopupButtonModel extends ButtonModel { /** * Adds an PopupActionListener to the model. * * @param l * the listener to add */ void addPopupActionListener(PopupActionListener l); /** * Removes an PopupActionListener from the model. * * @param l * the listener to remove */ void removePopupActionListener(PopupActionListener l); /** * Sets indication on the visibility status of the associated popup. * * @param flag * The visibility status of the associated popup. */ void setPopupShowing(boolean flag); /** * Returns indication whether the associated popup is showing. * * @return true if the associated popup is showing, * false otherwise. */ boolean isPopupShowing(); }src/org/pushingpixels/flamingo/api/common/popup/0000755000175000017500000000000011401230444021073 5ustar tonytonysrc/org/pushingpixels/flamingo/api/common/popup/JColorSelectorPopupMenu.java0000644000175000017500000002334011401230444026502 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.popup; import java.awt.*; import java.util.*; import java.util.List; import javax.swing.JPanel; import org.pushingpixels.flamingo.api.common.JCommandMenuButton; import org.pushingpixels.flamingo.api.common.JCommandToggleMenuButton; import org.pushingpixels.flamingo.internal.ui.common.popup.JColorSelectorComponent; import org.pushingpixels.flamingo.internal.ui.common.popup.JColorSelectorPanel; public class JColorSelectorPopupMenu extends JCommandPopupMenu { private ColorSelectorCallback colorSelectorCallback; private JColorSelectorPanel lastColorSelectorPanel; private static LinkedList recentlySelected = new LinkedList(); public static interface ColorSelectorCallback { public void onColorRollover(Color color); public void onColorSelected(Color color); } public JColorSelectorPopupMenu(ColorSelectorCallback colorSelectorCallback) { this.colorSelectorCallback = colorSelectorCallback; } public void addColorSectionWithDerived(String label, Color[] primaryColors) { if ((primaryColors == null) || (primaryColors.length != 10)) { throw new IllegalArgumentException("Must pass exactly 10 colors"); } JPanel selectorContainer = new MultiRowSelector(primaryColors); JColorSelectorPanel selector = new JColorSelectorPanel(label, selectorContainer); this.addMenuPanel(selector); this.lastColorSelectorPanel = selector; } public void addColorSection(String label, Color[] primaryColors) { if ((primaryColors == null) || (primaryColors.length != 10)) { throw new IllegalArgumentException("Must pass exactly 10 colors"); } JPanel selectorContainer = new SingleRowSelector(primaryColors); JColorSelectorPanel selector = new JColorSelectorPanel(label, selectorContainer); this.addMenuPanel(selector); this.lastColorSelectorPanel = selector; } public void addRecentSection(String label) { JPanel recent = new SingleRowSelector(recentlySelected .toArray(new Color[0])); JColorSelectorPanel recentPanel = new JColorSelectorPanel(label, recent); recentPanel.setLastPanel(true); this.addMenuPanel(recentPanel); this.lastColorSelectorPanel = recentPanel; } @Override public void addMenuButton(JCommandMenuButton menuButton) { super.addMenuButton(menuButton); this.updateLastColorSelectorPanel(); } @Override public void addMenuButton(JCommandToggleMenuButton menuButton) { super.addMenuButton(menuButton); this.updateLastColorSelectorPanel(); } @Override public void addMenuSeparator() { super.addMenuSeparator(); this.updateLastColorSelectorPanel(); } private void updateLastColorSelectorPanel() { if (this.lastColorSelectorPanel != null) { this.lastColorSelectorPanel.setLastPanel(true); this.lastColorSelectorPanel = null; } } public ColorSelectorCallback getColorSelectorCallback() { return this.colorSelectorCallback; } private static void wireToLRU(JColorSelectorComponent colorSelector) { colorSelector .addColorSelectorCallback(new JColorSelectorPopupMenu.ColorSelectorCallback() { @Override public void onColorSelected(Color color) { addColorToRecentlyUsed(color); } @Override public void onColorRollover(Color color) { } }); } public synchronized static List getRecentlyUsedColors() { return Collections.unmodifiableList(recentlySelected); } public synchronized static void addColorToRecentlyUsed(Color color) { // is in? if (recentlySelected.contains(color)) { recentlySelected.remove(color); recentlySelected.addLast(color); return; } if (recentlySelected.size() == 10) { recentlySelected.removeFirst(); } recentlySelected.addLast(color); } private class SingleRowSelector extends JPanel { public SingleRowSelector(final Color... colors) { final JColorSelectorComponent[] comps = new JColorSelectorComponent[colors.length]; for (int i = 0; i < colors.length; i++) { comps[i] = new JColorSelectorComponent(colors[i], colorSelectorCallback); wireToLRU(comps[i]); this.add(comps[i]); } this.setLayout(new LayoutManager() { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension minimumLayoutSize(Container parent) { return new Dimension(10, 10); } @Override public Dimension preferredLayoutSize(Container parent) { int gap = getGap(); int size = getSize(); return new Dimension(colors.length * size + (colors.length + 1) * gap, size + 2 * gap); } @Override public void layoutContainer(Container parent) { int gap = getGap(); int size = getSize(); if (parent.getComponentOrientation().isLeftToRight()) { int x = gap; int y = gap; for (int i = 0; i < colors.length; i++) { comps[i].setBounds(x, y, size, size); x += (size + gap); } } else { int x = getWidth() - gap; int y = gap; for (int i = 0; i < colors.length; i++) { comps[i].setBounds(x - size, y, size, size); x -= (size + gap); } } } private int getGap() { return 4; } private int getSize() { return 13; } }); } } private class MultiRowSelector extends JPanel { static final int SECONDARY_ROWS = 5; public MultiRowSelector(final Color... colors) { final JColorSelectorComponent[][] comps = new JColorSelectorComponent[colors.length][1 + SECONDARY_ROWS]; for (int i = 0; i < colors.length; i++) { Color primary = colors[i]; comps[i][0] = new JColorSelectorComponent(primary, colorSelectorCallback); wireToLRU(comps[i][0]); this.add(comps[i][0]); float[] primaryHsb = new float[3]; Color.RGBtoHSB(primary.getRed(), primary.getGreen(), primary .getBlue(), primaryHsb); for (int row = 1; row <= SECONDARY_ROWS; row++) { float bFactor = (float) (row - 1) / (float) (SECONDARY_ROWS); bFactor = (float) Math.pow(bFactor, 1.4f); float brightness = 1.0f - bFactor; if (primaryHsb[1] == 0.0f) { // special handling for gray scale float max = 0.5f + 0.5f * primaryHsb[2]; brightness = max * (SECONDARY_ROWS - row + 1) / SECONDARY_ROWS; } Color secondary = new Color(Color.HSBtoRGB(primaryHsb[0], primaryHsb[1] * (row + 1) / (SECONDARY_ROWS + 1), brightness)); comps[i][row] = new JColorSelectorComponent(secondary, colorSelectorCallback); comps[i][row].setTopOpen(row > 1); comps[i][row].setBottomOpen(row < SECONDARY_ROWS); wireToLRU(comps[i][row]); this.add(comps[i][row]); } } this.setLayout(new LayoutManager() { @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension minimumLayoutSize(Container parent) { return new Dimension(10, 10); } @Override public Dimension preferredLayoutSize(Container parent) { int gap = getGap(); int size = getSize(); return new Dimension(colors.length * size + (colors.length + 1) * gap, gap + size + gap + SECONDARY_ROWS * size + gap); } @Override public void layoutContainer(Container parent) { int gap = getGap(); int size = getSize(); if (parent.getComponentOrientation().isLeftToRight()) { int y = gap; for (int row = 0; row <= SECONDARY_ROWS; row++) { int x = gap; for (int i = 0; i < colors.length; i++) { comps[i][row].setBounds(x, y, size, size); x += (size + gap); } y += size; if (row == 0) { y += gap; } } } else { int y = gap; for (int row = 0; row <= SECONDARY_ROWS; row++) { int x = getWidth() - gap; for (int i = 0; i < colors.length; i++) { comps[i][row] .setBounds(x - size, y, size, size); x -= (size + gap); } y += size; if (row == 0) { y += gap; } } } } private int getGap() { return 4; } private int getSize() { return 13; } }); } } } src/org/pushingpixels/flamingo/api/common/popup/JCommandPopupMenu.java0000644000175000017500000002650211421613102025302 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.popup; import java.awt.Component; import java.util.ArrayList; import java.util.Collections; import javax.swing.*; import javax.swing.JPopupMenu.Separator; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.internal.ui.common.popup.BasicCommandPopupMenuUI; import org.pushingpixels.flamingo.internal.ui.common.popup.PopupPanelUI; /** * Popup menu. Can host any number of command menu buttons added with * {@link #addMenuButton(JCommandMenuButton)} separated with optional * {@link #addMenuSeparator()}. The * {@link #JCommandPopupMenu(JCommandButtonPanel, int, int)} constructor allows * placing a scrollable command button panel in the top part of the popup menu. * * @author Kirill Grouchnikov */ public class JCommandPopupMenu extends JPopupPanel { /** * @see #getUIClassID */ public static final String uiClassID = "CommandPopupMenuUI"; /** * The main button panel. Can be null if this command popup * menu was created with the {@link #JCommandPopupMenu()} constructor. * * @see #JCommandPopupMenu(JCommandButtonPanel, int, int) * @see #hasCommandButtonPanel() * @see #getMainButtonPanel() */ protected JCommandButtonPanel mainButtonPanel; /** * Menu components. This list holds: *

    *
  • {@link JCommandMenuButton}s added with * {@link #addMenuButton(JCommandMenuButton)}
  • *
  • {@link JCommandToggleMenuButton}s added with * {@link #addMenuButton(JCommandToggleMenuButton)}
  • *
  • {@link Separator}s added with {@link #addMenuSeparator()}
  • *
  • {@link JPanel}s added by the subclasses with * {@link #addMenuPanel(JPanel)}
  • *
* * @see #addMenuButton(JCommandMenuButton) * @see #addMenuButton(JCommandToggleMenuButton) * @see #addMenuSeparator() * @see #addMenuPanel(JPanel) * @see #getMenuComponents() */ protected java.util.List menuComponents; /** * Maximum number of button columns visible in the {@link #mainButtonPanel}. * * @see #JCommandPopupMenu(JCommandButtonPanel, int, int) * @see #getMaxButtonColumns() */ protected int maxButtonColumns; /** * Maximum number of button rows visible in the {@link #mainButtonPanel}. * * @see #JCommandPopupMenu(JCommandButtonPanel, int, int) * @see #getMaxVisibleButtonRows() */ protected int maxVisibleButtonRows; /** * Maximum number of menu items visible in this menu. If more buttons are * added with the {@link #addMenuButton(JCommandMenuButton)} and * {@link #addMenuButton(JCommandToggleMenuButton)} APIs, the menu part will * show scroller buttons above the first and below the last menu button. If * the value is negative, there is no limitation on how many menu buttons * are shown, and the entire popup menu can overflow the monitor edges. */ protected int maxVisibleMenuButtons; private boolean toDismissOnChildClick; /** * Creates an empty popup menu with no button panel. */ public JCommandPopupMenu() { this.menuComponents = new ArrayList(); this.maxVisibleMenuButtons = -1; this.toDismissOnChildClick = true; } /** * Creates a popup menu hosting the specified button panel. * * @param buttonPanel * Fully constructed button panel. * @param maxButtonColumns * Maximum number of button columns visible in * buttonPanel. * @param maxVisibleButtonRows * Maximum number of button rows visible in * buttonPanel. */ public JCommandPopupMenu(JCommandButtonPanel buttonPanel, int maxButtonColumns, int maxVisibleButtonRows) { this(); this.mainButtonPanel = buttonPanel; this.maxButtonColumns = maxButtonColumns; this.maxVisibleButtonRows = maxVisibleButtonRows; this.updateUI(); } /** * Adds the specified menu button to this menu. * * @param menuButton * Menu button to add. */ public void addMenuButton(JCommandMenuButton menuButton) { menuButton.setHorizontalAlignment(SwingUtilities.LEFT); this.menuComponents.add(menuButton); this.fireStateChanged(); } /** * Adds the specified toggle menu button to this menu. * * @param menuButton * Menu button to add. */ public void addMenuButton(JCommandToggleMenuButton menuButton) { menuButton.setHorizontalAlignment(SwingUtilities.LEFT); this.menuComponents.add(menuButton); this.fireStateChanged(); } /** * Adds a menu separator to this menu. */ public void addMenuSeparator() { this.menuComponents.add(new JPopupMenu.Separator()); this.fireStateChanged(); } /** * Adds a menu panel to this menu. * * @param menuPanel * Menu panel to add. */ protected void addMenuPanel(JPanel menuPanel) { if (this.maxVisibleMenuButtons > 0) { throw new IllegalStateException( "This method is not supported on menu that contains a command button panel"); } this.menuComponents.add(menuPanel); this.fireStateChanged(); } /** * Returns indication whether this menu has a command button panel. * * @return true if this menu has a command button panel, * false otherwise. * @see #getMainButtonPanel() */ public boolean hasCommandButtonPanel() { return (this.mainButtonPanel != null); } /** * Returns the command button panel of this menu. Can return * null. * * @return The command button panel of this menu. * @see #hasCommandButtonPanel() */ public JCommandButtonPanel getMainButtonPanel() { return this.mainButtonPanel; } /** * Returns an unmodifiable list of all the menu components. Can return * null. * * @return An unmodifiable list of all the menu components */ public java.util.List getMenuComponents() { if (this.menuComponents == null) return null; return Collections.unmodifiableList(this.menuComponents); } /** * Returns the maximum number of button columns visible in the command * button panel of this menu. If this menu has been created with the * {@link #JCommandPopupMenu()} constructor, zero is returned. * * @return The maximum number of button columns visible in the command * button panel of this menu. * @see #JCommandPopupMenu(JCommandButtonPanel, int, int) * @see #getMaxVisibleButtonRows() */ public int getMaxButtonColumns() { return this.maxButtonColumns; } /** * Returns the maximum number of button rows visible in the command button * panel of this menu. If this menu has been created with the * {@link #JCommandPopupMenu()} constructor, zero is returned. * * @return The maximum number of button rows visible in the command button * panel of this menu. * @see #JCommandPopupMenu(JCommandButtonPanel, int, int) * @see #getMaxButtonColumns() */ public int getMaxVisibleButtonRows() { return this.maxVisibleButtonRows; } /** * Returns the maximum number of menu items visible in this menu. * * @return The maximum number of menu items visible in this menu. If the * value is negative, there is no limitation on how many menu * buttons are shown, and the entire popup menu can overflow the * monitor edges. */ public int getMaxVisibleMenuButtons() { return this.maxVisibleMenuButtons; } /** * Sets the maximum number of menu items visible in this menu. If the value * is negative, there is no limitation on how many menu buttons are shown, * and the entire popup menu can overflow the monitor edges. * * @param maxVisibleMenuButtons * The new value for the maximum number of menu items visible in * this menu. */ public void setMaxVisibleMenuButtons(int maxVisibleMenuButtons) { for (Component menuComp : this.menuComponents) { if (menuComp instanceof JPanel) { throw new IllegalStateException( "This method is not supported on menus with panels"); } } int old = this.maxVisibleMenuButtons; this.maxVisibleMenuButtons = maxVisibleMenuButtons; if (old != this.maxVisibleMenuButtons) { this.firePropertyChange("maxVisibleMenuButtons", old, this.maxVisibleMenuButtons); } } @Override public String getUIClassID() { return uiClassID; } @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((PopupPanelUI) UIManager.getUI(this)); } else { setUI(BasicCommandPopupMenuUI.createUI(this)); } } /** * Adds the specified change listener to track changes to this popup menu. * * @param l * Change listener to add. * @see #removeChangeListener(ChangeListener) */ public void addChangeListener(ChangeListener l) { this.listenerList.add(ChangeListener.class, l); } /** * Removes the specified change listener from tracking changes to this popup * menu. * * @param l * Change listener to remove. * @see #addChangeListener(ChangeListener) */ public void removeChangeListener(ChangeListener l) { this.listenerList.remove(ChangeListener.class, l); } /** * Notifies all registered listener that the state of this popup menu has * changed. */ protected void fireStateChanged() { // Guaranteed to return a non-null array Object[] listeners = this.listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event ChangeEvent event = new ChangeEvent(this); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { ((ChangeListener) listeners[i + 1]).stateChanged(event); } } } public boolean isToDismissOnChildClick() { return toDismissOnChildClick; } public void setToDismissOnChildClick(boolean toDismissOnChildClick) { this.toDismissOnChildClick = toDismissOnChildClick; } } src/org/pushingpixels/flamingo/api/common/popup/PopupPanelCallback.java0000644000175000017500000000476511401230444025452 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.popup; import org.pushingpixels.flamingo.api.common.JCommandButton; import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntrySecondary; /** * An application hook that allows associating a custom popup panel with a popup * area of the specific command button. * * @author Kirill Grouchnikov * @see JCommandButton#setPopupCallback(PopupPanelCallback) * @see RibbonApplicationMenuEntrySecondary#setPopupCallback(PopupPanelCallback) */ public interface PopupPanelCallback { /** * Returns the popup panel to be shown when the popup area of the specified * command button is activated. * * @param commandButton * Command button. * @return The popup panel to be shown when the popup area of the specified * command button is activated. */ public JPopupPanel getPopupPanel(JCommandButton commandButton); } src/org/pushingpixels/flamingo/api/common/popup/JPopupPanel.java0000644000175000017500000001023211421612346024140 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.popup; import java.awt.Rectangle; import javax.swing.JPanel; import javax.swing.UIManager; import org.pushingpixels.flamingo.internal.ui.common.popup.BasicPopupPanelUI; import org.pushingpixels.flamingo.internal.ui.common.popup.PopupPanelUI; /** * Base class for popup panels. * * @author Kirill Grouchnikov * @see PopupPanelManager#addPopup(javax.swing.JComponent, javax.swing.Popup, * JPopupPanel) */ public abstract class JPopupPanel extends JPanel { /** * @see #getUIClassID */ public static final String uiClassID = "PopupPanelUI"; /** * The customizer for this popup panel. Can be null. * * @see #getCustomizer() * @see #setCustomizer(PopupPanelCustomizer) */ protected PopupPanelCustomizer customizer; /** * Allows providing custom application logic for computing the screen bounds * of popup panels before they are shown on the screen. * * @author Kirill Grouchnikov */ public static interface PopupPanelCustomizer { /** * Returns the requested screen bounds of the associated popup panel. * * @return The requested screen bounds of the associated popup panel. */ public Rectangle getScreenBounds(); } /** * Protected to prevent direct instantiation. */ protected JPopupPanel() { } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUI() */ @Override public PopupPanelUI getUI() { return (PopupPanelUI) ui; } /** * Sets the look and feel (L&F) object that renders this component. * * @param ui * the PopupGalleryUI L&F object */ protected void setUI(PopupPanelUI ui) { super.setUI(ui); } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /* * (non-Javadoc) * * @see javax.swing.JPanel#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((PopupPanelUI) UIManager.getUI(this)); } else { setUI(BasicPopupPanelUI.createUI(this)); } } /** * Sets the customizer for this popup panel. * * @param customizer * The customizer for this popup panel. * @see #getCustomizer() */ public void setCustomizer(PopupPanelCustomizer customizer) { this.customizer = customizer; } /** * Returns the customizer of this popup panel. Can return null. * * @return The customizer of this popup panel. * @see #setCustomizer(PopupPanelCustomizer) */ public PopupPanelCustomizer getCustomizer() { return this.customizer; } } src/org/pushingpixels/flamingo/api/common/popup/PopupPanelManager.java0000644000175000017500000002441311401230444025320 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.popup; import java.awt.Component; import java.awt.event.ComponentEvent; import java.util.*; import javax.swing.JComponent; import javax.swing.Popup; import javax.swing.event.EventListenerList; import org.pushingpixels.flamingo.api.common.JCommandButton; /** * Manager for showing and hiding {@link JPopupPanel}s. * * @author Kirill Grouchnikov */ public class PopupPanelManager { /** * Listener on showing and hiding the popup panels. * * @author Kirill Grouchnikov */ public static interface PopupListener extends EventListener { /** * Fired when a popup panel has been shown. * * @param event * Popup event. */ void popupShown(PopupEvent event); /** * Fired when a popup panel has been hidden. * * @param event * Popup event. */ void popupHidden(PopupEvent event); } /** * Popup event. * * @author Kirill Grouchnikov */ public static class PopupEvent extends ComponentEvent { /** * ID of "popup shown" event. */ public static final int POPUP_SHOWN = 100; /** * ID of "popup hidden" event. */ public static final int POPUP_HIDDEN = 101; /** * The popup originator component. */ private JComponent popupOriginator; /** * Creates a new popup event. * * @param source * Event source. * @param id * Event ID. * @param popupOriginator * Popup originator component. */ public PopupEvent(JPopupPanel source, int id, JComponent popupOriginator) { super(source, id); this.popupOriginator = popupOriginator; } /** * Returns the popup originator component. * * @return Popup originator component. */ public JComponent getPopupOriginator() { return this.popupOriginator; } } /** * List of all registered listeners. */ protected EventListenerList listenerList = new EventListenerList(); /** * The singleton instance of popup panel manager. */ private static final PopupPanelManager instance = new PopupPanelManager(); /** * Information on a single showing popup. * * @author Kirill Grouchnikov */ public static class PopupInfo { /** * The popup panel. */ private JPopupPanel popupPanel; /** * The originating component. */ private JComponent popupOriginator; /** * Creates a new information object. * * @param popupOriginator * The originating component. * @param popupPanel * The popup panel. */ public PopupInfo(JComponent popupOriginator, JPopupPanel popupPanel) { this.popupOriginator = popupOriginator; this.popupPanel = popupPanel; } /** * Returns the popup panel. * * @return The popup panel. */ public JPopupPanel getPopupPanel() { return this.popupPanel; } /** * Returns the originating component. * * @return The originating component. */ public JComponent getPopupOriginator() { return this.popupOriginator; } } /** * Returns the default popup panel manager. * * @return a PopupPanelManager object */ public static PopupPanelManager defaultManager() { return instance; } /** * All currently shown popup panels. */ protected LinkedList shownPath = new LinkedList(); /** * Map of all popup panels and associated {@link Popup} objects. */ protected Map popupPanels = new HashMap(); /** * Adds new popup to the tracking structures. * * @param popupOriginator * The originating component. * @param popup * The new popup. * @param popupInitiator * The initiator of the popup. */ public void addPopup(JComponent popupOriginator, Popup popup, JPopupPanel popupInitiator) { popupPanels.put(popupInitiator, popup); shownPath.addLast(new PopupInfo(popupOriginator, popupInitiator)); popup.show(); if (popupOriginator instanceof JCommandButton) { ((JCommandButton) popupOriginator).getPopupModel().setPopupShowing( true); } this.firePopupShown(popupInitiator, popupOriginator); } /** * Hides the last shown popup panel. */ public void hideLastPopup() { if (shownPath.size() == 0) return; PopupInfo last = shownPath.removeLast(); Popup popup = popupPanels.get(last.popupPanel); popup.hide(); popupPanels.remove(last.popupPanel); if (last.popupOriginator instanceof JCommandButton) { ((JCommandButton) last.popupOriginator).getPopupModel() .setPopupShowing(false); } // KeyTipManager.defaultManager().showChainBefore(last.popupPanel); this.firePopupHidden(last.popupPanel, last.popupOriginator); } /** * Hides all popup panels based on the specified component. We find the * first ancestor of the specified component that is popup panel, and close * all popup panels that were open from that popup panel. If the specified * component is null, all popup panels are closed. * * @param comp * Component. */ public void hidePopups(Component comp) { // System.out.println("Hiding all popups"); // try { // throw new Exception(); // } // catch (Exception exc) { // exc.printStackTrace(System.out); // System.out.println("At " + System.currentTimeMillis() + "\n"); // } boolean foundAndDismissed = false; if (comp != null) { Component c = comp; // find JPopupGallery parent of the component while (c != null) { if (c instanceof JPopupPanel) { foundAndDismissed = true; // And close all popups that were opened // from the found popup panel while (shownPath.size() > 0) { if (shownPath.getLast().popupPanel == c) return; PopupInfo last = shownPath.removeLast(); Popup popup = popupPanels.get(last.popupPanel); popup.hide(); if (last.popupOriginator instanceof JCommandButton) { ((JCommandButton) last.popupOriginator) .getPopupModel().setPopupShowing(false); } this.firePopupHidden(last.popupPanel, last.popupOriginator); popupPanels.remove(last.popupPanel); } } c = c.getParent(); } } if (!foundAndDismissed || (comp == null)) { while (shownPath.size() > 0) { PopupInfo last = shownPath.removeLast(); Popup popup = popupPanels.get(last.popupPanel); popup.hide(); if (last.popupOriginator instanceof JCommandButton) { ((JCommandButton) last.popupOriginator).getPopupModel() .setPopupShowing(false); } this.firePopupHidden(last.popupPanel, last.popupOriginator); popupPanels.remove(last.popupPanel); } } } /** * Returns all currently shown popup panels. * * @return All currently shown popup panels. */ public List getShownPath() { List toReturn = new ArrayList(); for (PopupInfo pInfo : this.shownPath) toReturn.add(pInfo); return toReturn; } /** * Adds the specified popup listener. * * @param l * Listener to add. */ public void addPopupListener(PopupListener l) { this.listenerList.add(PopupListener.class, l); } /** * Removes the specified popup listener. * * @param l * Listener to remove. */ public void removePopupListener(PopupListener l) { this.listenerList.remove(PopupListener.class, l); } /** * Fires an event on showing the specified popup panel. * * @param panel * Popup panel that was shown. * @param popupOriginator * The originating component. */ protected void firePopupShown(JPopupPanel panel, JComponent popupOriginator) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); PopupEvent popupEvent = new PopupEvent(panel, PopupEvent.POPUP_SHOWN, popupOriginator); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == PopupListener.class) { ((PopupListener) listeners[i + 1]).popupShown(popupEvent); } } } /** * Fires an event on hiding the specified popup panel. * * @param panel * Popup panel that was hidden. * @param popupOriginator * The originating component. */ protected void firePopupHidden(JPopupPanel panel, JComponent popupOriginator) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); PopupEvent popupEvent = new PopupEvent(panel, PopupEvent.POPUP_HIDDEN, popupOriginator); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == PopupListener.class) { ((PopupListener) listeners[i + 1]).popupHidden(popupEvent); } } } } src/org/pushingpixels/flamingo/api/common/AsynchronousLoadListener.java0000644000175000017500000000471011401230444025576 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.util.EventListener; import java.util.concurrent.ExecutorService; import org.pushingpixels.flamingo.api.svg.SvgBatikResizableIcon; /** * This interface is used for asynchronously-loaded contents. For example, the * {@link SvgBatikResizableIcon} uses the {@link ExecutorService} to load the * SVG image in the background. When the image is loaded, the component that * contains this image ({@link JCommandButton} for example) is notified to * repaint itself. * * @author Kirill Grouchnikov. */ public interface AsynchronousLoadListener extends EventListener { /** * Indicates that the asynchronous load has been completed. * * @param success * If true, the load has been completed * successfully. */ public void completed(boolean success); } src/org/pushingpixels/flamingo/api/common/CommandButtonLayoutManager.java0000644000175000017500000001265411401230444026046 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.*; import java.beans.PropertyChangeListener; import java.util.List; /** * Definition of a layout manager for {@link AbstractCommandButton}s. * * @author Kirill Grouchnikov */ public interface CommandButtonLayoutManager extends PropertyChangeListener { /** * Enumerates the available values for separator orientations. * * @author Kirill Grouchnikov */ public enum CommandButtonSeparatorOrientation { /** * Vertical separator orientation. */ VERTICAL, /** * Horizontal separator orientation. */ HORIZONTAL } /** * Layout information on a single line of text. * * @author Kirill Grouchnikov */ public class TextLayoutInfo { /** * Text itself. */ public String text; /** * The text rectangle. */ public Rectangle textRect; } /** * Layout information on different visual parts of a single command button. * * @author Kirill Grouchnikov */ public class CommandButtonLayoutInfo { /** * The action area. A mouse click in this area will trigger all * listeners associated with the command button action model * {@link AbstractCommandButton#addActionListener(java.awt.event.ActionListener)} */ public Rectangle actionClickArea; /** * The popup area. A mouse click in this area will trigger the listener * associated with the command button popup model * {@link JCommandButton#setPopupCallback(org.jvnet.flamingo.common.popup.PopupPanelCallback)} */ public Rectangle popupClickArea; /** * The separator area. If it's not empty, the command button will show a * separator between {@link #actionClickArea} and * {@link #popupClickArea} on mouse rollover - depending on the current * look-and-feel. */ public Rectangle separatorArea; public CommandButtonSeparatorOrientation separatorOrientation; /** * Rectangle for the command button icon. */ public Rectangle iconRect; /** * Layout information for the command button text (that can span * multiple lines). */ public List textLayoutInfoList; /** * Layout information for the command button extra text (that can span * multiple lines). */ public List extraTextLayoutInfoList; /** * Rectangle for the icon associated with the {@link #popupClickArea}. * This icon is usually a single or double arrow indicating that the * command button has a popup area. */ public Rectangle popupActionRect; /** * Indication whether the command button text (rectangles in * {@link #textLayoutInfoList}) belongs in the action area. */ public boolean isTextInActionArea; } /** * Returns the preferred size of the specified command button. * * @param commandButton * Command button. * @return The preferred size of the specified command button. */ public Dimension getPreferredSize(AbstractCommandButton commandButton); /** * Returns the preferred icon size of command buttons which use this layout * manager. * * @return The preferred icon size of command buttons which use this layout * manager. */ public int getPreferredIconSize(); /** * Returns the anchor center point of the key tip of the specified command * button. * * @param commandButton * Command button. * @return The anchor center point of the key tip of the specified command * button. */ public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton); /** * Returns the layout information for the specified command button. * * @param commandButton * Command button. * @param g * Graphics context. * @return The layout information for the specified command button. */ public CommandButtonLayoutInfo getLayoutInfo( AbstractCommandButton commandButton, Graphics g); } src/org/pushingpixels/flamingo/api/common/JCommandToggleButton.java0000644000175000017500000000702511401230444024625 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import javax.swing.JComponent; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.model.ActionToggleButtonModel; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandToggleButtonUI; import org.pushingpixels.flamingo.internal.ui.common.CommandButtonUI; /** * Command button. * * @author Kirill Grouchnikov */ public class JCommandToggleButton extends AbstractCommandButton { /** * The UI class ID string. */ public static final String uiClassID = "CommandToggleButtonUI"; /** * Creates a new command toggle button with empty text * * @param icon * Button icon. */ public JCommandToggleButton(ResizableIcon icon) { this(null, icon); } /** * Creates a new command toggle button without an icon. * * @param title * Button title. May contain any number of words. */ public JCommandToggleButton(String title) { this(title, null); } /** * Creates a new command toggle button. * * @param title * Button title. May contain any number of words. * @param icon * Button icon. */ public JCommandToggleButton(String title, ResizableIcon icon) { super(title, icon); this.setActionModel(new ActionToggleButtonModel(false)); this.updateUI(); } /** * Resets the UI property to a value from the current look and feel. * * @see JComponent#updateUI */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((CommandButtonUI) UIManager.getUI(this)); } else { setUI(BasicCommandToggleButtonUI.createUI(this)); } } /* * (non-Javadoc) * * @see javax.swing.JComponent#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } @Override public String toString() { return "Command toggle button[" + this.getText() + "]"; } } src/org/pushingpixels/flamingo/api/common/JCommandButton.java0000644000175000017500000006117011401230444023464 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.model.ActionRepeatableButtonModel; import org.pushingpixels.flamingo.api.common.model.PopupButtonModel; import org.pushingpixels.flamingo.api.common.popup.PopupPanelCallback; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonUI; import org.pushingpixels.flamingo.internal.ui.common.CommandButtonUI; /** * Command button. * * @author Kirill Grouchnikov */ public class JCommandButton extends AbstractCommandButton { /** * The UI class ID string. */ public static final String uiClassID = "CommandButtonUI"; /** * Associated popup callback. May be null. * * @see #setPopupCallback(PopupPanelCallback) * @see #getPopupCallback() */ protected PopupPanelCallback popupCallback; /** * The command button kind of this button. * * @see #setCommandButtonKind(CommandButtonKind) * @see #getCommandButtonKind() */ protected CommandButtonKind commandButtonKind; /** * The popup orientation kind of this button. * * @see #setPopupOrientationKind(CommandButtonPopupOrientationKind) * @see #getPopupOrientationKind() */ protected CommandButtonPopupOrientationKind popupOrientationKind; /** * Indicates the auto-repeat action mode. When the button is not in the * auto-repeat action mode, the registered action listeners are activated * when the mouse is released (just as with the base {@link AbstractButton} * ). When the button is in auto-repeat mode, the registered action * listeners are activated when the mouse is pressed. In addition, if the * mouse is still pressed after {@link #getAutoRepeatInitialInterval()}, the * action listeners will be activated every * {@link #getAutoRepeatSubsequentInterval()} until the button is disabled * or the mouse is released. * * @see #autoRepeatInitialInterval * @see #autoRepeatSubsequentInterval * @see #setAutoRepeatAction(boolean) * @see #isAutoRepeatAction() */ protected boolean isAutoRepeatAction; /** * The initial interval for invoking the registered action listeners in the * auto-repeat action mode. * * @see #isAutoRepeatAction * @see #autoRepeatSubsequentInterval * @see #getAutoRepeatInitialInterval() * @see #setAutoRepeatActionIntervals(int, int) */ protected int autoRepeatInitialInterval; /** * The subsequent interval for invoking the registered action listeners in * the auto-repeat action mode. * * @see #isAutoRepeatAction * @see #autoRepeatInitialInterval * @see #getAutoRepeatSubsequentInterval() * @see #setAutoRepeatActionIntervals(int, int) */ protected int autoRepeatSubsequentInterval; /** * Indicates that rollover should result in firing the action. Used in * conjunction with the {@link #isAutoRepeatAction} can model quick pan * buttons such as breadcrumb bar scrollers. * * @see #setFireActionOnRollover(boolean) * @see #isFireActionOnRollover() */ protected boolean isFireActionOnRollover; /** * Popup model of this button. * * @see #setPopupModel(PopupButtonModel) * @see #getPopupModel() */ protected PopupButtonModel popupModel; /** * Default popup handler for this button. */ protected PopupHandler popupHandler; /** * Rich tooltip for the popup area of this button. * * @see #setPopupRichTooltip(RichTooltip) * @see #getRichTooltip(MouseEvent) */ private RichTooltip popupRichTooltip; /** * Key tip for the popup area of this button. * * @see #setPopupKeyTip(String) * @see #getPopupKeyTip() */ protected String popupKeyTip; /** * Enumerates the available command button kinds. * * @author Kirill Grouchnikov */ public static enum CommandButtonKind { /** * Command button that has only action area. */ ACTION_ONLY(true, false), /** * Command button that has only popup area. */ POPUP_ONLY(false, true), /** * Command button that has both action and popup areas, with the main * text click activating the action. */ ACTION_AND_POPUP_MAIN_ACTION(true, true), /** * Command button that has both action and popup areas, with the main * text click activating the popup. */ ACTION_AND_POPUP_MAIN_POPUP(true, true); /** * true if the command button kind has an action. */ private boolean hasAction; /** * true if the command button kind has a popup. */ private boolean hasPopup; /** * Constructs a new command button kind. * * @param hasAction * Indicates whether the command button kind has an action. * @param hasPopup * Indicates whether the command button kind has a popup. */ private CommandButtonKind(boolean hasAction, boolean hasPopup) { this.hasAction = hasAction; this.hasPopup = hasPopup; } /** * Returns indication whether this command button kind has an action. * * @return true if the command button kind has an action, * false otherwise. */ public boolean hasAction() { return hasAction; } /** * Returns indication whether this command button kind has a popup. * * @return true if the command button kind has a popup, * false otherwise. */ public boolean hasPopup() { return hasPopup; } } /** * Orientation kind for the popup. * * @author Kirill Grouchnikov */ public static enum CommandButtonPopupOrientationKind { /** * Indicates that the popup should be displayed below the button. */ DOWNWARD, /** * Indicates that the popup should be displayed to the side of the * button. */ SIDEWARD } /** * Extension of the default button model that supports the * {@link PopupButtonModel} interface. * * @author Kirill Grouchnikov */ private static class DefaultPopupButtonModel extends DefaultButtonModel implements PopupButtonModel { /** * Timer for the auto-repeat action mode. */ protected Timer autoRepeatTimer; /** * Identifies the "popup showing" bit in the bitmask, which indicates * that the visibility status of the associated popup. */ public final static int POPUP_SHOWING = 1 << 8; /** * Creates a new default popup button model. */ public DefaultPopupButtonModel() { super(); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.PopupButtonModel#addPopupActionListener * (org.jvnet.flamingo.common.PopupActionListener) */ @Override public void addPopupActionListener(PopupActionListener l) { listenerList.add(PopupActionListener.class, l); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.PopupButtonModel#removePopupActionListener * (org.jvnet.flamingo.common.PopupActionListener) */ @Override public void removePopupActionListener(PopupActionListener l) { listenerList.remove(PopupActionListener.class, l); } /** * Notifies all listeners that have registered interest for notification * on this event type. * * @param e * the ActionEvent to deliver to listeners * @see EventListenerList */ protected void firePopupActionPerformed(ActionEvent e) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == PopupActionListener.class) { ((PopupActionListener) listeners[i + 1]).actionPerformed(e); } } } /* * (non-Javadoc) * * @see javax.swing.DefaultButtonModel#setPressed(boolean) */ @Override public void setPressed(boolean b) { if ((isPressed() == b) || !isEnabled()) { return; } if (b) { stateMask |= PRESSED; } else { stateMask &= ~PRESSED; } if (isPressed() && isArmed()) { // fire the popup action on button press and not on button // release - like the comboboxes int modifiers = 0; AWTEvent currentEvent = EventQueue.getCurrentEvent(); if (currentEvent instanceof InputEvent) { modifiers = ((InputEvent) currentEvent).getModifiers(); } else if (currentEvent instanceof ActionEvent) { modifiers = ((ActionEvent) currentEvent).getModifiers(); } firePopupActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand(), EventQueue.getMostRecentEventTime(), modifiers)); } fireStateChanged(); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.PopupButtonModel#isPopupShowing() */ @Override public boolean isPopupShowing() { return (stateMask & POPUP_SHOWING) != 0; } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.PopupButtonModel#setPopupShowing(boolean) */ @Override public void setPopupShowing(boolean b) { // System.out.println(this.isPopupShowing() + "-->" + b); if (this.isPopupShowing() == b) { return; } if (b) { stateMask |= POPUP_SHOWING; } else { stateMask &= ~POPUP_SHOWING; } fireStateChanged(); } } /** * Creates a new command button with empty text * * @param icon * Button icon. */ public JCommandButton(ResizableIcon icon) { this(null, icon); } /** * Creates a new command button without an icon. * * @param title * Button title. May contain any number of words. */ public JCommandButton(String title) { this(title, null); } /** * Creates a new command button. * * @param title * Button title. May contain any number of words. * @param icon * Button icon. */ public JCommandButton(String title, ResizableIcon icon) { super(title, icon); this.setActionModel(new ActionRepeatableButtonModel(this)); // important - handler creation must be done before setting // the popup model so that it can be registered to track the // changes this.popupHandler = new PopupHandler(); this.setPopupModel(new DefaultPopupButtonModel()); this.commandButtonKind = CommandButtonKind.ACTION_ONLY; this.popupOrientationKind = CommandButtonPopupOrientationKind.DOWNWARD; // this.displayState = CommandButtonDisplayState.CUSTOM; this.isAutoRepeatAction = false; this.autoRepeatInitialInterval = 500; this.autoRepeatSubsequentInterval = 100; this.updateUI(); } /** * Returns the command button kind of this button. * * @return Command button kind of this button. * @see #setCommandButtonKind(CommandButtonKind) */ public CommandButtonKind getCommandButtonKind() { return this.commandButtonKind; } /** * Sets the kind for this button. Fires a commandButtonKind * property change event. * * @param commandButtonKind * The new button kind. * @see #getCommandButtonKind() */ public void setCommandButtonKind(CommandButtonKind commandButtonKind) { CommandButtonKind old = this.commandButtonKind; this.commandButtonKind = commandButtonKind; if (old != this.commandButtonKind) { firePropertyChange("commandButtonKind", old, this.commandButtonKind); } } /** * Returns the popup orientation kind of this button. * * @return Popup orientation kind of this button. * @see #setPopupOrientationKind(CommandButtonPopupOrientationKind) */ public CommandButtonPopupOrientationKind getPopupOrientationKind() { return this.popupOrientationKind; } /** * Sets the popup orientation for this button. Fires a * popupOrientationKind property change event. * * @param popupOrientationKind * The new popup orientation kind. * @see #getPopupOrientationKind() */ public void setPopupOrientationKind( CommandButtonPopupOrientationKind popupOrientationKind) { CommandButtonPopupOrientationKind old = this.popupOrientationKind; this.popupOrientationKind = popupOrientationKind; if (old != this.popupOrientationKind) { firePropertyChange("popupOrientationKind", old, this.popupOrientationKind); } } /* * (non-Javadoc) * * @see javax.swing.JComponent#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((CommandButtonUI) UIManager.getUI(this)); } else { setUI(BasicCommandButtonUI.createUI(this)); } } /* * (non-Javadoc) * * @see javax.swing.JComponent#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /** * Returns the associated popup callback. * * @return The associated popup callback. * @see #setPopupCallback(PopupPanelCallback) */ public PopupPanelCallback getPopupCallback() { return this.popupCallback; } /** * Sets new popup callback for this button. * * @param popupCallback * New popup callback for this button. * @see #getPopupCallback() */ public void setPopupCallback(PopupPanelCallback popupCallback) { this.popupCallback = popupCallback; } /** * Sets the auto-repeat action indication. * * @param isAutoRepeatAction * If true, pressing the button will activate * auto-repeat action mode. When the button is not in the * auto-repeat action mode, the registered action listeners are * activated when the mouse is released (just as with the base * {@link AbstractButton}). When the button is in auto-repeat * mode, the registered action listeners are activated when the * mouse is pressed. In addition, is the mouse is still pressed * after {@link #getAutoRepeatInitialInterval()}, the action * listeners will be activated every * {@link #getAutoRepeatSubsequentInterval()} until the button is * disabled or the mouse is released. * @see #setAutoRepeatActionIntervals(int, int) * @see #isAutoRepeatAction() */ public void setAutoRepeatAction(boolean isAutoRepeatAction) { this.isAutoRepeatAction = isAutoRepeatAction; } /** * Sets the intervals for the auto-repeat action mode. * * @param initial * The initial interval for invoking the registered action * listeners in the auto-repeat action mode. * @param subsequent * The subsequent interval for invoking the registered action * listeners in the auto-repeat action mode. * @see #setAutoRepeatAction(boolean) * @see #isAutoRepeatAction() * @see #getAutoRepeatInitialInterval() * @see #getAutoRepeatSubsequentInterval() */ public void setAutoRepeatActionIntervals(int initial, int subsequent) { this.autoRepeatInitialInterval = initial; this.autoRepeatSubsequentInterval = subsequent; } /** * Returns indication whether the button is in auto-repeat action mode. * * @return true if the button is in auto-repeat action mode, * false otherwise. * @see #setAutoRepeatAction(boolean) * @see #setAutoRepeatActionIntervals(int, int) * @see #getAutoRepeatInitialInterval() * @see #getAutoRepeatSubsequentInterval() */ public boolean isAutoRepeatAction() { return this.isAutoRepeatAction; } /** * Returns the initial interval for invoking the registered action listeners * in the auto-repeat action mode. * * @return The initial interval for invoking the registered action listeners * in the auto-repeat action mode. * @see #setAutoRepeatActionIntervals(int, int) * @see #setAutoRepeatAction(boolean) * @see #isAutoRepeatAction() * @see #getAutoRepeatSubsequentInterval() */ public int getAutoRepeatInitialInterval() { return autoRepeatInitialInterval; } /** * Returns the subsequent interval for invoking the registered action * listeners in the auto-repeat action mode. * * @return The subsequent interval for invoking the registered action * listeners in the auto-repeat action mode. * @see #setAutoRepeatActionIntervals(int, int) * @see #setAutoRepeatAction(boolean) * @see #isAutoRepeatAction() * @see #getAutoRepeatInitialInterval() */ public int getAutoRepeatSubsequentInterval() { return autoRepeatSubsequentInterval; } /** * Sets action-on-rollover mode. When this mode is on, button will fire * action events when it gets rollover (instead of press). Combine with * {@link #setAutoRepeatAction(boolean)} passing true to get * auto-repeat action fired on rollover (useful for quicker manipulation of * scroller buttons, for example). * * @param isFireActionOnRollover * If true, the button is moved into the * action-on-rollover mode. * @see #isFireActionOnRollover() */ public void setFireActionOnRollover(boolean isFireActionOnRollover) { this.isFireActionOnRollover = isFireActionOnRollover; } /** * Returns indication whether this button is in action-on-rollover mode. * * @return true if this button is in action-on-rollover mode, * false otherwise. * @see #setFireActionOnRollover(boolean) */ public boolean isFireActionOnRollover() { return this.isFireActionOnRollover; } /** * Returns the popup model of this button. * * @return The popup model of this button. * @see #setPopupModel(PopupButtonModel) */ public PopupButtonModel getPopupModel() { return this.popupModel; } /** * Sets the new popup model for this button. Fires a popupModel * property change event. * * @param newModel * The new popup model for this button. * @see #getPopupModel() */ public void setPopupModel(PopupButtonModel newModel) { PopupButtonModel oldModel = getPopupModel(); if (oldModel != null) { oldModel.removeChangeListener(this.popupHandler); oldModel.removeActionListener(this.popupHandler); } this.popupModel = newModel; if (newModel != null) { newModel.addChangeListener(this.popupHandler); newModel.addActionListener(this.popupHandler); } firePropertyChange("popupModel", oldModel, newModel); if (newModel != oldModel) { revalidate(); repaint(); } } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.AbstractCommandButton#setEnabled(boolean) */ @Override public void setEnabled(boolean b) { if (!b && popupModel.isRollover()) { popupModel.setRollover(false); } super.setEnabled(b); popupModel.setEnabled(b); } /** * Default popup handler. * * @author Kirill Grouchnikov */ class PopupHandler implements PopupActionListener, ChangeListener { public void stateChanged(ChangeEvent e) { fireStateChanged(); repaint(); } public void actionPerformed(ActionEvent event) { firePopupActionPerformed(event); } } /** * Notifies all listeners that have registered interest for notification on * this event type. The event instance is lazily created using the * event parameter. * * @param event * the ActionEvent object * @see EventListenerList */ protected void firePopupActionPerformed(ActionEvent event) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); ActionEvent e = null; // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == PopupActionListener.class) { // Lazily create the event: if (e == null) { String actionCommand = event.getActionCommand(); e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand, event.getWhen(), event .getModifiers()); } ((PopupActionListener) listeners[i + 1]).actionPerformed(e); } } } @Override boolean hasRichTooltips() { return super.hasRichTooltips() || (this.popupRichTooltip != null); } /** * Sets the rich tooltip for the popup area of this button. * * @param richTooltip * Rich tooltip for the popup area of this button. * @see #getRichTooltip(MouseEvent) * @see #setActionRichTooltip(RichTooltip) */ public void setPopupRichTooltip(RichTooltip richTooltip) { this.popupRichTooltip = richTooltip; RichToolTipManager richToolTipManager = RichToolTipManager .sharedInstance(); if (this.hasRichTooltips()) { richToolTipManager.registerComponent(this); } else { richToolTipManager.unregisterComponent(this); } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AbstractCommandButton#getRichTooltip(java.awt * .event.MouseEvent) */ @Override public RichTooltip getRichTooltip(MouseEvent event) { CommandButtonUI ui = this.getUI(); if (ui.getLayoutInfo().actionClickArea.contains(event.getPoint())) return super.getRichTooltip(event); if (ui.getLayoutInfo().popupClickArea.contains(event.getPoint())) return this.popupRichTooltip; return null; } /** * Returns the key tip for the popup area of this button. * * @return The key tip for the popup area of this button. * @see #setPopupKeyTip(String) * @see #getActionKeyTip() */ public String getPopupKeyTip() { return this.popupKeyTip; } /** * Sets the key tip for the popup area of this button. Fires a * popupKeyTip property change event. * * @param popupKeyTip * The key tip for the popup area of this button. * @see #getPopupKeyTip() * @see #setActionKeyTip(String) */ public void setPopupKeyTip(String popupKeyTip) { if (!canHaveBothKeyTips() && (popupKeyTip != null) && (this.actionKeyTip != null)) { throw new IllegalArgumentException( "Action *and* popup keytips are not supported at the same time"); } String old = this.popupKeyTip; this.popupKeyTip = popupKeyTip; this.firePropertyChange("popupKeyTip", old, this.popupKeyTip); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AbstractCommandButton#setActionKeyTip(java. * lang.String) */ @Override public void setActionKeyTip(String actionKeyTip) { if (!canHaveBothKeyTips() && (popupKeyTip != null) && (this.actionKeyTip != null)) { throw new IllegalArgumentException( "Action *and* popup keytips are not supported at the same time"); } super.setActionKeyTip(actionKeyTip); } /** * Returns indication whether key tips can be installed on both action and * popup areas of this button. This method is for internal use only. * * @return true if key tips can be installed on both action and * popup areas of this button, false otherwise. */ boolean canHaveBothKeyTips() { return false; } /** * Programmatically perform a "click" on the popup area. This does the same * thing as if the user had pressed and released the popup area of the * button. */ public void doPopupClick() { Dimension size = getSize(); PopupButtonModel popupModel = this.getPopupModel(); popupModel.setArmed(true); popupModel.setPressed(true); paintImmediately(new Rectangle(0, 0, size.width, size.height)); try { Thread.sleep(100); } catch (InterruptedException ie) { } popupModel.setPressed(false); popupModel.setArmed(false); popupModel.setPopupShowing(true); paintImmediately(new Rectangle(0, 0, size.width, size.height)); } } src/org/pushingpixels/flamingo/api/common/JCommandButtonStrip.java0000644000175000017500000002646611401230444024517 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.Component; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonStripUI; import org.pushingpixels.flamingo.internal.ui.common.CommandButtonStripUI; /** * Button strip component. Provides visual appearance of a strip. The buttons in * the strip are either drawn horizontally with no horizontal space between them * or drawn vertically with no vertical space between them. * * @author Kirill Grouchnikov */ public class JCommandButtonStrip extends JComponent { /** * The UI class ID string. */ public static final String uiClassID = "CommandButtonStripUI"; /** * Element state for the buttons in this button strip. Default state is * {@link CommandButtonDisplayState#SMALL}. */ protected CommandButtonDisplayState displayState; /** * Scale factor for horizontal gaps. * * @see #setVGapScaleFactor(double) */ protected double hgapScaleFactor; /** * Scale factor for vertical gaps. * * @see #setVGapScaleFactor(double) */ protected double vgapScaleFactor; /** * Button strip orientation. * * @author Kirill Grouchnikov */ public enum StripOrientation { /** * Horizontal strip orientation. */ HORIZONTAL, /** * Vertical strip orientation. */ VERTICAL } /** * Orientation of this strip. * * @see #getOrientation() */ private StripOrientation orientation; /** * Creates an empty horizontally-oriented strip. */ public JCommandButtonStrip() { this(StripOrientation.HORIZONTAL); } /** * Creates an empty strip. * * @param orientation * Orientation for this strip. */ public JCommandButtonStrip(StripOrientation orientation) { this.orientation = orientation; this.displayState = CommandButtonDisplayState.SMALL; switch (orientation) { case HORIZONTAL: this.hgapScaleFactor = 0.75; this.vgapScaleFactor = 1.0; break; case VERTICAL: this.hgapScaleFactor = 1.0; this.vgapScaleFactor = 0.75; break; } this.setOpaque(false); updateUI(); } /** * Sets the display state for the buttons in this button strip. This method * must be called before adding the first command button. The * default state is {@link CommandButtonDisplayState#SMALL}. * * @param elementState * New element state for the buttons in this button strip. */ public void setDisplayState(CommandButtonDisplayState elementState) { if (this.getComponentCount() > 0) { throw new IllegalStateException( "Can't call this method after buttons have been already added"); } this.displayState = elementState; } /** * Sets the horizontal gap scale factor for the buttons in this button * strip. This method must be called before adding the first * command button. * *

* The default horizontal gap scale factor for horizontally oriented strips * is 0.75. The default horizontal gap scale factor for vertically oriented * strips is 1.0. *

* * @param hgapScaleFactor * New horizontal gap scale factor for the buttons in this button * strip. * @see #setVGapScaleFactor(double) */ public void setHGapScaleFactor(double hgapScaleFactor) { if (this.getComponentCount() > 0) { throw new IllegalStateException( "Can't call this method after buttons have been already added"); } this.hgapScaleFactor = hgapScaleFactor; } /** * Sets the vertical gap scale factor for the buttons in this button strip. * This method must be called before adding the first command * button. * *

* The default vertical gap scale factor for vertically oriented strips is * 0.75. The default vertical gap scale factor for horizontally oriented * strips is 1.0. *

* * @param vgapScaleFactor * New vertical gap scale factor for the buttons in this button * strip. * @see #setHGapScaleFactor(double) */ public void setVGapScaleFactor(double vgapScaleFactor) { if (this.getComponentCount() > 0) { throw new IllegalStateException( "Can't call this method after buttons have been already added"); } this.vgapScaleFactor = vgapScaleFactor; } /* * (non-Javadoc) * * @see java.awt.Container#add(java.awt.Component, java.lang.Object, int) */ @Override public void add(Component comp, Object constraints, int index) { throw new UnsupportedOperationException(); } /* * (non-Javadoc) * * @see java.awt.Container#add(java.awt.Component, java.lang.Object) */ @Override public void add(Component comp, Object constraints) { throw new UnsupportedOperationException(); } /* * (non-Javadoc) * * @see java.awt.Container#add(java.awt.Component, int) */ @Override public Component add(Component comp, int index) { if (!(comp instanceof AbstractCommandButton)) throw new UnsupportedOperationException(); this.configureCommandButton((AbstractCommandButton) comp); return super.add(comp, index); } /* * (non-Javadoc) * * @see java.awt.Container#add(java.awt.Component) */ @Override public Component add(Component comp) { if (!(comp instanceof AbstractCommandButton)) throw new UnsupportedOperationException(); try { this.configureCommandButton((AbstractCommandButton) comp); Component result = super.add(comp); return result; } finally { this.fireStateChanged(); } } /** * Configures the specified command button. * * @param button * Command button to configure. */ private void configureCommandButton(AbstractCommandButton button) { button.setDisplayState(this.displayState); button.setHGapScaleFactor(this.hgapScaleFactor); button.setVGapScaleFactor(this.vgapScaleFactor); button.setFlat(false); } /* * (non-Javadoc) * * @see java.awt.Container#add(java.lang.String, java.awt.Component) */ @Override public Component add(String name, Component comp) { throw new UnsupportedOperationException(); } /** * Sets the new UI delegate. * * @param ui * New UI delegate. */ public void setUI(CommandButtonStripUI ui) { super.setUI(ui); } /** * Resets the UI property to a value from the current look and feel. * * @see JComponent#updateUI */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((CommandButtonStripUI) UIManager.getUI(this)); } else { setUI(BasicCommandButtonStripUI.createUI(this)); } } /** * Returns the UI object which implements the L&F for this component. * * @return a ButtonStripUI object * @see #setUI */ public CommandButtonStripUI getUI() { return (CommandButtonStripUI) ui; } /** * Returns the name of the UI class that implements the L&F for this * component. * * @return the string "ButtonStripUI" * @see JComponent#getUIClassID * @see UIDefaults#getUI */ @Override public String getUIClassID() { return uiClassID; } /** * Returns the number of buttons in this strip. * * @return Number of buttons in this strip. * @see #getButton(int) */ public int getButtonCount() { return this.getComponentCount(); } /** * Returns the specified button component of this strip. * * @param index * Button index. * @return The matching button. * @see #getButtonCount() */ public AbstractCommandButton getButton(int index) { return (AbstractCommandButton) this.getComponent(index); } /** * Checks whether the specified button is the first button in * this strip. * * @param button * Button to check. * @return true if the specified button is the first button in * this strip, false otherwise. * @see #isLast(AbstractCommandButton) */ public boolean isFirst(AbstractCommandButton button) { return (button == this.getButton(0)); } /** * Checks whether the specified button is the last button in * this strip. * * @param button * Button to check. * @return true if the specified button is the last button in * this strip, false otherwise. * @see #isFirst(AbstractCommandButton) */ public boolean isLast(AbstractCommandButton button) { return (button == this.getButton(this.getButtonCount() - 1)); } /** * Returns the orientation of this strip. * * @return Orientation of this strip. */ public StripOrientation getOrientation() { return orientation; } /** * Adds the specified change listener to track changes to this command * button strip. * * @param l * Change listener to add. * @see #removeChangeListener(ChangeListener) */ public void addChangeListener(ChangeListener l) { this.listenerList.add(ChangeListener.class, l); } /** * Removes the specified change listener from tracking changes to this * command button strip. * * @param l * Change listener to remove. * @see #addChangeListener(ChangeListener) */ public void removeChangeListener(ChangeListener l) { this.listenerList.remove(ChangeListener.class, l); } /** * Notifies all registered listener that the state of this command button * strip has changed. */ protected void fireStateChanged() { // Guaranteed to return a non-null array Object[] listeners = this.listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event ChangeEvent event = new ChangeEvent(this); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { ((ChangeListener) listeners[i + 1]).stateChanged(event); } } } } src/org/pushingpixels/flamingo/api/common/JScrollablePanel.java0000644000175000017500000001065111401230444023752 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.pushingpixels.flamingo.internal.ui.common.BasicScrollablePanelUI; import org.pushingpixels.flamingo.internal.ui.common.ScrollablePanelUI; /** * ScrollablePanel allows to have scrolling buttons on each side. */ public class JScrollablePanel extends JPanel { /** * @see #getUIClassID */ public static final String uiClassID = "ScrollablePanelUI"; private T view; private ScrollType scrollType; private boolean isScrollOnRollover; public enum ScrollType { VERTICALLY, HORIZONTALLY } public JScrollablePanel(T c, final ScrollType scrollType) { super(); this.view = c; this.scrollType = scrollType; this.isScrollOnRollover = true; this.updateUI(); } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUI() */ @Override public ScrollablePanelUI getUI() { return (ScrollablePanelUI) ui; } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /* * (non-Javadoc) * * @see javax.swing.JPanel#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((ScrollablePanelUI) UIManager.getUI(this)); } else { setUI(BasicScrollablePanelUI.createUI(this)); } } public void setScrollOnRollover(boolean toScrollOnRollover) { boolean old = this.isScrollOnRollover; this.isScrollOnRollover = toScrollOnRollover; if (old != this.isScrollOnRollover) { this.firePropertyChange("scrollOnRollover", old, this.isScrollOnRollover); } } public void scrollToIfNecessary(int startPosition, int span) { this.getUI().scrollToIfNecessary(startPosition, span); } public T getView() { return view; } public void addChangeListener(ChangeListener l) { listenerList.add(ChangeListener.class, l); } public void removeChangeListener(ChangeListener l) { listenerList.remove(ChangeListener.class, l); } protected void fireStateChanged() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event ChangeEvent changeEvent = new ChangeEvent(this); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent); } } } @Override public void doLayout() { super.doLayout(); this.fireStateChanged(); } public ScrollType getScrollType() { return scrollType; } public boolean isScrollOnRollover() { return this.isScrollOnRollover; } public boolean isShowingScrollButtons() { return this.getUI().isShowingScrollButtons(); } } src/org/pushingpixels/flamingo/api/common/JCommandToggleMenuButton.java0000644000175000017500000000602511401230444025451 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.popup.JCommandPopupMenu; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandToggleMenuButtonUI; import org.pushingpixels.flamingo.internal.ui.common.CommandButtonUI; /** * A command toggle button that can be placed in {@link JCommandPopupMenu}. * * @author Kirill Grouchnikov * @see JCommandPopupMenu#addMenuButton(JCommandToggleMenuButton) */ public class JCommandToggleMenuButton extends JCommandToggleButton { /** * The UI class ID string. */ public static final String uiClassID = "CommandToggleMenuButtonUI"; /** * Creates a new command toggle menu button. * * @param title * Command toggle menu button title. * @param icon * Command toggle menu button icon. */ public JCommandToggleMenuButton(String title, ResizableIcon icon) { super(title, icon); } /* * (non-Javadoc) * * @see javax.swing.JComponent#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /* * (non-Javadoc) * * @see javax.swing.JComponent#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((CommandButtonUI) UIManager.getUI(this)); } else { setUI(BasicCommandToggleMenuButtonUI.createUI(this)); } } } src/org/pushingpixels/flamingo/api/common/AbstractCommandButton.java0000644000175000017500000005505011401230444025036 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.event.*; import javax.accessibility.AccessibleContext; import javax.swing.ButtonModel; import javax.swing.SwingConstants; import javax.swing.event.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.model.ActionButtonModel; import org.pushingpixels.flamingo.internal.ui.common.CommandButtonUI; /** * Base class for command buttons. * * @author Kirill Grouchnikov */ public abstract class AbstractCommandButton extends RichToolTipManager.JTrackableComponent { /** * Associated icon. * * @see #setIcon(ResizableIcon) * @see #getIcon() */ protected ResizableIcon icon; /** * Associated disabled icon. * * @see #setDisabledIcon(ResizableIcon) * @see #getDisabledIcon() */ protected ResizableIcon disabledIcon; /** * The button text. * * @see #setText(String) * @see #getText() */ private String text; /** * The button action model. * * @see #getActionModel() * @see #setActionModel(ActionButtonModel) */ protected ActionButtonModel actionModel; /** * Additional text. This is shown for {@link CommandButtonDisplayState#TILE} * . * * @see #setExtraText(String) * @see #getExtraText() */ protected String extraText; /** * Current display state of this button. * * @see #setDisplayState(CommandButtonDisplayState) * @see #getDisplayState() */ protected CommandButtonDisplayState displayState; /** * The dimension of the icon of the associated command button in the * {@link CommandButtonDisplayState#FIT_TO_ICON} state. * * @see #getCustomDimension() * @see #updateCustomDimension(int) */ protected int customDimension; /** * Indication whether this button is flat. * * @see #setFlat(boolean) * @see #isFlat() */ protected boolean isFlat; /** * Horizontal alignment of the content. * * @see #setHorizontalAlignment(int) * @see #getHorizontalAlignment() */ private int horizontalAlignment; /** * Scale factor for horizontal gaps. * * @see #setHGapScaleFactor(double) * @see #getHGapScaleFactor() */ private double hgapScaleFactor; /** * Scale factor for vertical gaps. * * @see #setVGapScaleFactor(double) * @see #getVGapScaleFactor() */ private double vgapScaleFactor; /** * Rich tooltip for the action area. * * @see #setActionRichTooltip(RichTooltip) * @see #getRichTooltip(MouseEvent) */ private RichTooltip actionRichTooltip; /** * Location order kind for buttons placed in command button strips or for * buttons that need the visuals of segmented strips. * * @see #setLocationOrderKind(CommandButtonLocationOrderKind) * @see #getLocationOrderKind() */ private CommandButtonLocationOrderKind locationOrderKind; /** * Action handler for the button. */ protected ActionHandler actionHandler; /** * Key tip for the action area. * * @see #setActionKeyTip(String) * @see #getActionKeyTip() */ protected String actionKeyTip; /** * Enumerates the available values for the location order kind. This is used * for buttons placed in command button strips or for buttons that need the * visuals of segmented strips. * * @author Kirill Grouchnikov */ public static enum CommandButtonLocationOrderKind { /** * Indicates that this button is the only button in the strip. */ ONLY, /** * Indicates that this button is the first button in the strip. */ FIRST, /** * Indicates that this button is in the middle of the strip. */ MIDDLE, /** * Indicates that this button is the last button in the strip. */ LAST } /** * Creates a new command button. * * @param text * Button title. May contain any number of words. * @param icon * Button icon. */ public AbstractCommandButton(String text, ResizableIcon icon) { this.icon = icon; this.customDimension = -1; this.displayState = CommandButtonDisplayState.FIT_TO_ICON; this.horizontalAlignment = SwingConstants.CENTER; this.actionHandler = new ActionHandler(); this.isFlat = true; this.hgapScaleFactor = 1.0; this.vgapScaleFactor = 1.0; this.setText(text); this.setOpaque(false); } /** * Sets the new UI delegate. * * @param ui * New UI delegate. */ public void setUI(CommandButtonUI ui) { super.setUI(ui); } /** * Returns the UI delegate for this button. * * @return The UI delegate for this button. */ public CommandButtonUI getUI() { return (CommandButtonUI) ui; } /** * Sets new display state for this button. Fires a * displayState property change event. * * @param state * New display state. * @see #getDisplayState() */ public void setDisplayState(CommandButtonDisplayState state) { CommandButtonDisplayState old = this.displayState; this.displayState = state; this.firePropertyChange("displayState", old, this.displayState); } /** * Returns the associated icon. * * @return The associated icon. * @see #getDisabledIcon() * @see #setIcon(ResizableIcon) */ public ResizableIcon getIcon() { return icon; } /** * Sets new icon for this button. Fires an icon property change * event. * * @param defaultIcon * New default icon for this button. * @see #setDisabledIcon(ResizableIcon) * @see #getIcon() */ public void setIcon(ResizableIcon defaultIcon) { ResizableIcon oldValue = this.icon; this.icon = defaultIcon; firePropertyChange("icon", oldValue, defaultIcon); if (defaultIcon != oldValue) { if (defaultIcon == null || oldValue == null || defaultIcon.getIconWidth() != oldValue.getIconWidth() || defaultIcon.getIconHeight() != oldValue.getIconHeight()) { revalidate(); } repaint(); } } /** * Sets the disabled icon for this button. * * @param disabledIcon * Disabled icon for this button. * @see #setIcon(ResizableIcon) * @see #getDisabledIcon() */ public void setDisabledIcon(ResizableIcon disabledIcon) { this.disabledIcon = disabledIcon; } /** * Returns the associated disabled icon. * * @return The associated disabled icon. * @see #setDisabledIcon(ResizableIcon) * @see #getIcon() */ public ResizableIcon getDisabledIcon() { return disabledIcon; } /** * Return the current display state of this button. * * @return The current display state of this button. * @see #setDisplayState(CommandButtonDisplayState) */ public CommandButtonDisplayState getDisplayState() { return displayState; } /** * Returns the extra text of this button. * * @return Extra text of this button. * @see #setExtraText(String) */ public String getExtraText() { return this.extraText; } /** * Sets the extra text for this button. Fires an extraText * property change event. * * @param extraText * Extra text for this button. * @see #getExtraText() */ public void setExtraText(String extraText) { String oldValue = this.extraText; this.extraText = extraText; firePropertyChange("extraText", oldValue, extraText); if (accessibleContext != null) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, oldValue, extraText); } if (extraText == null || oldValue == null || !extraText.equals(oldValue)) { revalidate(); repaint(); } } /** * Returns the text of this button. * * @return The text of this button. * @see #setText(String) */ public String getText() { return this.text; } /** * Sets the new text for this button. Fires a text property * change event. * * @param text * The new text for this button. * @see #getText() */ public void setText(String text) { String oldValue = this.text; this.text = text; firePropertyChange("text", oldValue, text); if (accessibleContext != null) { accessibleContext.firePropertyChange( AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, oldValue, text); } if (text == null || oldValue == null || !text.equals(oldValue)) { revalidate(); repaint(); } } /** * Updates the dimension of the icon of the associated command button in the * {@link CommandButtonDisplayState#FIT_TO_ICON} state. Fires a * customDimension property change event. * * @param dimension * New dimension of the icon of the associated command button in * the {@link CommandButtonDisplayState#FIT_TO_ICON} state. * @see #getCustomDimension() */ public void updateCustomDimension(int dimension) { if (this.customDimension != dimension) { int old = this.customDimension; this.customDimension = dimension; this.firePropertyChange("customDimension", old, this.customDimension); } } /** * Returns the dimension of the icon of the associated command button in the * {@link CommandButtonDisplayState#FIT_TO_ICON} state. * * @return The dimension of the icon of the associated command button in the * {@link CommandButtonDisplayState#FIT_TO_ICON} state. * @see #updateCustomDimension(int) */ public int getCustomDimension() { return this.customDimension; } /** * Returns indication whether this button has flat appearance. * * @return true if this button has flat appearance, * false otherwise. * @see #setFlat(boolean) */ public boolean isFlat() { return this.isFlat; } /** * Sets the flat appearance of this button. Fires a flat * property change event. * * @param isFlat * If true, this button will have flat appearance, * otherwise this button will not have flat appearance. * @see #isFlat() */ public void setFlat(boolean isFlat) { boolean old = this.isFlat; this.isFlat = isFlat; if (old != this.isFlat) { this.firePropertyChange("flat", old, this.isFlat); } if (old != isFlat) { repaint(); } } /** * Returns the action model for this button. * * @return The action model for this button. * @see #setActionModel(ActionButtonModel) */ public ActionButtonModel getActionModel() { return this.actionModel; } /** * Sets the new action model for this button. Fires an * actionModel property change event. * * @param newModel * The new action model for this button. * @see #getActionModel() */ public void setActionModel(ActionButtonModel newModel) { ButtonModel oldModel = getActionModel(); if (oldModel != null) { oldModel.removeChangeListener(this.actionHandler); oldModel.removeActionListener(this.actionHandler); } actionModel = newModel; if (newModel != null) { newModel.addChangeListener(this.actionHandler); newModel.addActionListener(this.actionHandler); } firePropertyChange("actionModel", oldModel, newModel); if (newModel != oldModel) { revalidate(); repaint(); } } /** * Adds the specified action listener to this button. * * @param l * Action listener to add. * @see #removeActionListener(ActionListener) */ public void addActionListener(ActionListener l) { this.listenerList.add(ActionListener.class, l); } /** * Removes the specified action listener from this button. * * @param l * Action listener to remove. * @see #addActionListener(ActionListener) */ public void removeActionListener(ActionListener l) { this.listenerList.remove(ActionListener.class, l); } /** * Adds the specified change listener to this button. * * @param l * Change listener to add. * @see #removeChangeListener(ChangeListener) */ public void addChangeListener(ChangeListener l) { this.listenerList.add(ChangeListener.class, l); } /** * Removes the specified change listener from this button. * * @param l * Change listener to remove. * @see #addChangeListener(ChangeListener) */ public void removeChangeListener(ChangeListener l) { this.listenerList.remove(ChangeListener.class, l); } /* * (non-Javadoc) * * @see javax.swing.JComponent#setEnabled(boolean) */ @Override public void setEnabled(boolean b) { if (!b && actionModel.isRollover()) { actionModel.setRollover(false); } super.setEnabled(b); actionModel.setEnabled(b); } /** * Default action handler for this button. * * @author Kirill Grouchnikov */ class ActionHandler implements ActionListener, ChangeListener { public void stateChanged(ChangeEvent e) { fireStateChanged(); repaint(); } public void actionPerformed(ActionEvent event) { fireActionPerformed(event); } } /** * Notifies all listeners that have registered interest for notification on * this event type. The event instance is lazily created. * * @see EventListenerList */ protected void fireStateChanged() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event ChangeEvent ce = new ChangeEvent(this); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { // Lazily create the event: ((ChangeListener) listeners[i + 1]).stateChanged(ce); } } } /** * Notifies all listeners that have registered interest for notification on * this event type. The event instance is lazily created using the * event parameter. * * @param event * the ActionEvent object * @see EventListenerList */ protected void fireActionPerformed(ActionEvent event) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); ActionEvent e = null; // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ActionListener.class) { // Lazily create the event: if (e == null) { String actionCommand = event.getActionCommand(); e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand, event.getWhen(), event .getModifiers()); } ((ActionListener) listeners[i + 1]).actionPerformed(e); } } } /** * Sets new horizontal alignment for the content of this button. Fires a * horizontalAlignment property change event. * * @param alignment * New horizontal alignment for the content of this button. * @see #getHorizontalAlignment() */ public void setHorizontalAlignment(int alignment) { if (alignment == this.horizontalAlignment) return; int oldValue = this.horizontalAlignment; this.horizontalAlignment = alignment; firePropertyChange("horizontalAlignment", oldValue, this.horizontalAlignment); repaint(); } /** * Returns the horizontal alignment for the content of this button. * * @return The horizontal alignment for the content of this button. * @see #setHorizontalAlignment(int) */ public int getHorizontalAlignment() { return this.horizontalAlignment; } /** * Sets new horizontal gap scale factor for the content of this button. * Fires an hgapScaleFactor property change event. * * @param hgapScaleFactor * New horizontal gap scale factor for the content of this * button. * @see #getHGapScaleFactor() * @see #setVGapScaleFactor(double) * @see #setGapScaleFactor(double) */ public void setHGapScaleFactor(double hgapScaleFactor) { if (hgapScaleFactor == this.hgapScaleFactor) return; double oldValue = this.hgapScaleFactor; this.hgapScaleFactor = hgapScaleFactor; firePropertyChange("hgapScaleFactor", oldValue, this.hgapScaleFactor); if (this.hgapScaleFactor != oldValue) { revalidate(); repaint(); } } /** * Sets new vertical gap scale factor for the content of this button. Fires * a vgapScaleFactor property change event. * * @param vgapScaleFactor * New vertical gap scale factor for the content of this button. * @see #getVGapScaleFactor() * @see #setHGapScaleFactor(double) * @see #setGapScaleFactor(double) */ public void setVGapScaleFactor(double vgapScaleFactor) { if (vgapScaleFactor == this.vgapScaleFactor) return; double oldValue = this.vgapScaleFactor; this.vgapScaleFactor = vgapScaleFactor; firePropertyChange("vgapScaleFactor", oldValue, this.vgapScaleFactor); if (this.vgapScaleFactor != oldValue) { revalidate(); repaint(); } } /** * Sets new gap scale factor for the content of this button. * * @param gapScaleFactor * New gap scale factor for the content of this button. * @see #getHGapScaleFactor() * @see #getVGapScaleFactor() */ public void setGapScaleFactor(double gapScaleFactor) { setHGapScaleFactor(gapScaleFactor); setVGapScaleFactor(gapScaleFactor); } /** * Returns the horizontal gap scale factor for the content of this button. * * @return The horizontal gap scale factor for the content of this button. * @see #setHGapScaleFactor(double) * @see #setGapScaleFactor(double) * @see #getVGapScaleFactor() */ public double getHGapScaleFactor() { return this.hgapScaleFactor; } /** * Returns the vertical gap scale factor for the content of this button. * * @return The vertical gap scale factor for the content of this button. * @see #setVGapScaleFactor(double) * @see #setGapScaleFactor(double) * @see #getHGapScaleFactor() */ public double getVGapScaleFactor() { return this.vgapScaleFactor; } /** * Programmatically perform an action "click". This does the same thing as * if the user had pressed and released the action area of the button. */ public void doActionClick() { Dimension size = getSize(); ButtonModel actionModel = this.getActionModel(); actionModel.setArmed(true); actionModel.setPressed(true); paintImmediately(new Rectangle(0, 0, size.width, size.height)); try { Thread.sleep(100); } catch (InterruptedException ie) { } actionModel.setPressed(false); actionModel.setArmed(false); } boolean hasRichTooltips() { return (this.actionRichTooltip != null); } /** * Sets the rich tooltip for the action area of this button. * * @param richTooltip * Rich tooltip for the action area of this button. * @see #getRichTooltip(MouseEvent) */ public void setActionRichTooltip(RichTooltip richTooltip) { this.actionRichTooltip = richTooltip; RichToolTipManager richToolTipManager = RichToolTipManager .sharedInstance(); if (this.hasRichTooltips()) { richToolTipManager.registerComponent(this); } else { richToolTipManager.unregisterComponent(this); } } /* * (non-Javadoc) * * @seeorg.jvnet.flamingo.common.RichToolTipManager.JTrackableComponent# * getRichTooltip(java.awt.event.MouseEvent) */ @Override public RichTooltip getRichTooltip(MouseEvent mouseEvent) { return this.actionRichTooltip; } /* * (non-Javadoc) * * @see javax.swing.JComponent#setToolTipText(java.lang.String) */ @Override public void setToolTipText(String text) { throw new UnsupportedOperationException("Use rich tooltip APIs"); } /** * Returns the location order kind for buttons placed in command button * strips or for buttons that need the visuals of segmented strips. * * @return The location order kind for buttons placed in command button * strips or for buttons that need the visuals of segmented strips. * @see #setLocationOrderKind(CommandButtonLocationOrderKind) */ public CommandButtonLocationOrderKind getLocationOrderKind() { return this.locationOrderKind; } /** * Sets the location order kind for buttons placed in command button strips * or for buttons that need the visuals of segmented strips. Fires a * locationOrderKind property change event. * * @param locationOrderKind * The location order kind for buttons placed in command button * strips or for buttons that need the visuals of segmented * strips. * @see #getLocationOrderKind() */ public void setLocationOrderKind( CommandButtonLocationOrderKind locationOrderKind) { CommandButtonLocationOrderKind old = this.locationOrderKind; if (old != locationOrderKind) { this.locationOrderKind = locationOrderKind; this.firePropertyChange("locationOrderKind", old, this.locationOrderKind); } } /** * Returns the key tip for the action area of this button. * * @return The key tip for the action area of this button. * @see #setActionKeyTip(String) */ public String getActionKeyTip() { return this.actionKeyTip; } /** * Sets the key tip for the action area of this button. Fires an * actionKeyTip property change event. * * @param actionKeyTip * The key tip for the action area of this button. * @see #getActionKeyTip() */ public void setActionKeyTip(String actionKeyTip) { String old = this.actionKeyTip; this.actionKeyTip = actionKeyTip; this.firePropertyChange("actionKeyTip", old, this.actionKeyTip); } } src/org/pushingpixels/flamingo/api/common/CommandButtonDisplayState.java0000644000175000017500000001201011401230444025666 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import org.pushingpixels.flamingo.internal.ui.common.*; /** * Display state for command buttons. This class provides a number of core * display states, and it is possible to create additional custom states by * using the protected constructor and implementing the relevant abstract * methods. * * @author Kirill Grouchnikov */ public abstract class CommandButtonDisplayState { /** * Fit to icon state. */ public static final CommandButtonDisplayState FIT_TO_ICON = new CommandButtonDisplayState( "Fit to icon", -1) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton commandButton) { return new CommandButtonLayoutManagerCustom(commandButton); } }; /** * Big state. */ public static final CommandButtonDisplayState BIG = new CommandButtonDisplayState( "Big", 32) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton commandButton) { return new CommandButtonLayoutManagerBig(commandButton); } }; /** * Tile state. */ public static final CommandButtonDisplayState TILE = new CommandButtonDisplayState( "Tile", 32) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton arg0) { return new CommandButtonLayoutManagerTile(); } }; /** * Medium state. */ public static final CommandButtonDisplayState MEDIUM = new CommandButtonDisplayState( "Medium", 16) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton arg0) { return new CommandButtonLayoutManagerMedium(); } }; /** * Small state. */ public static final CommandButtonDisplayState SMALL = new CommandButtonDisplayState( "Small", 16) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton arg0) { return new CommandButtonLayoutManagerSmall(); } }; /** * Preferred icon size for this state. * * @see #CommandButtonDisplayState(String, int) * @see #getPreferredIconSize() */ int preferredIconSize; /** * Display name for this state. * * @see #CommandButtonDisplayState(String, int) * @see #getDisplayName() */ String displayName; /** * Creates a new element state. * * @param displayName * Display name. * @param preferredIconSize * Preferred icon size. */ protected CommandButtonDisplayState(String displayName, int preferredIconSize) { this.displayName = displayName; this.preferredIconSize = preferredIconSize; } /** * Returns the display name for this state. * * @return The display name for this state. * @see #CommandButtonDisplayState(String, int) */ public String getDisplayName() { return this.displayName; } /** * Returns the preferred icon size for this state. * * @return The preferred icon size for this state. * @see #CommandButtonDisplayState(String, int) */ public int getPreferredIconSize() { return this.preferredIconSize; } /** * Creates a layout manager for the specified button. * * @param commandButton * Command button. * @return A layout manager for the specified button. */ public abstract CommandButtonLayoutManager createLayoutManager( AbstractCommandButton commandButton); /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return this.getDisplayName(); } } src/org/pushingpixels/flamingo/api/common/icon/0000755000175000017500000000000011401230444020660 5ustar tonytonysrc/org/pushingpixels/flamingo/api/common/icon/LayeredIcon.java0000644000175000017500000000563511401230444023732 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.*; /** * Decorator icon that layers icons one on top of the other. The original icons * are drawn and resized together as one layered stack. * * @author Kirill Grouchnikov */ public class LayeredIcon implements ResizableIcon { /** * The layer icons. */ protected ResizableIcon[] layers; /** * Creates a new layered icon. * * @param layers * Layer icons. */ public LayeredIcon(ResizableIcon... layers) { this.layers = layers; } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.ResizableIcon#setDimension(java.awt.Dimension) */ public void setDimension(Dimension newDimension) { for (ResizableIcon layer : layers) layer.setDimension(newDimension); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return layers[0].getIconHeight(); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return layers[0].getIconWidth(); } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ public void paintIcon(Component c, Graphics g, int x, int y) { for (ResizableIcon layer : layers) layer.paintIcon(c, g, x, y); } } src/org/pushingpixels/flamingo/api/common/icon/DecoratedResizableIcon.java0000644000175000017500000001451411401230444026074 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.*; import java.util.ArrayList; import org.pushingpixels.flamingo.api.common.AsynchronousLoadListener; import org.pushingpixels.flamingo.api.common.AsynchronousLoading; /** * Implementation of {@link ResizableIcon} that adds decorations to a main icon. * * @author Kirill Grouchnikov */ public class DecoratedResizableIcon implements ResizableIcon, AsynchronousLoading { /** * The main delegate icon. */ protected ResizableIcon delegate; /** * List of icon decorators. */ protected java.util.List decorators; /** * Icon decorator interface. * * @author Kirill Grouchnikov */ public static interface IconDecorator { /** * Paints the icon decoration. * * @param c * Component. * @param g * Graphics context. * @param mainIconX * X position of main icon painting. * @param mainIconY * Y position of main icon painting. * @param mainIconWidth * Width of main icon. * @param mainIconHeight * Height of main icon. */ public void paintIconDecoration(Component c, Graphics g, int mainIconX, int mainIconY, int mainIconWidth, int mainIconHeight); } /** * Creates a new decorated icon. * * @param delegate * The main icon. * @param decorators * Icon decorators. */ public DecoratedResizableIcon(ResizableIcon delegate, IconDecorator... decorators) { this.delegate = delegate; this.decorators = new ArrayList(); if (decorators != null) { for (IconDecorator decorator : decorators) { this.decorators.add(decorator); } } } /** * Creates a new decorated icon with no decorators. Decorators can be added * later with {@link #addIconDecorator(IconDecorator)}. * * @param delegate * Main icon. */ public DecoratedResizableIcon(ResizableIcon delegate) { this(delegate, (IconDecorator) null); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return this.delegate.getIconHeight(); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return this.delegate.getIconWidth(); } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ public void paintIcon(Component c, Graphics g, int x, int y) { this.delegate.paintIcon(c, g, x, y); for (IconDecorator decorator : this.decorators) { decorator.paintIconDecoration(c, g, x, y, this.delegate .getIconWidth(), this.delegate.getIconHeight()); } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.icon.ResizableIcon#setDimension(java.awt.Dimension * ) */ public void setDimension(Dimension newDimension) { this.delegate.setDimension(newDimension); } /** * Adds the specified decorator to the end of the decorator sequence. If the * specified decorator already exists, it is not moved to the end of the * sequence. * * @param decorator * Decorator to add. */ public void addIconDecorator(IconDecorator decorator) { if (this.decorators.contains(decorator)) return; this.decorators.add(decorator); } /** * Removes the specified decorator. * * @param decorator * Decorator to remove. */ public void removeIconDecorator(IconDecorator decorator) { this.decorators.remove(decorator); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#addAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ @Override public void addAsynchronousLoadListener(AsynchronousLoadListener l) { if (this.delegate instanceof AsynchronousLoading) { ((AsynchronousLoading) this.delegate) .addAsynchronousLoadListener(l); } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#removeAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ @Override public void removeAsynchronousLoadListener(AsynchronousLoadListener l) { if (this.delegate instanceof AsynchronousLoading) { ((AsynchronousLoading) this.delegate) .removeAsynchronousLoadListener(l); } } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.AsynchronousLoading#isLoading() */ @Override public synchronized boolean isLoading() { if (this.delegate instanceof AsynchronousLoading) { if (((AsynchronousLoading) this.delegate).isLoading()) return true; } for (IconDecorator decorator : this.decorators) { if (decorator instanceof AsynchronousLoading) { if (((AsynchronousLoading) decorator).isLoading()) return true; } } return false; } } src/org/pushingpixels/flamingo/api/common/icon/IconDeckResizableIcon.java0000644000175000017500000001200111401230444025646 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.*; import java.util.Map; import org.pushingpixels.flamingo.api.common.AsynchronousLoadListener; import org.pushingpixels.flamingo.api.common.AsynchronousLoading; /** * Implementation of the {@link ResizableIcon} that allows switching the icon * painting at runtime. This class can be used as a delegate in the * {@link DecoratedResizableIcon} where the "base" icon is changed at runtime * without the need to recompute all the decorators. * * @param * enumeration key into the deck * @author Kenneth Flynn flynnk@darkcornersoftware.com. */ public class IconDeckResizableIcon implements ResizableIcon, AsynchronousLoading { /** * Currently shown icon. */ private ResizableIcon currentIcon; /** * The icon deck. */ private final Map iconDeck; /** * Creates the icon deck. * * @param iconDeck * Icon deck. */ public IconDeckResizableIcon(Map iconDeck) { if (iconDeck.isEmpty()) throw new IllegalArgumentException( "Icon deck is empty; must have at least one icon"); this.iconDeck = iconDeck; this.currentIcon = iconDeck.values().iterator().next(); } /** * Sets the currently shown icon. * * @param key * Icon key. */ public void setIcon(T key) { this.currentIcon = iconDeck.get(key); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.icon.ResizableIcon#setDimension(java.awt.Dimension * ) */ public void setDimension(Dimension dim) { for (ResizableIcon icon : iconDeck.values()) { int currH = icon.getIconHeight(); int currW = icon.getIconWidth(); if ((currH != dim.height) || (currW != dim.width)) icon.setDimension(dim); } } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return currentIcon.getIconHeight(); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return currentIcon.getIconWidth(); } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ public void paintIcon(Component c, Graphics g, int x, int y) { currentIcon.paintIcon(c, g, x, y); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#addAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ public void addAsynchronousLoadListener(AsynchronousLoadListener l) { for (ResizableIcon icon : iconDeck.values()) { if (icon instanceof AsynchronousLoading) ((AsynchronousLoading) icon).addAsynchronousLoadListener(l); } } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.AsynchronousLoading#isLoading() */ public boolean isLoading() { for (ResizableIcon icon : iconDeck.values()) { if (icon instanceof AsynchronousLoading) { if (((AsynchronousLoading) icon).isLoading()) return true; } } return false; } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#removeAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ public void removeAsynchronousLoadListener(AsynchronousLoadListener l) { for (ResizableIcon icon : iconDeck.values()) { if (icon instanceof AsynchronousLoading) ((AsynchronousLoading) icon).removeAsynchronousLoadListener(l); } } } src/org/pushingpixels/flamingo/api/common/icon/ImageWrapperIcon.java0000644000175000017500000002116111401230444024720 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.*; import java.awt.image.BufferedImage; import java.io.InputStream; import java.util.LinkedHashMap; import java.util.Map; import javax.imageio.ImageIO; import javax.swing.Icon; import javax.swing.SwingWorker; import javax.swing.event.EventListenerList; import org.pushingpixels.flamingo.api.common.AsynchronousLoadListener; import org.pushingpixels.flamingo.api.common.AsynchronousLoading; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Helper class to load images and expose them as icons of dynamic size. * * @author Kirill Grouchnikov */ abstract class ImageWrapperIcon implements Icon, AsynchronousLoading { /** * The original image. */ protected BufferedImage originalImage; /** * The input stream of the original image. */ protected InputStream imageInputStream; /** * The input stream of the original image. */ protected Image image; /** * Contains all precomputed images. */ protected Map cachedImages; /** * The width of the current image. */ protected int width; /** * The height of the current image. */ protected int height; /** * The listeners. */ protected EventListenerList listenerList = new EventListenerList(); /** * Create a new image-wrapper icon. * * @param inputStream * The input stream to read the image from. * @param w * The width of the icon. * @param h * The height of the icon. */ public ImageWrapperIcon(InputStream inputStream, int w, int h) { this.imageInputStream = inputStream; this.width = w; this.height = h; this.listenerList = new EventListenerList(); this.cachedImages = new LinkedHashMap() { @Override protected boolean removeEldestEntry( Map.Entry eldest) { return size() > 5; }; }; this.renderImage(this.width, this.height); } /** * Create a new image-wrapper icon. * * @param image * The original image. * @param w * The width of the icon. * @param h * The height of the icon. */ public ImageWrapperIcon(Image image, int w, int h) { this.imageInputStream = null; this.image = image; this.width = w; this.height = h; this.listenerList = new EventListenerList(); this.cachedImages = new LinkedHashMap() { @Override protected boolean removeEldestEntry( Map.Entry eldest) { return size() > 5; }; }; this.renderImage(this.width, this.height); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#addAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ public void addAsynchronousLoadListener(AsynchronousLoadListener l) { this.listenerList.add(AsynchronousLoadListener.class, l); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#removeAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ public void removeAsynchronousLoadListener(AsynchronousLoadListener l) { this.listenerList.remove(AsynchronousLoadListener.class, l); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return width; } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return height; } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ public void paintIcon(Component c, Graphics g, int x, int y) { BufferedImage image = this.cachedImages.get(this.getIconWidth() + ":" + this.getIconHeight()); if (image != null) { int dx = (this.width - image.getWidth()) / 2; int dy = (this.height - image.getHeight()) / 2; g.drawImage(image, x + dx, y + dy, null); } } /** * Sets the preferred size for this icon. The rendering is * scheduled automatically. * * @param dim * Preferred size. */ public synchronized void setPreferredSize(Dimension dim) { if ((dim.width == this.width) && (dim.height == this.height)) return; this.width = dim.width; this.height = dim.height; this.renderImage(this.width, this.height); } /** * Renders the image. * * @param renderWidth * Requested rendering width. * @param renderHeight * Requested rendering height. */ protected synchronized void renderImage(final int renderWidth, final int renderHeight) { String key = renderWidth + ":" + renderHeight; if (this.cachedImages.containsKey(key)) { fireAsyncCompleted(true); return; } SwingWorker worker = new SwingWorker() { @Override protected BufferedImage doInBackground() throws Exception { if (imageInputStream != null) { synchronized (imageInputStream) { if (originalImage == null) { // read original image originalImage = ImageIO.read(imageInputStream); } } } else { GraphicsEnvironment e = GraphicsEnvironment .getLocalGraphicsEnvironment(); GraphicsDevice d = e.getDefaultScreenDevice(); GraphicsConfiguration c = d.getDefaultConfiguration(); originalImage = c.createCompatibleImage(image .getWidth(null), image.getHeight(null), Transparency.TRANSLUCENT); Graphics g = originalImage.getGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); } BufferedImage result = originalImage; float scaleX = (float) originalImage.getWidth() / (float) renderWidth; float scaleY = (float) originalImage.getHeight() / (float) height; float scale = Math.max(scaleX, scaleY); if (scale > 1.0f) { int finalWidth = (int) (originalImage.getWidth() / scale); result = FlamingoUtilities.createThumbnail(originalImage, finalWidth); } return result; } @Override protected void done() { try { BufferedImage bufferedImage = get(); cachedImages.put(renderWidth + ":" + renderHeight, bufferedImage); fireAsyncCompleted(true); } catch (Exception exc) { fireAsyncCompleted(false); } } }; worker.execute(); } /** * Fires the asynchronous load event. * * @param event * Event object. */ protected void fireAsyncCompleted(Boolean event) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == AsynchronousLoadListener.class) { ((AsynchronousLoadListener) listeners[i + 1]).completed(event); } } } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.AsynchronousLoading#isLoading() */ @Override public synchronized boolean isLoading() { BufferedImage image = this.cachedImages.get(this.getIconWidth() + ":" + this.getIconHeight()); return (image == null); } } src/org/pushingpixels/flamingo/api/common/icon/FilteredResizableIcon.java0000644000175000017500000001056311401230444025740 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.util.LinkedHashMap; import java.util.Map; import org.pushingpixels.flamingo.api.common.AsynchronousLoading; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Implementation of {@link ResizableIcon} that allows applying a * {@link BufferedImageOp} on another icon. * * @author Kirill Grouchnikov */ public class FilteredResizableIcon implements ResizableIcon { /** * Image cache to speed up rendering. */ protected Map cachedImages; /** * The main (pre-filtered) icon. */ protected ResizableIcon delegate; /** * Filter operation. */ protected BufferedImageOp operation; /** * Creates a new filtered icon. * * @param delegate * The main (pre-filtered) icon. * @param operation * Filter operation. */ public FilteredResizableIcon(ResizableIcon delegate, BufferedImageOp operation) { super(); this.delegate = delegate; this.operation = operation; this.cachedImages = new LinkedHashMap() { @Override protected boolean removeEldestEntry( java.util.Map.Entry eldest) { return size() > 5; } }; } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return delegate.getIconHeight(); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return delegate.getIconWidth(); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.icon.ResizableIcon#setDimension(java.awt.Dimension * ) */ public void setDimension(Dimension newDimension) { delegate.setDimension(newDimension); } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ @Override public void paintIcon(Component c, Graphics g, int x, int y) { // check cache String key = this.getIconWidth() + ":" + this.getIconHeight(); if (!this.cachedImages.containsKey(key)) { // check if loading if (this.delegate instanceof AsynchronousLoading) { AsynchronousLoading asyncDelegate = (AsynchronousLoading) this.delegate; // if the delegate is still loading - do nothing if (asyncDelegate.isLoading()) return; } BufferedImage offscreen = FlamingoUtilities.getBlankImage(this .getIconWidth(), this.getIconHeight()); Graphics2D g2d = offscreen.createGraphics(); this.delegate.paintIcon(c, g2d, 0, 0); g2d.dispose(); BufferedImage filtered = this.operation.filter(offscreen, null); this.cachedImages.put(key, filtered); } g.drawImage(this.cachedImages.get(key), x, y, null); } } src/org/pushingpixels/flamingo/api/common/icon/IcoWrapperResizableIcon.java0000644000175000017500000000652511401230444026260 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.Dimension; import java.io.IOException; import java.io.InputStream; import java.net.URL; /** * Implementation of {@link ResizableIcon} interface that wraps ICO files. * * @author Kirill Grouchnikov */ public class IcoWrapperResizableIcon extends IcoWrapperIcon implements ResizableIcon { /** * Returns the icon for the specified URL. * * @param location * Icon URL. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static IcoWrapperResizableIcon getIcon(URL location, final Dimension initialDim) { try { return new IcoWrapperResizableIcon(location.openStream(), initialDim); } catch (IOException ioe) { ioe.printStackTrace(); return null; } } /** * Returns the icon for the specified input stream. * * @param inputStream * Icon input stream. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static IcoWrapperResizableIcon getIcon(InputStream inputStream, final Dimension initialDim) { return new IcoWrapperResizableIcon(inputStream, initialDim); } /** * Creates a new ICO-based resizable icon. * * @param inputStream * Input stream with the ICO content. * @param initialDim * Initial dimension of the icon. */ private IcoWrapperResizableIcon(InputStream inputStream, Dimension initialDim) { super(inputStream, initialDim.width, initialDim.height); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.icon.ResizableIcon#setDimension(java.awt.Dimension * ) */ public void setDimension(Dimension dim) { this.setPreferredSize(dim); } } src/org/pushingpixels/flamingo/api/common/icon/ResizableIcon.java0000644000175000017500000000406111401230444024255 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.Dimension; import javax.swing.Icon; /** * Interface for icons that have resizability behaviour. * * @author Kirill Grouchnikov */ public interface ResizableIcon extends Icon { /** * Changes the dimension of this icon. * * @param newDimension * New dimension for this icon. */ public void setDimension(Dimension newDimension); } src/org/pushingpixels/flamingo/api/common/icon/ImageWrapperResizableIcon.java0000644000175000017500000000777411401230444026577 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.Dimension; import java.awt.Image; import java.io.IOException; import java.io.InputStream; import java.net.URL; /** * Implementation of {@link ResizableIcon} interface that wraps image files. * * @author Kirill Grouchnikov */ public class ImageWrapperResizableIcon extends ImageWrapperIcon implements ResizableIcon { /** * Returns the icon for the specified URL. * * @param image * Image. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static ImageWrapperResizableIcon getIcon(Image image, Dimension initialDim) { return new ImageWrapperResizableIcon(image, initialDim); } /** * Returns the icon for the specified URL. * * @param location * Icon URL. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static ImageWrapperResizableIcon getIcon(URL location, Dimension initialDim) { try { return new ImageWrapperResizableIcon(location.openStream(), initialDim); } catch (IOException ioe) { ioe.printStackTrace(); return null; } } /** * Returns the icon for the specified input stream. * * @param inputStream * Icon input stream. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static ImageWrapperResizableIcon getIcon(InputStream inputStream, Dimension initialDim) { return new ImageWrapperResizableIcon(inputStream, initialDim); } /** * Creates a new image-based resizable icon. * * @param image * Image. * @param initialDim * Initial dimension of the icon. */ private ImageWrapperResizableIcon(Image image, Dimension initialDim) { super(image, initialDim.width, initialDim.height); } /** * Creates a new image-based resizable icon. * * @param inputStream * Input stream with the image content. * @param initialDim * Initial dimension of the icon. */ private ImageWrapperResizableIcon(InputStream inputStream, final Dimension initialDim) { super(inputStream, initialDim.width, initialDim.height); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.icon.ResizableIcon#setDimension(java.awt.Dimension * ) */ public void setDimension(Dimension dim) { this.setPreferredSize(dim); } } src/org/pushingpixels/flamingo/api/common/icon/EmptyResizableIcon.java0000644000175000017500000000622111401230444025274 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.*; /** * Implementation of {@link ResizableIcon} that paints nothing. * * @author Kirill Grouchnikov */ public class EmptyResizableIcon implements ResizableIcon { /** * The current icon width. */ protected int width; /** * The current icon height. */ protected int height; /** * Creates a new empty resizable icon of the specified size. * * @param initialDim * Initial dimension of the icon. */ public EmptyResizableIcon(Dimension initialDim) { this.width = initialDim.width; this.height = initialDim.height; } /** * Creates a new empty resizable icon of the specified size. * * @param initialDim * Initial dimension of the icon. */ public EmptyResizableIcon(int initialDim) { this(new Dimension(initialDim, initialDim)); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.icon.ResizableIcon#setDimension(java.awt.Dimension * ) */ public void setDimension(Dimension newDimension) { this.width = newDimension.width; this.height = newDimension.height; } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return this.height; } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return this.width; } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ public void paintIcon(Component c, Graphics g, int x, int y) { } } src/org/pushingpixels/flamingo/api/common/icon/IcoWrapperIcon.java0000644000175000017500000004757211401230444024426 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common.icon; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.net.URL; import java.util.*; import javax.imageio.ImageIO; import javax.swing.Icon; import javax.swing.SwingWorker; import javax.swing.event.EventListenerList; import org.pushingpixels.flamingo.api.common.AsynchronousLoadListener; import org.pushingpixels.flamingo.api.common.AsynchronousLoading; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Helper class to load image planes from .ICO files. * * @author Kirill Grouchnikov */ abstract class IcoWrapperIcon implements Icon, AsynchronousLoading { /** * The input stream of the original image. */ protected InputStream icoInputStream; /** * Image planes of the original ICO image. */ protected Map icoPlaneMap; /** * Contains all precomputed images. */ protected Map cachedImages; /** * The width of the current image. */ protected int width; /** * The height of the current image. */ protected int height; /** * The listeners. */ protected EventListenerList listenerList = new EventListenerList(); /** * Create a new SVG icon. * * @param inputStream * The input stream to read the SVG document from. * @param w * The width of the icon. * @param h * The height of the icon. */ public IcoWrapperIcon(InputStream inputStream, int w, int h) { this.icoInputStream = inputStream; this.width = w; this.height = h; this.listenerList = new EventListenerList(); this.cachedImages = new LinkedHashMap() { @Override protected boolean removeEldestEntry( Map.Entry eldest) { return size() > 5; }; }; this.renderImage(this.width, this.height); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#addAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ public void addAsynchronousLoadListener(AsynchronousLoadListener l) { this.listenerList.add(AsynchronousLoadListener.class, l); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#removeAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ public void removeAsynchronousLoadListener(AsynchronousLoadListener l) { this.listenerList.remove(AsynchronousLoadListener.class, l); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return width; } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return height; } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ public void paintIcon(Component c, Graphics g, int x, int y) { BufferedImage image = this.cachedImages.get(this.getIconWidth() + ":" + this.getIconHeight()); if (image != null) { int dx = (this.width - image.getWidth()) / 2; int dy = (this.height - image.getHeight()) / 2; g.drawImage(image, x + dx, y + dy, null); } } /** * Sets the preferred size for this icon. The rendering is * scheduled automatically. * * @param dim * Preferred size. */ public synchronized void setPreferredSize(Dimension dim) { if ((dim.width == this.width) && (dim.height == this.height)) return; this.width = dim.width; this.height = dim.height; this.renderImage(this.width, this.height); } /** * Renders the image. * * @param renderWidth * Requested rendering width. * @param renderHeight * Requested rendering height. */ protected synchronized void renderImage(final int renderWidth, final int renderHeight) { String key = renderWidth + ":" + renderHeight; if (this.cachedImages.containsKey(key)) { fireAsyncCompleted(true); return; } SwingWorker worker = new SwingWorker() { @Override protected BufferedImage doInBackground() throws Exception { synchronized (icoInputStream) { if (icoPlaneMap == null) { // read original ICO image Ico ico = new Ico(icoInputStream); icoPlaneMap = new TreeMap(); Set widths = new HashSet(); for (int i = 0; i < ico.getNumImages(); i++) { BufferedImage icoPlane = ico.getImage(i); widths.add(icoPlane.getWidth()); } for (int width : widths) { // find the ico plane with the largest color count BufferedImage bestMatch = null; int bestColorCount = -1; for (int i = 0; i < ico.getNumImages(); i++) { BufferedImage icoPlane = ico.getImage(i); if (icoPlane.getWidth() != width) continue; int icoPlaneColorCount = ico.getNumColors(i); if (icoPlaneColorCount == 0) { bestMatch = icoPlane; bestColorCount = 0; } else { if (bestColorCount == 0) continue; if (icoPlaneColorCount > bestColorCount) { bestMatch = icoPlane; bestColorCount = icoPlaneColorCount; } } } icoPlaneMap.put(width, bestMatch); } } } // find the best match int indexOfBestMatch = -1; int bestMatchWidth = -1; for (Map.Entry icoPlaneMapEntry : icoPlaneMap .entrySet()) { BufferedImage icoPlane = icoPlaneMapEntry.getValue(); int icoPlaneWidth = icoPlane.getWidth(); if (icoPlaneWidth > renderWidth) { // check if the ICO plane width is closer // to the required width than the best match so far if (bestMatchWidth < 0) { bestMatchWidth = icoPlaneWidth; } else { if (bestMatchWidth > icoPlaneWidth) { bestMatchWidth = icoPlaneWidth; } } } } // if at this point the best match is not found, it // means that the requested width is bigger than // any of the ICO planes. Take the biggest ICO plane // available if (indexOfBestMatch < 0) { for (Map.Entry icoPlaneMapEntry : icoPlaneMap .entrySet()) { BufferedImage icoPlane = icoPlaneMapEntry.getValue(); int icoPlaneWidth = icoPlane.getWidth(); if (icoPlaneWidth > bestMatchWidth) { bestMatchWidth = icoPlaneWidth; } } } BufferedImage bestMatchPlane = icoPlaneMap.get(bestMatchWidth); BufferedImage result = bestMatchPlane; float scaleX = (float) bestMatchPlane.getWidth() / (float) renderWidth; float scaleY = (float) bestMatchPlane.getHeight() / (float) renderHeight; float scale = Math.max(scaleX, scaleY); if (scale > 1.0f) { int finalWidth = (int) (bestMatchPlane.getWidth() / scale); result = FlamingoUtilities.createThumbnail(bestMatchPlane, finalWidth); } return result; } @Override protected void done() { try { BufferedImage bufferedImage = get(); cachedImages.put(renderWidth + ":" + renderHeight, bufferedImage); fireAsyncCompleted(true); } catch (Exception exc) { fireAsyncCompleted(false); } } }; worker.execute(); } /** * Fires the asynchronous load event. * * @param event * Event object. */ protected void fireAsyncCompleted(Boolean event) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == AsynchronousLoadListener.class) { ((AsynchronousLoadListener) listeners[i + 1]).completed(event); } } } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.AsynchronousLoading#isLoading() */ @Override public synchronized boolean isLoading() { BufferedImage image = this.cachedImages.get(this.getIconWidth() + ":" + this.getIconHeight()); return (image == null); } } /** * The code below is copyrighted by Jeff Friesen and first appeared on InformIT.com at this * location. This code is licensed under BSD license and can be reused as * long as the credit is given to Jeff Friesen, InformIT.com and the original * URL above. * * @author Jeff Friesen */ class Ico { private final static int FDE_OFFSET = 6; // first directory entry offset private final static int DE_LENGTH = 16; // directory entry length private final static int BMIH_LENGTH = 40; // BITMAPINFOHEADER length private byte[] icoimage = new byte[0]; // new byte [0] facilitates read() private int numImages; private BufferedImage[] bi; private int[] colorCount; public Ico(File file) throws BadIcoResException, IOException { this(file.getAbsolutePath()); } public Ico(InputStream is) throws BadIcoResException, IOException { try { read(is); parseICOImage(); } finally { try { is.close(); } catch (IOException ioe) { } } } public Ico(String filename) throws BadIcoResException, IOException { this(new FileInputStream(filename)); } public Ico(URL url) throws BadIcoResException, IOException { this(url.openStream()); } public BufferedImage getImage(int index) { if (index < 0 || index >= numImages) throw new IllegalArgumentException("index out of range"); return bi[index]; } public int getNumColors(int index) { if (index < 0 || index >= numImages) throw new IllegalArgumentException("index out of range"); return colorCount[index]; } public int getNumImages() { return numImages; } private int calcScanlineBytes(int width, int bitCount) { // Calculate minimum number of double-words required to store width // pixels where each pixel occupies bitCount bits. XOR and AND bitmaps // are stored such that each scanline is aligned on a double-word // boundary. return (((width * bitCount) + 31) / 32) * 4; } private void parseICOImage() throws BadIcoResException, IOException { // Check resource type field. if (icoimage[2] != 1 || icoimage[3] != 0) throw new BadIcoResException("Not an ICO resource"); numImages = ubyte(icoimage[5]); numImages <<= 8; numImages |= icoimage[4]; bi = new BufferedImage[numImages]; colorCount = new int[numImages]; for (int i = 0; i < numImages; i++) { int width = ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH]); int height = ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 1]); colorCount[i] = ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 2]); int bytesInRes = ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 11]); bytesInRes <<= 8; bytesInRes |= ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 10]); bytesInRes <<= 8; bytesInRes |= ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 9]); bytesInRes <<= 8; bytesInRes |= ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 8]); int imageOffset = ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 15]); imageOffset <<= 8; imageOffset |= ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 14]); imageOffset <<= 8; imageOffset |= ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 13]); imageOffset <<= 8; imageOffset |= ubyte(icoimage[FDE_OFFSET + i * DE_LENGTH + 12]); if (icoimage[imageOffset] == 40 && icoimage[imageOffset + 1] == 0 && icoimage[imageOffset + 2] == 0 && icoimage[imageOffset + 3] == 0) { // BITMAPINFOHEADER detected int _width = ubyte(icoimage[imageOffset + 7]); _width <<= 8; _width |= ubyte(icoimage[imageOffset + 6]); _width <<= 8; _width |= ubyte(icoimage[imageOffset + 5]); _width <<= 8; _width |= ubyte(icoimage[imageOffset + 4]); // If width is 0 (for 256 pixels or higher), _width contains // actual width. if (width == 0) width = _width; int _height = ubyte(icoimage[imageOffset + 11]); _height <<= 8; _height |= ubyte(icoimage[imageOffset + 10]); _height <<= 8; _height |= ubyte(icoimage[imageOffset + 9]); _height <<= 8; _height |= ubyte(icoimage[imageOffset + 8]); // If height is 0 (for 256 pixels or higher), _height contains // actual height times 2. if (height == 0) height = _height >> 1; // Divide by 2. int planes = ubyte(icoimage[imageOffset + 13]); planes <<= 8; planes |= ubyte(icoimage[imageOffset + 12]); int bitCount = ubyte(icoimage[imageOffset + 15]); bitCount <<= 8; bitCount |= ubyte(icoimage[imageOffset + 14]); // If colorCount [i] is 0, the number of colors is determined // from the planes and bitCount values. For example, the number // of colors is 256 when planes is 1 and bitCount is 8. Leave // colorCount [i] set to 0 when planes is 1 and bitCount is 32. if (colorCount[i] == 0) { if (planes == 1) { if (bitCount == 1) colorCount[i] = 2; else if (bitCount == 4) colorCount[i] = 16; else if (bitCount == 8) colorCount[i] = 256; else if (bitCount != 32) colorCount[i] = (int) Math.pow(2, bitCount); } else colorCount[i] = (int) Math.pow(2, bitCount * planes); } bi[i] = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); // Parse image to image buffer. int colorTableOffset = imageOffset + BMIH_LENGTH; if (colorCount[i] == 2) { int xorImageOffset = colorTableOffset + 2 * 4; int scanlineBytes = calcScanlineBytes(width, 1); int andImageOffset = xorImageOffset + scanlineBytes * height; int[] masks = { 128, 64, 32, 16, 8, 4, 2, 1 }; for (int row = 0; row < height; row++) for (int col = 0; col < width; col++) { int index; if ((ubyte(icoimage[xorImageOffset + row * scanlineBytes + col / 8]) & masks[col % 8]) != 0) index = 1; else index = 0; int rgb = 0; rgb |= (ubyte(icoimage[colorTableOffset + index * 4 + 2])); rgb <<= 8; rgb |= (ubyte(icoimage[colorTableOffset + index * 4 + 1])); rgb <<= 8; rgb |= (ubyte(icoimage[colorTableOffset + index * 4])); if ((ubyte(icoimage[andImageOffset + row * scanlineBytes + col / 8]) & masks[col % 8]) != 0) bi[i].setRGB(col, height - 1 - row, rgb); else bi[i].setRGB(col, height - 1 - row, 0xff000000 | rgb); } } else if (colorCount[i] == 16) { int xorImageOffset = colorTableOffset + 16 * 4; int scanlineBytes = calcScanlineBytes(width, 4); int andImageOffset = xorImageOffset + scanlineBytes * height; int[] masks = { 128, 64, 32, 16, 8, 4, 2, 1 }; for (int row = 0; row < height; row++) for (int col = 0; col < width; col++) { int index; if ((col & 1) == 0) // even { index = ubyte(icoimage[xorImageOffset + row * scanlineBytes + col / 2]); index >>= 4; } else { index = ubyte(icoimage[xorImageOffset + row * scanlineBytes + col / 2]) & 15; } int rgb = 0; rgb |= (ubyte(icoimage[colorTableOffset + index * 4 + 2])); rgb <<= 8; rgb |= (ubyte(icoimage[colorTableOffset + index * 4 + 1])); rgb <<= 8; rgb |= (ubyte(icoimage[colorTableOffset + index * 4])); if ((ubyte(icoimage[andImageOffset + row * calcScanlineBytes(width, 1) + col / 8]) & masks[col % 8]) != 0) bi[i].setRGB(col, height - 1 - row, rgb); else bi[i].setRGB(col, height - 1 - row, 0xff000000 | rgb); } } else if (colorCount[i] == 256) { int xorImageOffset = colorTableOffset + 256 * 4; int scanlineBytes = calcScanlineBytes(width, 8); int andImageOffset = xorImageOffset + scanlineBytes * height; int[] masks = { 128, 64, 32, 16, 8, 4, 2, 1 }; for (int row = 0; row < height; row++) for (int col = 0; col < width; col++) { int index; index = ubyte(icoimage[xorImageOffset + row * scanlineBytes + col]); int rgb = 0; rgb |= (ubyte(icoimage[colorTableOffset + index * 4 + 2])); rgb <<= 8; rgb |= (ubyte(icoimage[colorTableOffset + index * 4 + 1])); rgb <<= 8; rgb |= (ubyte(icoimage[colorTableOffset + index * 4])); if ((ubyte(icoimage[andImageOffset + row * calcScanlineBytes(width, 1) + col / 8]) & masks[col % 8]) != 0) bi[i].setRGB(col, height - 1 - row, rgb); else bi[i].setRGB(col, height - 1 - row, 0xff000000 | rgb); } } else if (colorCount[i] == 0) { int scanlineBytes = calcScanlineBytes(width, 32); for (int row = 0; row < height; row++) for (int col = 0; col < width; col++) { int rgb = ubyte(icoimage[colorTableOffset + row * scanlineBytes + col * 4 + 3]); rgb <<= 8; rgb |= ubyte(icoimage[colorTableOffset + row * scanlineBytes + col * 4 + 2]); rgb <<= 8; rgb |= ubyte(icoimage[colorTableOffset + row * scanlineBytes + col * 4 + 1]); rgb <<= 8; rgb |= ubyte(icoimage[colorTableOffset + row * scanlineBytes + col * 4]); bi[i].setRGB(col, height - 1 - row, rgb); } } } else if (ubyte(icoimage[imageOffset]) == 0x89 && icoimage[imageOffset + 1] == 0x50 && icoimage[imageOffset + 2] == 0x4e && icoimage[imageOffset + 3] == 0x47 && icoimage[imageOffset + 4] == 0x0d && icoimage[imageOffset + 5] == 0x0a && icoimage[imageOffset + 6] == 0x1a && icoimage[imageOffset + 7] == 0x0a) { // PNG detected ByteArrayInputStream bais; bais = new ByteArrayInputStream(icoimage, imageOffset, bytesInRes); bi[i] = ImageIO.read(bais); } else throw new BadIcoResException("BITMAPINFOHEADER or PNG " + "expected"); } icoimage = null; // This array can now be garbage collected. } private void read(InputStream is) throws IOException { int bytesToRead; while ((bytesToRead = is.available()) != 0) { byte[] icoimage2 = new byte[icoimage.length + bytesToRead]; System.arraycopy(icoimage, 0, icoimage2, 0, icoimage.length); is.read(icoimage2, icoimage.length, bytesToRead); icoimage = icoimage2; } } private int ubyte(byte b) { return (b < 0) ? 256 + b : b; // Convert byte to unsigned byte. } } class BadIcoResException extends Exception { public BadIcoResException(String message) { super(message); } } src/org/pushingpixels/flamingo/api/common/JCommandButtonPanel.java0000644000175000017500000006022111401230444024440 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.Dimension; import java.awt.Rectangle; import java.util.*; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.pushingpixels.flamingo.internal.ui.common.BasicCommandButtonPanelUI; import org.pushingpixels.flamingo.internal.ui.common.CommandButtonPanelUI; /** * Panel that hosts command buttons. Provides support for button groups, single * selection mode (for toggle command buttons), same icon state / dimension and * column-fill / row-fill layout. * *

* Under the default {@link LayoutKind#ROW_FILL}, the buttons are laid out in * rows, never exceeding the available horizontal space. A vertical scroll bar * will kick in once there is not enough vertical space to show all the buttons. * The schematic below shows a row-fill command button panel: *

* *
 * +-----------------------------+-+ 
 * |                             | |
 * | +----+ +----+ +----+ +----+ | |
 * | | 01 | | 02 | | 03 | | 04 | | |
 * | +----+ +----+ +----+ +----+ | |
 * |                             | |
 * | +----+ +----+ +----+ +----+ | |
 * | | 05 | | 06 | | 07 | | 07 | | |
 * | +----+ +----+ +----+ +----+ | |
 * |                             | |
 * | +----+ +----+ +----+ +----+ | |
 * | | 09 | | 10 | | 11 | | 12 | | |
 * | +----+ +----+ +----+ +----+ | |
 * |                             | |
 * | +----+ +----+ +----+ +----+ | |
 * | | 13 | | 14 | | 15 | | 16 | | |
 * +-----------------------------+-+
 * 
* *

* Each row hosts four buttons, and the vertical scroll bar allows scrolling the * content down. *

* *

* Under the {@link LayoutKind#COLUMN_FILL}, the buttons are laid out in * columns, never exceeding the available vertical space. A horizontal scroll * bar will kick in once there is not enough horizontal space to show all the * buttons. The schematic below shows a column-fill command button panel: *

* *
 * +---------------------------------+ 
 * |                                 |
 * | +----+ +----+ +----+ +----+ +---|
 * | | 01 | | 04 | | 07 | | 10 | | 13|
 * | +----+ +----+ +----+ +----+ +---|
 * |                                 |
 * | +----+ +----+ +----+ +----+ +---|
 * | | 02 | | 05 | | 08 | | 11 | | 14|
 * | +----+ +----+ +----+ +----+ +---|
 * |                                 |
 * | +----+ +----+ +----+ +----+ +---|
 * | | 03 | | 06 | | 09 | | 12 | | 15|
 * | +----+ +----+ +----+ +----+ +---|
 * |                                 |
 * +---------------------------------+
 * +---------------------------------+
 * 
* *

* Each column hosts three buttons, and the horizontal scroll bar allows * scrolling the content down. *

* * @author Kirill Grouchnikov */ public class JCommandButtonPanel extends JPanel implements Scrollable { /** * @see #getUIClassID */ public static final String uiClassID = "CommandButtonPanelUI"; /** * List of titles for all button groups. * * @see #getGroupCount() * @see #getGroupTitleAt(int) */ protected List groupTitles; /** * List of all button groups. * * @see #getGroupCount() * @see #getGroupButtons(int) */ protected List> buttons; /** * Maximum number of columns for this panel. Relevant only when the layout * kind is {@link LayoutKind#ROW_FILL}. * * @see #getMaxButtonColumns() * @see #setMaxButtonColumns(int) */ protected int maxButtonColumns; /** * Maximum number of rows for this panel. Relevant only when the layout kind * is {@link LayoutKind#COLUMN_FILL}. * * @see #getMaxButtonRows() * @see #setMaxButtonRows(int) */ protected int maxButtonRows; /** * Indicates the selection mode for the {@link JCommandToggleButton} in this * panel. * * @see #setSingleSelectionMode(boolean) */ protected boolean isSingleSelectionMode; /** * If true, the panel will show group labels. * * @see #setToShowGroupLabels(boolean) * @see #isToShowGroupLabels() */ protected boolean toShowGroupLabels; /** * The button group for the single selection mode. */ protected CommandToggleButtonGroup buttonGroup; /** * Current icon dimension. */ protected int currDimension; /** * Current icon state. */ protected CommandButtonDisplayState currState; /** * Layout kind of this button panel. * * @see #getLayoutKind() * @see #setLayoutKind(LayoutKind) */ protected LayoutKind layoutKind; /** * Enumerates the available layout kinds. * * @author Kirill Grouchnikov */ public enum LayoutKind { /** * The buttons are layed out in rows respecting the available width. */ ROW_FILL, /** * The buttons are layed out in columns respecting the available height. */ COLUMN_FILL } /** * Creates a new panel. */ protected JCommandButtonPanel() { this.buttons = new ArrayList>(); this.groupTitles = new ArrayList(); this.maxButtonColumns = -1; this.maxButtonRows = -1; this.isSingleSelectionMode = false; this.toShowGroupLabels = true; this.setLayoutKind(LayoutKind.ROW_FILL); } /** * Creates a new panel. * * @param startingDimension * Initial dimension for buttons. */ public JCommandButtonPanel(int startingDimension) { this(); this.currDimension = startingDimension; this.currState = CommandButtonDisplayState.FIT_TO_ICON; this.updateUI(); } /** * Creates a new panel. * * @param startingState * Initial state for buttons. */ public JCommandButtonPanel(CommandButtonDisplayState startingState) { this(); this.currDimension = -1; this.currState = startingState; this.updateUI(); } /** * Adds a new button group at the specified index. * * @param buttonGroupName * Button group name. * @param groupIndex * Button group index. * @see #addButtonGroup(String) * @see #removeButtonGroup(String) * @see #removeAllGroups() */ public void addButtonGroup(String buttonGroupName, int groupIndex) { this.groupTitles.add(groupIndex, buttonGroupName); List list = new ArrayList(); this.buttons.add(groupIndex, list); this.fireStateChanged(); } /** * Adds a new button group after all the existing button groups. * * @param buttonGroupName * Button group name. * @see #addButtonGroup(String, int) * @see #removeButtonGroup(String) * @see #removeAllGroups() */ public void addButtonGroup(String buttonGroupName) { this.addButtonGroup(buttonGroupName, this.groupTitles.size()); } /** * Removes the specified button group. * * @param buttonGroupName * Name of the button group to remove. * @see #addButtonGroup(String) * @see #addButtonGroup(String, int) * @see #removeAllGroups() */ public void removeButtonGroup(String buttonGroupName) { int groupIndex = this.groupTitles.indexOf(buttonGroupName); if (groupIndex < 0) return; this.groupTitles.remove(groupIndex); List list = this.buttons.get(groupIndex); if (list != null) { for (AbstractCommandButton button : list) { this.remove(button); if (this.isSingleSelectionMode && (button instanceof JCommandToggleButton)) { this.buttonGroup.remove((JCommandToggleButton) button); } } } this.buttons.remove(groupIndex); this.fireStateChanged(); } /** * Adds a new button to the specified button group. * * @param commandButton * Button to add. * @return Returns the index of the button on the specified group, or -1 if * no such group exists. * @see #addButtonToGroup(String, AbstractCommandButton) * @see #addButtonToGroup(String, int, AbstractCommandButton) * @see #removeButtonFromGroup(String, int) */ public int addButtonToLastGroup(AbstractCommandButton commandButton) { if (this.groupTitles.size() == 0) return -1; int groupIndex = this.groupTitles.size() - 1; commandButton.setDisplayState(this.currState); return this.addButtonToGroup(this.groupTitles.get(groupIndex), this.buttons.get(groupIndex).size(), commandButton); } /** * Adds a new button to the specified button group. * * @param buttonGroupName * Name of the button group. * @param commandButton * Button to add. * @return Returns the index of the button on the specified group, or -1 if * no such group exists. * @see #addButtonToGroup(String, int, AbstractCommandButton) * @see #addButtonToLastGroup(AbstractCommandButton) * @see #removeButtonFromGroup(String, int) */ public int addButtonToGroup(String buttonGroupName, AbstractCommandButton commandButton) { int groupIndex = this.groupTitles.indexOf(buttonGroupName); if (groupIndex < 0) return -1; commandButton.setDisplayState(this.currState); return this.addButtonToGroup(buttonGroupName, this.buttons.get( groupIndex).size(), commandButton); } /** * Adds a new button to the specified button group. * * @param buttonGroupName * Name of the button group. * @param indexInGroup * Index of the button in group. * @param commandButton * Button to add. * @return Returns the index of the button on the specified group, or -1 if * no such group exists. * @see #addButtonToGroup(String, int, AbstractCommandButton) * @see #addButtonToLastGroup(AbstractCommandButton) * @see #removeButtonFromGroup(String, int) */ public int addButtonToGroup(String buttonGroupName, int indexInGroup, AbstractCommandButton commandButton) { int groupIndex = this.groupTitles.indexOf(buttonGroupName); if (groupIndex < 0) return -1; // commandButton.setState(ElementState.ORIG, true); this.add(commandButton); this.buttons.get(groupIndex).add(indexInGroup, commandButton); if (this.isSingleSelectionMode && (commandButton instanceof JCommandToggleButton)) { this.buttonGroup.add((JCommandToggleButton) commandButton); } this.fireStateChanged(); return indexInGroup; } /** * Removes the button at the specified index from the specified button * group. * * @param buttonGroupName * Name of the button group. * @param indexInGroup * Index of the button to remove. * @see #addButtonToGroup(String, AbstractCommandButton) * @see #addButtonToGroup(String, int, AbstractCommandButton) * @see #addButtonToLastGroup(AbstractCommandButton) */ public void removeButtonFromGroup(String buttonGroupName, int indexInGroup) { int groupIndex = this.groupTitles.indexOf(buttonGroupName); if (groupIndex < 0) return; AbstractCommandButton removed = this.buttons.get(groupIndex).remove( indexInGroup); this.remove(removed); if (this.isSingleSelectionMode && (removed instanceof JCommandToggleButton)) { this.buttonGroup.remove((JCommandToggleButton) removed); } this.fireStateChanged(); } /** * Removes all the button groups and buttons from this panel. * * @see #addButtonGroup(String, int) * @see #addButtonGroup(String) * @see #removeButtonGroup(String) * @see #removeButtonFromGroup(String, int) */ public void removeAllGroups() { for (List ljcb : this.buttons) { for (AbstractCommandButton jcb : ljcb) { if (this.isSingleSelectionMode && (jcb instanceof JCommandToggleButton)) { this.buttonGroup.remove((JCommandToggleButton) jcb); } this.remove(jcb); } } this.buttons.clear(); this.groupTitles.clear(); this.fireStateChanged(); } /** * Returns the number of button groups in this panel. * * @return Number of button groups in this panel. */ public int getGroupCount() { if (this.groupTitles == null) return 0; return this.groupTitles.size(); } /** * Returns the number of buttons in this panel. * * @return Number of buttons in this panel. */ public int getButtonCount() { int result = 0; for (List ljcb : this.buttons) { result += ljcb.size(); } return result; } /** * Returns the title of the button group at the specified index. * * @param index * Button group index. * @return Title of the button group at the specified index. */ public String getGroupTitleAt(int index) { return this.groupTitles.get(index); } /* * (non-Javadoc) * * @see javax.swing.JPanel#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((CommandButtonPanelUI) UIManager.getUI(this)); } else { setUI(BasicCommandButtonPanelUI.createUI(this)); } } /* * (non-Javadoc) * * @see javax.swing.JPanel#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /** * Sets the maximum button columns for this panel. When this panel is shown * and the layout kind is {@link LayoutKind#ROW_FILL}, it will have no more * than this number of buttons in each row. Fires a * maxButtonColumns property change event. * * @param maxButtonColumns * Maximum button columns for this panel. * @see #getMaxButtonColumns() * @see #setMaxButtonRows(int) */ public void setMaxButtonColumns(int maxButtonColumns) { if (maxButtonColumns != this.maxButtonColumns) { int oldValue = this.maxButtonColumns; this.maxButtonColumns = maxButtonColumns; this.firePropertyChange("maxButtonColumns", oldValue, this.maxButtonColumns); } } /** * Returns the maximum button columns for this panel. The return value is * relevant only when the layout kind is {@link LayoutKind#ROW_FILL}. * * @return Maximum button columns for this panel. * @see #setMaxButtonColumns(int) * @see #getMaxButtonRows() */ public int getMaxButtonColumns() { return this.maxButtonColumns; } /** * Sets the maximum button rows for this panel. When this panel is shown and * the layout kind is {@link LayoutKind#COLUMN_FILL}, it will have no more * than this number of buttons in each column. Fires a * maxButtonRows property change event. * * @param maxButtonRows * Maximum button rows for this panel. * @see #getMaxButtonRows() * @see #setMaxButtonColumns(int) */ public void setMaxButtonRows(int maxButtonRows) { if (maxButtonRows != this.maxButtonRows) { int oldValue = this.maxButtonRows; this.maxButtonRows = maxButtonRows; this.firePropertyChange("maxButtonRows", oldValue, this.maxButtonRows); } } /** * Returns the maximum button rows for this panel. The return value is * relevant only when the layout kind is {@link LayoutKind#COLUMN_FILL}. * * @return Maximum button rows for this panel. * @see #setMaxButtonRows(int) * @see #getMaxButtonColumns() */ public int getMaxButtonRows() { return this.maxButtonRows; } /** * Returns the list of all buttons in the specified button group. * * @param groupIndex * Group index. * @return Unmodifiable view on the list of all buttons in the specified * button group. * @see #getGroupCount() */ public List getGroupButtons(int groupIndex) { return Collections.unmodifiableList(this.buttons.get(groupIndex)); } /** * Sets the selection mode for this panel. If true is passed as * the parameter, all {@link JCommandToggleButton} in this panel are set to * belong to the same button group. * * @param isSingleSelectionMode * If true,all {@link JCommandToggleButton} in this * panel are set to belong to the same button group. * @see #getSelectedButton() */ public void setSingleSelectionMode(boolean isSingleSelectionMode) { if (this.isSingleSelectionMode == isSingleSelectionMode) return; this.isSingleSelectionMode = isSingleSelectionMode; if (this.isSingleSelectionMode) { this.buttonGroup = new CommandToggleButtonGroup(); for (List ljrb : this.buttons) { for (AbstractCommandButton jrb : ljrb) { if (jrb instanceof JCommandToggleButton) { this.buttonGroup.add((JCommandToggleButton) jrb); } } } } else { for (List ljrb : this.buttons) { for (AbstractCommandButton jrb : ljrb) { if (jrb instanceof JCommandToggleButton) { this.buttonGroup.remove((JCommandToggleButton) jrb); } } } this.buttonGroup = null; } } /** * Sets indication whether button group labels should be shown. Fires a * toShowGroupLabels property change event. * * @param toShowGroupLabels * If true, this panel will show the labels of the * button groups. * @see #isToShowGroupLabels() */ public void setToShowGroupLabels(boolean toShowGroupLabels) { if ((layoutKind == LayoutKind.COLUMN_FILL) && toShowGroupLabels) { throw new IllegalArgumentException( "Column fill layout is not supported when group labels are shown"); } if (this.toShowGroupLabels != toShowGroupLabels) { boolean oldValue = this.toShowGroupLabels; this.toShowGroupLabels = toShowGroupLabels; this.firePropertyChange("toShowGroupLabels", oldValue, this.toShowGroupLabels); } } /** * Returns indication whether button group labels should be shown. * * @return If true, this panel shows the labels of the button * groups, and false otherwise. * @see #setToShowGroupLabels(boolean) */ public boolean isToShowGroupLabels() { return this.toShowGroupLabels; } /** * Sets the new dimension for the icons in this panel. The state for all the * icons is set to {@link CommandButtonDisplayState#FIT_TO_ICON}. * * @param dimension * New dimension for the icons in this panel. * @see #setIconState(CommandButtonDisplayState) */ public void setIconDimension(int dimension) { this.currDimension = dimension; this.currState = CommandButtonDisplayState.FIT_TO_ICON; for (List buttonList : this.buttons) { for (AbstractCommandButton button : buttonList) { button.updateCustomDimension(dimension); } } this.revalidate(); this.doLayout(); this.repaint(); } /** * Sets the new state for the icons in this panel. The dimension for all the * icons is set to -1; this method should only be called with a state that * has an associated default size (like * {@link CommandButtonDisplayState#BIG}, * {@link CommandButtonDisplayState#TILE}, * {@link CommandButtonDisplayState#MEDIUM} and * {@link CommandButtonDisplayState#SMALL}). * * @param state * New state for the icons in this panel. * @see #setIconDimension(int) */ public void setIconState(CommandButtonDisplayState state) { this.currDimension = -1; this.currState = state; for (List ljrb : this.buttons) { for (AbstractCommandButton jrb : ljrb) { jrb.setDisplayState(state); jrb.revalidate(); jrb.doLayout(); } } this.revalidate(); this.doLayout(); this.repaint(); } /** * Returns the selected button of this panel. Only relevant for single * selection mode (set by {@link #setSingleSelectionMode(boolean)}), * returning null otherwise. * * @return The selected button of this panel. * @see #setSingleSelectionMode(boolean) */ public JCommandToggleButton getSelectedButton() { if (this.isSingleSelectionMode) { for (List ljrb : this.buttons) { for (AbstractCommandButton jrb : ljrb) { if (jrb instanceof JCommandToggleButton) { JCommandToggleButton jctb = (JCommandToggleButton) jrb; if (jctb.getActionModel().isSelected()) return jctb; } } } } return null; } /** * Returns the layout kind of this panel. * * @return Layout kind of this panel. * @see #setLayoutKind(LayoutKind) */ public LayoutKind getLayoutKind() { return layoutKind; } /** * Sets the new layout kind for this panel. Fires a layoutKind * property change event. * * @param layoutKind * New layout kind for this panel. * @see #getLayoutKind() */ public void setLayoutKind(LayoutKind layoutKind) { if (layoutKind == null) throw new IllegalArgumentException("Layout kind cannot be null"); if ((layoutKind == LayoutKind.COLUMN_FILL) && this.isToShowGroupLabels()) { throw new IllegalArgumentException( "Column fill layout is not supported when group labels are shown"); } if (layoutKind != this.layoutKind) { LayoutKind old = this.layoutKind; this.layoutKind = layoutKind; this.firePropertyChange("layoutKind", old, this.layoutKind); } } /** * Adds the specified change listener to this button panel. * * @param l * Change listener to add. * @see #removeChangeListener(ChangeListener) */ public void addChangeListener(ChangeListener l) { this.listenerList.add(ChangeListener.class, l); } /** * Removes the specified change listener from this button panel. * * @param l * Change listener to remove. * @see #addChangeListener(ChangeListener) */ public void removeChangeListener(ChangeListener l) { this.listenerList.remove(ChangeListener.class, l); } /** * Notifies all registered listener that the state of this command button * panel has changed. */ protected void fireStateChanged() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event ChangeEvent event = new ChangeEvent(this); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { ((ChangeListener) listeners[i + 1]).stateChanged(event); } } } /* * (non-Javadoc) * * @see javax.swing.Scrollable#getPreferredScrollableViewportSize() */ public Dimension getPreferredScrollableViewportSize() { return this.getPreferredSize(); } /* * (non-Javadoc) * * @see * javax.swing.Scrollable#getScrollableBlockIncrement(java.awt.Rectangle, * int, int) */ public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return 30; } /* * (non-Javadoc) * * @see javax.swing.Scrollable#getScrollableTracksViewportHeight() */ public boolean getScrollableTracksViewportHeight() { return (this.layoutKind == LayoutKind.COLUMN_FILL); } /* * (non-Javadoc) * * @see javax.swing.Scrollable#getScrollableTracksViewportWidth() */ public boolean getScrollableTracksViewportWidth() { return (this.layoutKind == LayoutKind.ROW_FILL); } /* * (non-Javadoc) * * @see * javax.swing.Scrollable#getScrollableUnitIncrement(java.awt.Rectangle, * int, int) */ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 10; } } src/org/pushingpixels/flamingo/api/common/HorizontalAlignment.java0000644000175000017500000000463111401230444024567 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; /** * Enumerates the available values for horizontal alignment. * * @author Kirill Grouchnikov */ public enum HorizontalAlignment { /** * If the preferred width is less than the available width, the relevant * component is placed at the leading position in its parent (left for LTR * and right for RTL). */ LEADING, /** * If the preferred width is less than the available width, the relevant * component is horizontally centerd in its parent. */ CENTER, /** * If the preferred width is less than the available width, the relevant * component is placed at the trailing position in its parent (right for LTR * and left for RTL). */ TRAILING, /** * The component is placed to fill all available width from its parent. */ FILL }src/org/pushingpixels/flamingo/api/common/PopupActionListener.java0000644000175000017500000000365111401230444024547 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.awt.event.ActionListener; /** * Popup action listener. Is used to associate application logic with the popup * area of {@link JCommandButton} component. * * @author Kirill Grouchnikov */ public interface PopupActionListener extends ActionListener { } src/org/pushingpixels/flamingo/api/common/ProgressEvent.java0000644000175000017500000000621611401230444023406 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.util.EventObject; /** * This event is used to notify interested parties that progress has been made * in the event source. * * @author Kirill Grouchnikov * @see ProgressListener */ public class ProgressEvent extends EventObject { /** * Minimum value of the available progress range. */ private int minimum; /** * Maximum value of the available progress range. */ private int maximum; /** * Current value of the progress. */ private int progress; /** * Creates a new progress event. * * @param source * Event source. * @param min * Minimum value of the available progress range. * @param max * Maximum value of the available progress range. * @param progress * Current value of the progress. */ public ProgressEvent(Object source, int min, int max, int progress) { super(source); this.maximum = max; this.minimum = min; this.progress = progress; } /** * Returns the maximum value of the available progress range. * * @return The maximum value of the available progress range. */ public int getMaximum() { return this.maximum; } /** * Returns the minimum value of the available progress range. * * @return The minimum value of the available progress range. */ public int getMinimum() { return this.minimum; } /** * Returns the current value of the progress. * * @return The current value of the progress. */ public int getProgress() { return this.progress; } } src/org/pushingpixels/flamingo/api/common/CommandToggleButtonGroup.java0000644000175000017500000002066511401230444025535 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.Serializable; import java.util.*; import javax.swing.ButtonGroup; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * Group of command toggle buttons. Unlike the {@link ButtonGroup}, this class * operates on buttons and not on button models. * * @author Kirill Grouchnikov */ public class CommandToggleButtonGroup implements Serializable { /** * Contains all group buttons. */ protected Vector buttons; /** * Map of registered model change listeners. */ protected Map modelChangeListeners; /** * Property change support to track the registered property change * listeners. */ private PropertyChangeSupport changeSupport; /** * Name of the property change event fired when the group selection is * changed. */ public static final String SELECTED_PROPERTY = "selected"; /** * The currently selected button. Can be null. */ protected JCommandToggleButton selection; /** * If false, the selection cannot be cleared. By default the * button group allows clearing the selection in {@link #clearSelection()} * or {@link #setSelected(JCommandToggleButton, boolean)} (passing the * currently selected button and false). */ protected boolean allowsClearingSelection; /** * Creates a new button group. */ public CommandToggleButtonGroup() { this.buttons = new Vector(); this.modelChangeListeners = new HashMap(); this.allowsClearingSelection = true; } /** * Sets the new value for clearing selection. If true is * passed, the selection can be cleared in {@link #clearSelection()} or * {@link #setSelected(JCommandToggleButton, boolean)} (passing the * currently selected button and false). * * @param allowsClearingSelection * The new value for clearing selection. */ public void setAllowsClearingSelection(boolean allowsClearingSelection) { this.allowsClearingSelection = allowsClearingSelection; } /** * Returns the current value for clearing selection. true is * returned when selection can be cleared in {@link #clearSelection()} or * {@link #setSelected(JCommandToggleButton, boolean)} (passing the * currently selected button and false). * * @return The current value for clearing selection. */ public boolean isAllowsClearingSelection() { return allowsClearingSelection; } /** * Adds the specified button to the group. If the button is selected, and * the group has a selected button, the newly added button is marked as * unselected. * * @param b * The button to be added. */ public void add(final JCommandToggleButton b) { if (b == null) { return; } buttons.addElement(b); boolean wasSelectionNull = (this.selection == null); if (b.getActionModel().isSelected()) { if (wasSelectionNull) { selection = b; } else { b.getActionModel().setSelected(false); } } ChangeListener cl = new ChangeListener() { boolean wasSelected = b.getActionModel().isSelected(); @Override public void stateChanged(ChangeEvent e) { boolean isSelected = b.getActionModel().isSelected(); if (wasSelected != isSelected) setSelected(b, isSelected); wasSelected = isSelected; } }; b.getActionModel().addChangeListener(cl); this.modelChangeListeners.put(b, cl); if (wasSelectionNull) { this.firePropertyChange(SELECTED_PROPERTY, null, b); } } /** * Removes the specified button from the group. * * @param b * The button to be removed */ public void remove(JCommandToggleButton b) { if (b == null) { return; } buttons.removeElement(b); boolean wasSelected = (b == selection); if (wasSelected) { selection = null; } b.getActionModel().removeChangeListener( this.modelChangeListeners.get(b)); this.modelChangeListeners.remove(b); if (wasSelected) { this.firePropertyChange(SELECTED_PROPERTY, b, null); } } /** * Changes the selected status of the specified button. * * @param button * Button. * @param isSelected * Selection indication. */ public void setSelected(JCommandToggleButton button, boolean isSelected) { if (isSelected && button != null && button != selection) { JCommandToggleButton oldSelection = selection; selection = button; if (oldSelection != null) { oldSelection.getActionModel().setSelected(false); } button.getActionModel().setSelected(true); this.firePropertyChange(SELECTED_PROPERTY, oldSelection, button); } if (!isSelected && (button != null) && (button == selection)) { if (this.allowsClearingSelection) { selection = null; button.getActionModel().setSelected(false); this.firePropertyChange(SELECTED_PROPERTY, button, null); } else { // set the button back to selected button.getActionModel().setSelected(true); } } } /** * Returns the selected button of this group. * * @return The selected button of this group. The result can be * null. */ public JCommandToggleButton getSelected() { return this.selection; } /** * Clears the selection of this button group. */ public void clearSelection() { if (this.allowsClearingSelection && (this.selection != null)) { this.selection.getActionModel().setSelected(false); } } /** * Adds the specified property change listener on this button group. * * @param listener * Listener to add. */ public synchronized void addPropertyChangeListener( PropertyChangeListener listener) { if (listener == null) { return; } if (changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } /** * Removes the specified property change listener from this button group. * * @param listener * Listener to remove. */ public synchronized void removePropertyChangeListener( PropertyChangeListener listener) { if (listener == null || changeSupport == null) { return; } changeSupport.removePropertyChangeListener(listener); } /** * Fires a property change event on all registered listeners. * * @param propertyName * Name of the changed property. * @param oldValue * Old property value. * @param newValue * New property value. */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { PropertyChangeSupport changeSupport = this.changeSupport; if (changeSupport == null || oldValue == newValue) { return; } changeSupport.firePropertyChange(propertyName, oldValue, newValue); } } src/org/pushingpixels/flamingo/api/common/KeyValuePair.java0000644000175000017500000000623211401230444023137 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.common; import java.util.*; /** * Generic key-value pair with optional property map. * * @author Kirill Grouchnikov * @param * Key class. * @param * Value class. */ public class KeyValuePair { /** * Pair key. */ protected S key; /** * Pair value. */ protected T value; /** * Property map. */ protected Map propMap; /** * Creates a new pair. * * @param key * Pair key. * @param value * Pair value. */ public KeyValuePair(S key, T value) { this.key = key; this.value = value; this.propMap = new HashMap(); } /** * Returns the pair value. * * @return Pair value. */ public T getValue() { return value; } /** * Returns the pair key. * * @return Pair key. */ public S getKey() { return key; } /** * Returns the property attached to the specified key. * * @param propKey * Property key. * @return Attached property. */ public Object get(String propKey) { return this.propMap.get(propKey); } /** * Sets the property specified by the key and value. * * @param propKey * Property key. * @param propValue * Property value. */ public void set(String propKey, Object propValue) { this.propMap.put(propKey, propValue); } /** * Returns all attached properties. * * @return All attached properties. */ public Map getProps() { return Collections.unmodifiableMap(this.propMap); } }src/org/pushingpixels/flamingo/api/ribbon/0000755000175000017500000000000011415653300017720 5ustar tonytonysrc/org/pushingpixels/flamingo/api/ribbon/JRibbonComponent.java0000644000175000017500000002333211415653300023776 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.MouseEvent; import javax.swing.JComponent; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.ui.ribbon.BasicRibbonComponentUI; import org.pushingpixels.flamingo.internal.ui.ribbon.RibbonComponentUI; /** * Wrapper around core and 3rd party Swing controls to allow placing them in the * {@link JRibbonBand}. * * @author Kirill Grouchnikov */ public class JRibbonComponent extends RichToolTipManager.JTrackableComponent { /** * Wrapper icon. Can be null. * * @see #JRibbonComponent(ResizableIcon, String, JComponent) */ private ResizableIcon icon; /** * Wrapper caption. Can be null. * * @see #JRibbonComponent(ResizableIcon, String, JComponent) */ private String caption; /** * The wrapped component. Is guaranteed to be non null. */ private JComponent mainComponent; /** * Indication whether this wrapper is simple. A simple wrapper has * null {@link #icon} and null {@link #caption}. */ private boolean isSimpleWrapper; /** * The key tip for this wrapper component. * * @see #setKeyTip(String) * @see #getKeyTip() */ private String keyTip; /** * The rich tooltip for this wrapper component. * * @see #setRichTooltip(RichTooltip) * @see #getRichTooltip(MouseEvent) */ private RichTooltip richTooltip; /** * The horizontal alignment for this wrapper component. * * @see #getHorizontalAlignment() * @see #setHorizontalAlignment(HorizontalAlignment) */ private HorizontalAlignment horizontalAlignment; private RibbonElementPriority displayPriority; private boolean isResizingAware; /** * The UI class ID string. */ public static final String uiClassID = "RibbonComponentUI"; /** * Creates a simple wrapper with no icon and no caption. * * @param mainComponent * Wrapped component. Can not be null. * @throws IllegalArgumentException * if mainComponent is null. */ public JRibbonComponent(JComponent mainComponent) { if (mainComponent == null) throw new IllegalArgumentException( "All parameters must be non-null"); this.mainComponent = mainComponent; this.isSimpleWrapper = true; this.horizontalAlignment = HorizontalAlignment.LEADING; this.isResizingAware = false; this.displayPriority = RibbonElementPriority.TOP; this.updateUI(); } /** * Creates a wrapper with an icon and a caption. * * @param icon * Wrapper icon. Can be null. * @param caption * Wrapper caption. Can not be null. * @param mainComponent * Wrapped component. Can not be null. * @throws IllegalArgumentException * if caption or mainComponent is * null. */ public JRibbonComponent(ResizableIcon icon, String caption, JComponent mainComponent) { if (caption == null) throw new IllegalArgumentException("Caption must be non-null"); if (mainComponent == null) throw new IllegalArgumentException( "Main component must be non-null"); this.icon = icon; this.caption = caption; this.mainComponent = mainComponent; this.isSimpleWrapper = false; this.horizontalAlignment = HorizontalAlignment.TRAILING; this.updateUI(); } /* * (non-Javadoc) * * @see javax.swing.JComponent#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI(UIManager.getUI(this)); } else { setUI(BasicRibbonComponentUI.createUI(this)); } } /* * (non-Javadoc) * * @see javax.swing.JComponent#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /** * Returns the UI object which implements the L&F for this component. * * @return a RibbonUI object * @see #setUI */ public RibbonComponentUI getUI() { return (RibbonComponentUI) ui; } /** * Returns the wrapper icon of this wrapper component. Can return * null. * * @return The wrapper icon of this wrapper component. * @see #JRibbonComponent(ResizableIcon, String, JComponent) */ public ResizableIcon getIcon() { return this.icon; } /** * Returns the caption of this wrapper component. Can return * null. * * @return The caption of this wrapper component. * @see #JRibbonComponent(ResizableIcon, String, JComponent) */ public String getCaption() { return this.caption; } /** * Sets new value for the caption of this wrapper component. * * @param caption * The new caption. */ public void setCaption(String caption) { if (this.isSimpleWrapper) { throw new IllegalArgumentException( "Cannot set caption on a simple component"); } if (caption == null) { throw new IllegalArgumentException("Caption must be non-null"); } String old = this.caption; this.caption = caption; this.firePropertyChange("caption", old, this.caption); } /** * Returns the wrapped component of this wrapper component. The result is * guaranteed to be non null. * * @return The wrapped component of this wrapper component. */ public JComponent getMainComponent() { return this.mainComponent; } /** * Returns indication whether this wrapper is simple. * * @return true if both {@link #getIcon()} and * {@link #getCaption()} return null, * false otherwise. */ public boolean isSimpleWrapper() { return this.isSimpleWrapper; } /** * Returns the key tip for this wrapper component. * * @return The key tip for this wrapper component. * @see #setKeyTip(String) */ public String getKeyTip() { return this.keyTip; } /** * Sets the specified string to be the key tip for this wrapper component. * Fires a keyTip property change event. * * @param keyTip * The new key tip for this wrapper component. */ public void setKeyTip(String keyTip) { String old = this.keyTip; this.keyTip = keyTip; this.firePropertyChange("keyTip", old, this.keyTip); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.RichToolTipManager.JTrackableComponent# * getRichTooltip(java.awt.event.MouseEvent) */ @Override public RichTooltip getRichTooltip(MouseEvent mouseEvent) { return this.richTooltip; } /** * Sets the rich tooltip for this wrapper component. * * @param richTooltip * @see #getRichTooltip(MouseEvent) */ public void setRichTooltip(RichTooltip richTooltip) { this.richTooltip = richTooltip; RichToolTipManager richToolTipManager = RichToolTipManager .sharedInstance(); if (richTooltip != null) { richToolTipManager.registerComponent(this); } else { richToolTipManager.unregisterComponent(this); } } /** * Returns the horizontal alignment for this wrapper component. * * @return The horizontal alignment for this wrapper component. * @see #setHorizontalAlignment(HorizontalAlignment) */ public HorizontalAlignment getHorizontalAlignment() { return this.horizontalAlignment; } /** * Sets the specified parameter to be the horizontal alignment for this * wrapper component. * * @param horizontalAlignment * The new horizontal alignment for this wrapper component. * @see #getHorizontalAlignment() */ public void setHorizontalAlignment(HorizontalAlignment horizontalAlignment) { this.horizontalAlignment = horizontalAlignment; } public RibbonElementPriority getDisplayPriority() { return this.displayPriority; } public void setDisplayPriority(RibbonElementPriority displayPriority) { RibbonElementPriority old = this.displayPriority; this.displayPriority = displayPriority; if (old != displayPriority) { this.firePropertyChange("displayPriority", old, this.displayPriority); } } public boolean isResizingAware() { return this.isResizingAware; } public void setResizingAware(boolean isResizingAware) { this.isResizingAware = isResizingAware; } } src/org/pushingpixels/flamingo/api/ribbon/resize/0000755000175000017500000000000011415653300021221 5ustar tonytonysrc/org/pushingpixels/flamingo/api/ribbon/resize/RibbonBandResizeSequencingPolicy.java0000644000175000017500000000613411401230444030447 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon.resize; import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand; import org.pushingpixels.flamingo.api.ribbon.RibbonTask; import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizeSequencingPolicies.CollapseFromLast; import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizeSequencingPolicies.RoundRobin; /** * Defines the resize sequencing policies for {@link RibbonTask}s. * *

* The resize sequencing policy defines which ribbon band will be chosen next * when the ribbon is shrinked / expanded. It is installed with the * {@link RibbonTask#setResizeSequencingPolicy(RibbonBandResizeSequencingPolicy)} * . *

* *

* The {@link CoreRibbonResizeSequencingPolicies} provides two built in resize * sequencing policies: *

* *
    *
  • {@link RoundRobin} under which the ribbon bands are being collapsed in a * cyclic fashion, distributing the collapsed pixels between the different * bands.
  • *
  • {@link CollapseFromLast} under which the ribbon bands are being collapsed * from right to left.
  • *
* * @author Kirill Grouchnikov */ public interface RibbonBandResizeSequencingPolicy { /** * Resets this policy. Note that this method is for internal use only and * should not be called by the application code. */ public void reset(); /** * Returns the next ribbon band for collapse. * * @return The next ribbon band for collapse. */ public AbstractRibbonBand next(); } src/org/pushingpixels/flamingo/api/ribbon/resize/RibbonBandResizePolicy.java0000644000175000017500000001723711401230444026433 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon.resize; import org.pushingpixels.flamingo.api.common.AbstractCommandButton; import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies.*; /** * Defines the resize policies for the {@link JRibbonBand}s and * {@link JFlowRibbonBand}s. * *

* The resize policy defines a single visual state of the given ribbon band. For * every control in the specific ribbon band (command button, gallery etc), the * resize policy defines what is its display state. *

* *

* The resize policies are installed with * {@link AbstractRibbonBand#setResizePolicies(java.util.List)} API. The order * of the resize policies in this list is important. The first entry in the list * must be the most permissive policies that returns the largest value from its * {@link #getPreferredWidth(int, int)}. Each successive entry in the list must * return the value smaller than its predecessors. The last entry * must be {@link IconRibbonBandResizePolicy}. *

* *

* As the ribbon horizontal size is changed (by the user resizing the * application window), the ribbon task resize sequencing policy set by * {@link RibbonTask#setResizeSequencingPolicy(RibbonBandResizeSequencingPolicy)} * determines the order of ribbon bands to shrink / expand. See more details in * the documentation of the {@link RibbonBandResizeSequencingPolicy}. *

* *

* The {@link CoreRibbonResizePolicies} provides a number of built in resize * policies that respect the application element priorities passed to * {@link JRibbonBand#addCommandButton(org.pushingpixels.flamingo.api.common.AbstractCommandButton, org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority)} * and * {@link JRibbonBand#addRibbonGallery(String, java.util.List, java.util.Map, int, int, org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority)} * APIs. There are three types of built in resize policies: *

* *
    *
  • Resize policies for the {@link JFlowRibbonBand}s. The {@link FlowTwoRows} * and {@link FlowThreeRows} allow placing the flow ribbon band content in two * and three rows respectively.
  • *
  • Resize policies for the {@link JRibbonBand}s. The * {@link BaseCoreRibbonBandResizePolicy} is the base class for these policies. * These policies respect the {@link RibbonElementPriority} associated on * command buttons and ribbon galleries in {@link #getPreferredWidth(int, int)} * and {@link #install(int, int)}. While {@link #install(int, int)} call on a * {@link JFlowRibbonBand} only changes the bounds of the flow components, this * call on a {@link JRibbonBand} can also change the display state of the * command buttons (with * {@link AbstractCommandButton#setDisplayState(org.pushingpixels.flamingo.api.common.CommandButtonDisplayState)} * ) and the number of visible buttons in the ribbon galleries.
  • *
  • The collapsed policy that replaces the entire content of the ribbon band * with a single popup button. This is done when there is not enough horizontal * space to show the content of the ribbon band under the most restrictive * resize policy. Activating the popup button will show the original content * under the most permissive resize policy in a popup. This policy is * implemented in the {@link IconRibbonBandResizePolicy}.
  • *
* *

* In addition to the specific resize policies, the * {@link CoreRibbonResizePolicies} provides three core resize policies lists * for {@link JRibbonBand}s: *

* *
    *
  • {@link CoreRibbonResizePolicies#getCorePoliciesPermissive(JRibbonBand)} * returns a list that starts with a resize policy that shows all command * buttons in the {@link CommandButtonDisplayState#BIG} and ribbon galleries * with the largest number of visible buttons, fully utilizing the available * screen space.
  • *
  • {@link CoreRibbonResizePolicies#getCorePoliciesRestrictive(JRibbonBand)} * returns a list that starts with a resize policy that respects the associated * ribbon element priority set on the specific components.
  • *
  • {@link CoreRibbonResizePolicies#getCorePoliciesNone(JRibbonBand)} returns * a list that only has a mirror resize policy that respects the * associated ribbon element priority set on the specific components.
  • *
* *

* Note that as mentioned above, all the three lists above have the * collapsed policy as their last element. *

* *

* In addition, the * {@link CoreRibbonResizePolicies#getCoreFlowPoliciesRestrictive(JFlowRibbonBand, int)} * returns a restrictive resize policy for {@link JFlowRibbonBand}s. The list * starts with the two-row policy, goes to the three-row policy and then finally * to the collapsed policy. *

* * @author Kirill Grouchnikov */ public interface RibbonBandResizePolicy { /** * Returns the preferred width of the associated ribbon band under the * specified dimensions. * * @param availableHeight * The height available for the associated ribbon band. * @param gap * The inter-component gap. * @return The preferred width of the associated ribbon band under the * specified dimensions. */ public int getPreferredWidth(int availableHeight, int gap); /** * Installs this resize policy on the associated ribbon band. For * {@link JFlowRibbonBand}s only changes the bounds of the flow components. * For {@link JRibbonBand}s can also change the display state of the command * buttons (with * {@link AbstractCommandButton#setDisplayState(org.pushingpixels.flamingo.api.common.CommandButtonDisplayState)} * ) and the number of visible buttons in the ribbon galleries. Note that * this method is for internal use only and should not be called by the * application code. * * @param availableHeight * The height available for the associated ribbon band. * @param gap * The inter-component gap. */ public void install(int availableHeight, int gap); } src/org/pushingpixels/flamingo/api/ribbon/resize/BaseRibbonBandResizePolicy.java0000644000175000017500000000461311401230444027220 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon.resize; import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel; /** * Base class for the core ribbon band resize policies. * * @author Kirill Grouchnikov * @param * Class parameter that specifies the type of band control panel * implementation. */ public abstract class BaseRibbonBandResizePolicy implements RibbonBandResizePolicy { /** * The control panel of the associated ribbon band. */ protected T controlPanel; /** * Creates a new resize policy. * * @param controlPanel * The control panel of the associated ribbon band. */ protected BaseRibbonBandResizePolicy(T controlPanel) { this.controlPanel = controlPanel; } } src/org/pushingpixels/flamingo/api/ribbon/resize/CoreRibbonResizeSequencingPolicies.java0000644000175000017500000001056511401230444031006 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon.resize; import java.util.List; import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand; import org.pushingpixels.flamingo.api.ribbon.RibbonTask; /** * The core resize sequencing policies. Provides the following: * *
    *
  • {@link RoundRobin} under which the ribbon bands are being collapsed in a * cyclic fashion, distributing the collapsed pixels between the different * bands.
  • *
  • {@link CollapseFromLast} under which the ribbon bands are being collapsed * from right to left.
  • *
* * @author Kirill Grouchnikov */ public class CoreRibbonResizeSequencingPolicies { /** * The round robin resize sequencing policy. Under this policy the ribbon * bands are being collapsed in a cyclic fashion, distributing the collapsed * pixels between the different bands. * * @author Kirill Grouchnikov */ public static class RoundRobin extends BaseRibbonBandResizeSequencingPolicy { /** * The index of the next ribbon task for collapsing. */ int nextIndex; /** * Creates a new round robin resize sequencing policy for the specified * task. * * @param ribbonTask * Ribbon task. */ public RoundRobin(RibbonTask ribbonTask) { super(ribbonTask); } @Override public void reset() { this.nextIndex = this.ribbonTask.getBandCount() - 1; } @Override public AbstractRibbonBand next() { AbstractRibbonBand result = this.ribbonTask.getBand(this.nextIndex); this.nextIndex--; if (this.nextIndex < 0) this.nextIndex = this.ribbonTask.getBandCount() - 1; return result; } } /** * The collapse from last resize sequencing policy. Under this policy the * ribbon bands are being collapsed from right to left. * * @author Kirill Grouchnikov */ public static class CollapseFromLast extends BaseRibbonBandResizeSequencingPolicy { /** * The index of the next ribbon task for collapsing. */ int nextIndex; /** * Creates a new collapse from last resize sequencing policy for the * specified task. * * @param ribbonTask * Ribbon task. */ public CollapseFromLast(RibbonTask ribbonTask) { super(ribbonTask); } @Override public void reset() { this.nextIndex = this.ribbonTask.getBandCount() - 1; } @Override public AbstractRibbonBand next() { AbstractRibbonBand result = this.ribbonTask.getBand(this.nextIndex); // check whether the current resize policy on the returned ribbon // band is the last List resizePolicies = result .getResizePolicies(); if (result.getCurrentResizePolicy() == resizePolicies .get(resizePolicies.size() - 1)) { this.nextIndex--; if (this.nextIndex < 0) this.nextIndex = 0; } return result; } } } src/org/pushingpixels/flamingo/api/ribbon/resize/BaseRibbonBandResizeSequencingPolicy.java0000644000175000017500000000435311401230444031243 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon.resize; import org.pushingpixels.flamingo.api.ribbon.RibbonTask; /** * Base class for the core ribbon band resize sequencing policies. * * @author Kirill Grouchnikov */ public abstract class BaseRibbonBandResizeSequencingPolicy implements RibbonBandResizeSequencingPolicy { /** * The associated ribbon task. */ protected RibbonTask ribbonTask; /** * Creates a new resize sequencing policy. * * @param ribbonTask * The associated ribbon task. */ protected BaseRibbonBandResizeSequencingPolicy(RibbonTask ribbonTask) { this.ribbonTask = ribbonTask; } } src/org/pushingpixels/flamingo/api/ribbon/resize/CoreRibbonResizePolicies.java0000644000175000017500000007652011415724760027005 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon.resize; import java.awt.Insets; import java.util.*; import javax.swing.JComponent; import org.pushingpixels.flamingo.api.common.AbstractCommandButton; import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState; import org.pushingpixels.flamingo.api.ribbon.*; import org.pushingpixels.flamingo.internal.ui.ribbon.*; /** * The core resize policies. Provides a number of built in resize policies that * respect the application element priorities passed to * {@link JRibbonBand#addCommandButton(org.pushingpixels.flamingo.api.common.AbstractCommandButton, org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority)} * and * {@link JRibbonBand#addRibbonGallery(String, java.util.List, java.util.Map, int, int, org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority)} * APIs. There are three types of built in resize policies:

* *
    *
  • Resize policies for the {@link JFlowRibbonBand}s. The {@link FlowTwoRows} * and {@link FlowThreeRows} allow placing the flow ribbon band content in two * and three rows respectively.
  • *
  • Resize policies for the {@link JRibbonBand}s. The * {@link BaseCoreRibbonBandResizePolicy} is the base class for these policies. * These policies respect the {@link RibbonElementPriority} associated on * command buttons and ribbon galleries in * {@link RibbonBandResizePolicy#getPreferredWidth(int, int)} and * {@link RibbonBandResizePolicy#install(int, int)}. While * {@link RibbonBandResizePolicy#install(int, int)} call on a * {@link JFlowRibbonBand} only changes the bounds of the flow components, this * call on a {@link JRibbonBand} can also change the display state of the * command buttons (with * {@link AbstractCommandButton#setDisplayState(org.pushingpixels.flamingo.api.common.CommandButtonDisplayState)} * ) and the number of visible buttons in the ribbon galleries.
  • *
  • The collapsed policy that replaces the entire content of the ribbon band * with a single popup button. This is done when there is not enough horizontal * space to show the content of the ribbon band under the most restrictive * resize policy. Activating the popup button will show the original content * under the most permissive resize policy in a popup. This policy is * implemented in the {@link IconRibbonBandResizePolicy}.
  • *
* *

* In addition to the specific resize policies, this class provides three core * resize policies lists for {@link JRibbonBand}s: *

* *
    *
  • {@link #getCorePoliciesPermissive(JRibbonBand)} returns a list that * starts with a resize policy that shows all command buttons in the * {@link CommandButtonDisplayState#BIG} and ribbon galleries with the largest * number of visible buttons, fully utilizing the available screen space.
  • *
  • {@link #getCorePoliciesRestrictive(JRibbonBand)} returns a list that * starts with a resize policy that respects the associated ribbon element * priority set on the specific components.
  • *
  • {@link #getCorePoliciesNone(JRibbonBand)} returns a list that only has a * mirror resize policy that respects the associated ribbon element * priority set on the specific components.
  • *
* *

* Note that as mentioned above, all the three lists above have the * collapsed policy as their last element. *

* *

* In addition, the * {@link #getCoreFlowPoliciesRestrictive(JFlowRibbonBand, int)} returns a * restrictive resize policy for {@link JFlowRibbonBand}s. The list starts with * the two-row policy, goes to the three-row policy and then finally to the * collapsed policy. *

* * @author Kirill Grouchnikov */ public class CoreRibbonResizePolicies { /** * Maps the element priority associated with a ribbon band component to the * element priority assigned by the specific resize policy. * * @author Kirill Grouchnikov */ static interface Mapping { /** * Maps the element priority associated with a ribbon band component to * the element priority assigned by the specific resize policy. * * @param priority * The element priority associated with a ribbon band * component * @return The element priority assigned by the specific resize policy. */ RibbonElementPriority map(RibbonElementPriority priority); } /** * Returns a list that starts with a resize policy that shows all command * buttons in the {@link CommandButtonDisplayState#BIG} and ribbon galleries * with the largest number of visible buttons. The last entry is the * {@link IconRibbonBandResizePolicy}. * * @param ribbonBand * Ribbon band. * @return The permissive list of core ribbon band resize policies. */ public static List getCorePoliciesPermissive( JRibbonBand ribbonBand) { List result = new ArrayList(); result.add(new CoreRibbonResizePolicies.None(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.Low2Mid(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.Mid2Mid(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.Mirror(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.Mid2Low(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.High2Mid(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.High2Low(ribbonBand .getControlPanel())); result .add(new IconRibbonBandResizePolicy(ribbonBand .getControlPanel())); return result; } /** * Returns a list that starts with a resize policy that respects the * associated ribbon element priority set on the specific components. The * last entry is the {@link IconRibbonBandResizePolicy}. * * @param ribbonBand * Ribbon band. * @return The restrictive list of core ribbon band resize policies. */ public static List getCorePoliciesRestrictive( JRibbonBand ribbonBand) { List result = new ArrayList(); result.add(new CoreRibbonResizePolicies.Mirror(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.Mid2Low(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.High2Mid(ribbonBand .getControlPanel())); result.add(new CoreRibbonResizePolicies.High2Low(ribbonBand .getControlPanel())); result .add(new IconRibbonBandResizePolicy(ribbonBand .getControlPanel())); return result; } /** * Returns a list that only has a mirror resize policy that * respects the associated ribbon element priority set on the specific * components. The last entry is the {@link IconRibbonBandResizePolicy}. * * @param ribbonBand * Ribbon band. * @return The mirror list of core ribbon band resize policies. */ public static List getCorePoliciesNone( JRibbonBand ribbonBand) { List result = new ArrayList(); result.add(new CoreRibbonResizePolicies.Mirror(ribbonBand .getControlPanel())); result .add(new IconRibbonBandResizePolicy(ribbonBand .getControlPanel())); return result; } /** * The base class for mapping-based core resize policies. * * @author Kirill Grouchnikov */ protected static abstract class BaseCoreRibbonBandResizePolicy extends BaseRibbonBandResizePolicy { /** * The element priority mapping. */ protected Mapping mapping; /** * Creates a new resize policy. * * @param controlPanel * The control panel of the associated ribbon band. * @param mapping * The element priority mapping. */ protected BaseCoreRibbonBandResizePolicy( JBandControlPanel controlPanel, Mapping mapping) { super(controlPanel); this.mapping = mapping; } /** * Returns the total width of the specified buttons. * * @param gap * Inter component gap. * @param bigButtons * List of buttons in big display state. * @param mediumButtons * List of buttons in medium display state. * @param smallButtons * List of buttons in small display state. * @return Total width of the specified buttons. */ protected int getWidth(int gap, java.util.List bigButtons, java.util.List mediumButtons, java.util.List smallButtons) { int result = 0; boolean hasLeadingContent = false; for (AbstractCommandButton top : bigButtons) { if (hasLeadingContent) { result += gap; } result += getPreferredWidth(top, RibbonElementPriority.TOP); hasLeadingContent = true; } int medSize = mediumButtons.size(); if (medSize > 0) { // try to move buttons from low to med to make // three-somes. while (((mediumButtons.size() % 3) != 0) && (smallButtons.size() > 0)) { AbstractCommandButton low = smallButtons.remove(0); mediumButtons.add(low); } } // at this point, mediumButtons list contains either // threesomes, or there are no buttons in lowButtons. int index3 = 0; int maxWidth3 = 0; for (AbstractCommandButton medium : mediumButtons) { int medWidth = getPreferredWidth(medium, RibbonElementPriority.MEDIUM); maxWidth3 = Math.max(maxWidth3, medWidth); index3++; if (index3 == 3) { // last button in threesome index3 = 0; if (hasLeadingContent) { result += gap; } result += maxWidth3; hasLeadingContent = true; maxWidth3 = 0; } } // at this point, maxWidth3 may be non-zero. We can safely // add it, since in this case there will be no buttons // left in mediumButtons if (maxWidth3 > 0) { if (hasLeadingContent) { result += gap; } result += maxWidth3; hasLeadingContent = true; } index3 = 0; maxWidth3 = 0; for (AbstractCommandButton low : smallButtons) { int lowWidth = getPreferredWidth(low, RibbonElementPriority.LOW); maxWidth3 = Math.max(maxWidth3, lowWidth); index3++; if (index3 == 3) { // last button in threesome index3 = 0; if (hasLeadingContent) { result += gap; } result += maxWidth3; hasLeadingContent = true; maxWidth3 = 0; } } // at this point, maxWidth3 may be non-zero. We can safely // add it, since in this case there will be no buttons left if (maxWidth3 > 0) { if (hasLeadingContent) { result += gap; } result += maxWidth3; hasLeadingContent = true; } return result; } /** * Returns the preferred width of the specified command button under the * specified display priority. * * @param button * Command button. * @param buttonDisplayPriority * Button display priority. * @return The preferred width of the specified command button under the * specified display priority. */ private int getPreferredWidth(AbstractCommandButton button, RibbonElementPriority buttonDisplayPriority) { CommandButtonDisplayState displayState = null; switch (buttonDisplayPriority) { case TOP: displayState = CommandButtonDisplayState.BIG; break; case MEDIUM: displayState = CommandButtonDisplayState.MEDIUM; break; case LOW: displayState = CommandButtonDisplayState.SMALL; break; } return displayState.createLayoutManager(button).getPreferredSize( button).width; } @Override public int getPreferredWidth(int availableHeight, int gap) { int result = 0; Insets ins = this.controlPanel.getInsets(); for (JBandControlPanel.ControlPanelGroup controlPanelGroup : this.controlPanel .getControlPanelGroups()) { boolean isCoreContent = controlPanelGroup.isCoreContent(); if (isCoreContent) { List ribbonComps = controlPanelGroup .getRibbonComps(); Map ribbonCompRowSpans = controlPanelGroup .getRibbonCompsRowSpans(); // if a group has a title, then the core components in that // group start from the second row int startRowIndex = (controlPanelGroup.getGroupTitle() == null) ? 0 : 1; int rowIndex = startRowIndex; int maxWidthInCurrColumn = 0; for (int i = 0; i < ribbonComps.size(); i++) { JRibbonComponent ribbonComp = ribbonComps.get(i); int rowSpan = ribbonCompRowSpans.get(ribbonComp); // do we need to start a new column? int nextRowIndex = rowIndex + rowSpan; if (nextRowIndex > 3) { result += maxWidthInCurrColumn; result += gap; maxWidthInCurrColumn = 0; rowIndex = startRowIndex; } RibbonElementPriority targetPriority = RibbonElementPriority.TOP; if (ribbonComp.isResizingAware()) { targetPriority = this.mapping .map(RibbonElementPriority.TOP); } int prefWidth = ribbonComp.getUI().getPreferredSize( targetPriority).width; maxWidthInCurrColumn = Math.max(maxWidthInCurrColumn, prefWidth); rowIndex += rowSpan; } if ((rowIndex > 0) && (rowIndex <= 3)) { result += maxWidthInCurrColumn; result += gap; } } else { int galleryAvailableHeight = availableHeight - ins.top - ins.bottom; // ribbon galleries result += this.getPreferredGalleryWidth(controlPanelGroup, galleryAvailableHeight, gap); // ribbon buttons result += this.getPreferredButtonWidth(controlPanelGroup, gap); } result += gap * 3 / 2; } // no gap after the last group result -= gap * 3 / 2; // control panel insets result += ins.left + ins.right; result += gap; return result; } /** * Returns the preferred width of all the buttons in the specified * control panel group. * * @param controlPanelGroup * A single control panel group in the associated ribbon * band. * @param gap * Inter component gap. * @return The preferred width of all the buttons in the specified * control panel group. */ protected int getPreferredButtonWidth( JBandControlPanel.ControlPanelGroup controlPanelGroup, int gap) { Map> mapped = new HashMap>(); for (RibbonElementPriority rep : RibbonElementPriority.values()) { mapped.put(rep, new ArrayList()); } for (RibbonElementPriority elementPriority : RibbonElementPriority .values()) { // map the priority RibbonElementPriority mappedPriority = mapping .map(elementPriority); for (AbstractCommandButton button : controlPanelGroup .getRibbonButtons(elementPriority)) { // and add the button to a list based on the mapped priority mapped.get(mappedPriority).add(button); } } // at this point, the lists in the 'mapped' contain the buttons // grouped by the priority computed by the resize policy. return this.getWidth(gap, mapped.get(RibbonElementPriority.TOP), mapped.get(RibbonElementPriority.MEDIUM), mapped .get(RibbonElementPriority.LOW)); } /** * Returns the preferred width of all the ribbon galleries in the * specified control panel group. * * @param controlPanelGroup * A single control panel group in the associated ribbon * band. * @param galleryAvailableHeight * Available height for the ribbon galleries. * @param gap * Inter component gap. * @return The preferred width of all the ribbon galleries in the * specified control panel group. */ private int getPreferredGalleryWidth( JBandControlPanel.ControlPanelGroup controlPanelGroup, int galleryAvailableHeight, int gap) { int result = 0; for (RibbonElementPriority elementPriority : RibbonElementPriority .values()) { // map the priority RibbonElementPriority mappedPriority = mapping .map(elementPriority); // go over all galleries registered with the specific priority for (JRibbonGallery gallery : controlPanelGroup .getRibbonGalleries(elementPriority)) // and take the preferred width under the mapped priority result += (gallery.getPreferredWidth(mappedPriority, galleryAvailableHeight) + gap); } return result; } @Override public void install(int availableHeight, int gap) { for (JBandControlPanel.ControlPanelGroup controlPanelGroup : this.controlPanel .getControlPanelGroups()) { boolean isCoreContent = controlPanelGroup.isCoreContent(); if (isCoreContent) { List ribbonComps = controlPanelGroup .getRibbonComps(); for (int i = 0; i < ribbonComps.size(); i++) { JRibbonComponent ribbonComp = ribbonComps.get(i); RibbonElementPriority targetPriority = RibbonElementPriority.TOP; if (ribbonComp.isResizingAware()) { targetPriority = this.mapping .map(RibbonElementPriority.TOP); } ribbonComp.setDisplayPriority(targetPriority); } } else { // set the display priority for the galleries for (RibbonElementPriority elementPriority : RibbonElementPriority .values()) { // map the priority RibbonElementPriority mappedPriority = mapping .map(elementPriority); // go over all galleries registered with the specific // priority for (JRibbonGallery gallery : controlPanelGroup .getRibbonGalleries(elementPriority)) // and set the display priority based on the // specific // resize policy gallery.setDisplayPriority(mappedPriority); } // set the display priority for the buttons Map> mapped = new HashMap>(); for (RibbonElementPriority rep : RibbonElementPriority .values()) { mapped.put(rep, new ArrayList()); } for (RibbonElementPriority elementPriority : RibbonElementPriority .values()) { // map the priority RibbonElementPriority mappedPriority = mapping .map(elementPriority); for (AbstractCommandButton button : controlPanelGroup .getRibbonButtons(elementPriority)) { // and add the button to a list based on the mapped // priority mapped.get(mappedPriority).add(button); } } // start from the top priority for (AbstractCommandButton big : mapped .get(RibbonElementPriority.TOP)) { big.setDisplayState(CommandButtonDisplayState.BIG); } // next - medium priority if (mapped.get(RibbonElementPriority.MEDIUM).size() > 0) { // try to move buttons from small to medium to make // three-somes. while (((mapped.get(RibbonElementPriority.MEDIUM) .size() % 3) != 0) && (mapped.get(RibbonElementPriority.LOW) .size() > 0)) { AbstractCommandButton low = mapped.get( RibbonElementPriority.LOW).get(0); mapped.get(RibbonElementPriority.LOW).remove(low); mapped.get(RibbonElementPriority.MEDIUM).add(low); } } for (AbstractCommandButton medium : mapped .get(RibbonElementPriority.MEDIUM)) { medium .setDisplayState(CommandButtonDisplayState.MEDIUM); } // finally - low priority for (AbstractCommandButton low : mapped .get(RibbonElementPriority.LOW)) { low.setDisplayState(CommandButtonDisplayState.SMALL); } } } } } /** * Core resize policy that maps all {@link RibbonElementPriority}s to * {@link RibbonElementPriority#TOP}. * * @author Kirill Grouchnikov */ public static final class None extends BaseCoreRibbonBandResizePolicy { /** * Creates the new resize policy of type NONE. * * @param controlPanel * The control panel of the associated ribbon band. */ public None(JBandControlPanel controlPanel) { super(controlPanel, new Mapping() { @Override public RibbonElementPriority map(RibbonElementPriority priority) { return RibbonElementPriority.TOP; } }); } } /** * Core resize policy that maps: * *
    *
  • {@link RibbonElementPriority#TOP} -> * {@link RibbonElementPriority#TOP}
  • *
  • {@link RibbonElementPriority#MEDIUM} -> * {@link RibbonElementPriority#TOP}
  • *
  • {@link RibbonElementPriority#LOW} -> * {@link RibbonElementPriority#MEDIUM}
  • *
* * @author Kirill Grouchnikov */ public static final class Low2Mid extends BaseCoreRibbonBandResizePolicy { /** * Creates the new resize policy of type LOW2MID. * * @param controlPanel * The control panel of the associated ribbon band. */ public Low2Mid(JBandControlPanel controlPanel) { super(controlPanel, new Mapping() { @Override public RibbonElementPriority map(RibbonElementPriority priority) { switch (priority) { case TOP: return RibbonElementPriority.TOP; case MEDIUM: return RibbonElementPriority.TOP; case LOW: return RibbonElementPriority.MEDIUM; } return null; } }); } } /** * Core resize policy that maps: * *
    *
  • {@link RibbonElementPriority#TOP} -> * {@link RibbonElementPriority#TOP}
  • *
  • {@link RibbonElementPriority#MEDIUM} -> * {@link RibbonElementPriority#MEDIUM}
  • *
  • {@link RibbonElementPriority#LOW} -> * {@link RibbonElementPriority#MEDIUM}
  • *
* * @author Kirill Grouchnikov */ public static final class Mid2Mid extends BaseCoreRibbonBandResizePolicy { /** * Creates the new resize policy of type MID2MID. * * @param controlPanel * The control panel of the associated ribbon band. */ public Mid2Mid(JBandControlPanel controlPanel) { super(controlPanel, new Mapping() { @Override public RibbonElementPriority map(RibbonElementPriority priority) { switch (priority) { case TOP: return RibbonElementPriority.TOP; case MEDIUM: return RibbonElementPriority.MEDIUM; case LOW: return RibbonElementPriority.MEDIUM; } return null; } }); } } /** * Mirror core resize policy that maps the values of * {@link RibbonElementPriority}s to themselves. * * @author Kirill Grouchnikov */ public static final class Mirror extends BaseCoreRibbonBandResizePolicy { /** * Creates the new resize policy of type MIRROR. * * @param controlPanel * The control panel of the associated ribbon band. */ public Mirror(JBandControlPanel controlPanel) { super(controlPanel, new Mapping() { @Override public RibbonElementPriority map(RibbonElementPriority priority) { return priority; } }); } } /** * Core resize policy that maps: * *
    *
  • {@link RibbonElementPriority#TOP} -> * {@link RibbonElementPriority#TOP}
  • *
  • {@link RibbonElementPriority#MEDIUM} -> * {@link RibbonElementPriority#LOW}
  • *
  • {@link RibbonElementPriority#LOW} -> * {@link RibbonElementPriority#LOW}
  • *
* * @author Kirill Grouchnikov */ public static final class Mid2Low extends BaseCoreRibbonBandResizePolicy { /** * Creates the new resize policy of type MID2LOW. * * @param controlPanel * The control panel of the associated ribbon band. */ public Mid2Low(JBandControlPanel controlPanel) { super(controlPanel, new Mapping() { @Override public RibbonElementPriority map(RibbonElementPriority priority) { switch (priority) { case TOP: return RibbonElementPriority.TOP; case MEDIUM: return RibbonElementPriority.LOW; case LOW: return RibbonElementPriority.LOW; } return null; } }); } } /** * Core resize policy that maps: * *
    *
  • {@link RibbonElementPriority#TOP} -> * {@link RibbonElementPriority#MEDIUM}
  • *
  • {@link RibbonElementPriority#MEDIUM} -> * {@link RibbonElementPriority#LOW}
  • *
  • {@link RibbonElementPriority#LOW} -> * {@link RibbonElementPriority#LOW}
  • *
* * @author Kirill Grouchnikov */ public static final class High2Mid extends BaseCoreRibbonBandResizePolicy { /** * Creates the new resize policy of type HIGH2MID. * * @param controlPanel * The control panel of the associated ribbon band. */ public High2Mid(JBandControlPanel controlPanel) { super(controlPanel, new Mapping() { @Override public RibbonElementPriority map(RibbonElementPriority priority) { switch (priority) { case TOP: return RibbonElementPriority.MEDIUM; case MEDIUM: return RibbonElementPriority.LOW; case LOW: return RibbonElementPriority.LOW; } return null; } }); } } /** * Core resize policy that maps all {@link RibbonElementPriority}s to * {@link RibbonElementPriority#LOW}. * * @author Kirill Grouchnikov */ public static final class High2Low extends BaseCoreRibbonBandResizePolicy { /** * Creates the new resize policy of type HIGH2LOW. * * @param controlPanel * The control panel of the associated ribbon band. */ public High2Low(JBandControlPanel controlPanel) { super(controlPanel, new Mapping() { @Override public RibbonElementPriority map(RibbonElementPriority priority) { return RibbonElementPriority.LOW; } }); } } /** * Core resize policy for {@link JFlowRibbonBand} that places the content in * two rows. * * @author Kirill Grouchnikov */ public static class FlowTwoRows extends BaseRibbonBandResizePolicy { /** * Creates a new two-row resize policy for {@link JFlowRibbonBand}s. * * @param controlPanel * The control panel of the associated ribbon band. */ public FlowTwoRows(JFlowBandControlPanel controlPanel) { super(controlPanel); } @Override public int getPreferredWidth(int availableHeight, int gap) { int compCount = controlPanel.getFlowComponents().size(); int[] widths = new int[compCount]; int index = 0; int currBestResult = 0; for (JComponent flowComp : controlPanel.getFlowComponents()) { int pref = flowComp.getPreferredSize().width; widths[index++] = pref; currBestResult += (pref + gap); } // need to find the inflection point that results in // lowest value for max length of two sub-sequences for (int inflectionIndex = 0; inflectionIndex < (compCount - 1); inflectionIndex++) { int w1 = 0; for (int index1 = 0; index1 <= inflectionIndex; index1++) { w1 += widths[index1] + gap; } int w2 = 0; for (int index2 = inflectionIndex + 1; index2 < compCount; index2++) { w2 += widths[index2] + gap; } int width = Math.max(w1, w2); if (width < currBestResult) currBestResult = width; } return currBestResult; } @Override public void install(int availableHeight, int gap) { } } /** * Core resize policy for {@link JFlowRibbonBand} that places the content in * three rows. * * @author Kirill Grouchnikov */ public static class FlowThreeRows extends BaseRibbonBandResizePolicy { /** * Creates a new three-row resize policy for {@link JFlowRibbonBand}s. * * @param controlPanel * The control panel of the associated ribbon band. */ public FlowThreeRows(JFlowBandControlPanel controlPanel) { super(controlPanel); } @Override public int getPreferredWidth(int availableHeight, int gap) { int compCount = controlPanel.getFlowComponents().size(); int[] widths = new int[compCount]; int index = 0; int currBestResult = 0; for (JComponent flowComp : controlPanel.getFlowComponents()) { int pref = flowComp.getPreferredSize().width; widths[index++] = pref; currBestResult += (pref + gap); } // need to find the inflection points that results in // lowest value for max length of three sub-sequences for (int inflectionIndex1 = 0; inflectionIndex1 < (compCount - 2); inflectionIndex1++) { for (int inflectionIndex2 = inflectionIndex1 + 1; inflectionIndex2 < (compCount - 1); inflectionIndex2++) { int w1 = 0; for (int index1 = 0; index1 <= inflectionIndex1; index1++) { w1 += widths[index1] + gap; } int w2 = 0; for (int index2 = inflectionIndex1 + 1; index2 <= inflectionIndex2; index2++) { w2 += widths[index2] + gap; } int w3 = 0; for (int index3 = inflectionIndex2 + 1; index3 < compCount; index3++) { w3 += widths[index3] + gap; } int width = Math.max(Math.max(w1, w2), w3); if (width < currBestResult) currBestResult = width; } } return currBestResult; } @Override public void install(int availableHeight, int gap) { } } /** * Returns a list that has {@link FlowTwoRows} policy followed by the * {@link FlowThreeRows} resize policy. The last entry is the * {@link IconRibbonBandResizePolicy}. * * @param ribbonBand * Ribbon band. * @param stepsToRepeat * The number of times each one of the {@link FlowTwoRows} / * {@link FlowThreeRows} should appear consecutively in the * returned list. * @return The restrictive list of core ribbon band resize policies. */ public static List getCoreFlowPoliciesRestrictive( JFlowRibbonBand ribbonBand, int stepsToRepeat) { List result = new ArrayList(); for (int i = 0; i < stepsToRepeat; i++) { result.add(new FlowTwoRows(ribbonBand.getControlPanel())); } for (int i = 0; i < stepsToRepeat; i++) { result.add(new FlowThreeRows(ribbonBand.getControlPanel())); } result .add(new IconRibbonBandResizePolicy(ribbonBand .getControlPanel())); return result; } } src/org/pushingpixels/flamingo/api/ribbon/resize/IconRibbonBandResizePolicy.java0000644000175000017500000000624411401230444027240 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon.resize; import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand; import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel; import org.pushingpixels.flamingo.internal.ui.ribbon.RibbonBandUI; /** * Special resize policy that is used for collapsed ribbon bands. When there is * not enough horizontal space to show the ribbon band content under the most * restructive {@link RibbonBandResizePolicy}, the entire ribbon band content is * replaced by a single popup button. Activating the popup button will show the * original content under the most permissive resize policy in a popup. * *

* An instance of this policy must appear exactly once in the * list passed to {@link AbstractRibbonBand#setResizePolicies(java.util.List)}, * and it must be the last entry in that list. *

* * @author Kirill Grouchnikov */ public class IconRibbonBandResizePolicy extends BaseRibbonBandResizePolicy { /** * Creates a new collapsed resize policy. * * @param controlPanel * The control panel of the associated ribbon band. */ public IconRibbonBandResizePolicy(AbstractBandControlPanel controlPanel) { super(controlPanel); } @Override public int getPreferredWidth(int availableHeight, int gap) { AbstractRibbonBand ribbonBand = this.controlPanel.getRibbonBand(); RibbonBandUI ui = ribbonBand.getUI(); return ui.getPreferredCollapsedWidth(); } @Override public void install(int availableHeight, int gap) { } } src/org/pushingpixels/flamingo/api/ribbon/JRibbon.java0000644000175000017500000005475411401230444022122 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.Component; import java.awt.event.ActionListener; import java.util.*; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.internal.ui.ribbon.BasicRibbonUI; import org.pushingpixels.flamingo.internal.ui.ribbon.RibbonUI; /** * The ribbon component. * *

* The ribbon has the following major parts: *

*
    *
  • Ribbon tasks added with {@link #addTask(RibbonTask)}
  • *
  • Contextual ribbon task groups added with * {@link #addContextualTaskGroup(RibbonContextualTaskGroup)}
  • *
  • Application menu button set by * {@link #setApplicationMenu(RibbonApplicationMenu)}
  • *
  • Taskbar panel populated by {@link #addTaskbarComponent(Component)}
  • *
  • Help button set by {@link #configureHelp(ResizableIcon, ActionListener)}
  • *
* *

* While multiple ribbon tasks can be added to the ribbon, only one is visible * at any given time. This task is called the selected task. * Tasks can be switched with the task buttons placed along the top part of the * ribbon. Once a task has been added to the ribbon, it cannot be removed. *

* *

* The contextual ribbon task groups allow showing and hiding ribbon tasks based * on the current selection in the application. For example, Word only shows the * table tasks when a table is selected in the document. By default, tasks * belonging to the groups adde by * {@link #addContextualTaskGroup(RibbonContextualTaskGroup)} are not visible. * To show the tasks belonging to the specific group, call * {@link #setVisible(RibbonContextualTaskGroup, boolean)} API. Note that you * can have multiple task groups visible at the same time. *

* *

* The application menu button is a big round button shown in the top left * corner of the ribbon. If the * {@link #setApplicationMenu(RibbonApplicationMenu)} is not called, or called * with the null value, the application menu button is not shown, * and ribbon task buttons are shifted to the left. *

* *

* The taskbar panel allows showing controls that are visible no matter what * ribbon task is selected. To add a taskbar component use the * {@link #addTaskbarComponent(Component)} API. The taskbar panel lives to the * right of the application menu button. Taskbar components can be removed with * the {@link #removeTaskbarComponent(Component)} API. *

* *

* The ribbon can be minimized in one of the following ways: *

*
    *
  • Calling {@link #setMinimized(boolean)} with true.
  • *
  • User double-clicking on a task button.
  • *
  • User pressing Ctrl+F1 key combination.
  • *
* *

* A minimized ribbon shows the application menu button, taskbar panel, task * buttons and help button, but not the ribbon bands of the selected task. * Clicking a task button shows the ribbon bands of that task in a popup * without shifting the application content down. *

* * @author Kirill Grouchnikov */ public class JRibbon extends JComponent { /** * The general tasks. * * @see #addTask(RibbonTask) * @see #getTaskCount() * @see #getTask(int) */ private ArrayList tasks; /** * The contextual task groups. * * @see #addContextualTaskGroup(RibbonContextualTaskGroup) * @see #setVisible(RibbonContextualTaskGroup, boolean) * @see #isVisible(RibbonContextualTaskGroup) * @see #getContextualTaskGroupCount() * @see #getContextualTaskGroup(int) */ private ArrayList contextualTaskGroups; /** * The taskbar components (to the right of the application menu button). * * @see #addTaskbarComponent(Component) * @see #getTaskbarComponents() * @see #removeTaskbarComponent(Component) */ private ArrayList taskbarComponents; /** * Bands of the currently shown task. */ private ArrayList bands; /** * Currently selected (shown) task. */ private RibbonTask currentlySelectedTask; /** * Help icon. When not null, the ribbon will display a help * button at the far right of the tab area. * * @see #helpActionListener * @see #configureHelp(ResizableIcon, ActionListener) * @see #getHelpIcon() */ private ResizableIcon helpIcon; /** * When the {@link #helpIcon} is not null, this listener will * be invoked when the user activates the help button. * * @see #configureHelp(ResizableIcon, ActionListener) * @see #getHelpActionListener() */ private ActionListener helpActionListener; /** * Visibility status of the contextual task group. Must contain a value for * each group in {@link #contextualTaskGroups}. * * @see #setVisible(RibbonContextualTaskGroup, boolean) * @see #isVisible(RibbonContextualTaskGroup) */ private Map groupVisibilityMap; /** * The application menu. * * @see #setApplicationMenu(RibbonApplicationMenu) * @see #getApplicationMenu() */ private RibbonApplicationMenu applicationMenu; /** * The rich tooltip of {@link #applicationMenu} button. * * @see #applicationMenu * @see #setApplicationMenuRichTooltip(RichTooltip) * @see #getApplicationMenuRichTooltip() */ private RichTooltip applicationMenuRichTooltip; /** * The key tip of {@link #applicationMenu} button. * * @see #applicationMenu * @see #setApplicationMenuKeyTip(String) * @see #getApplicationMenuKeyTip() */ private String applicationMenuKeyTip; /** * Indicates whether the ribbon is currently minimized. * * @see #setMinimized(boolean) * @see #isMinimized() */ private boolean isMinimized; /** * The host ribbon frame. Is null when the ribbon is not hosted * in a {@link JRibbonFrame}. */ private JRibbonFrame ribbonFrame; /** * The UI class ID string. */ public static final String uiClassID = "RibbonUI"; /** * Creates a new empty ribbon. Applications are highly encouraged to use * {@link JRibbonFrame} and access the ribbon with * {@link JRibbonFrame#getRibbon()} API. */ public JRibbon() { this.tasks = new ArrayList(); this.contextualTaskGroups = new ArrayList(); this.taskbarComponents = new ArrayList(); this.bands = new ArrayList(); this.currentlySelectedTask = null; this.groupVisibilityMap = new HashMap(); updateUI(); } /** * Creates an empty ribbon for the specified ribbon frame. * * @param ribbonFrame * Host ribbon frame. */ JRibbon(JRibbonFrame ribbonFrame) { this(); this.ribbonFrame = ribbonFrame; } /** * Adds the specified taskbar component to this ribbon. * * @param comp * The taskbar component to add. * @see #removeTaskbarComponent(Component) * @see #getTaskbarComponents() */ public synchronized void addTaskbarComponent(Component comp) { if (comp instanceof AbstractCommandButton) { AbstractCommandButton button = (AbstractCommandButton) comp; button.setDisplayState(CommandButtonDisplayState.SMALL); button.setGapScaleFactor(0.5); button.setFocusable(false); } this.taskbarComponents.add(comp); this.fireStateChanged(); } /** * Removes the specified taskbar component from this ribbon. * * @param comp * The taskbar component to remove. * @see #addTaskbarComponent(Component) * @see #getTaskbarComponents() */ public synchronized void removeTaskbarComponent(Component comp) { this.taskbarComponents.remove(comp); this.fireStateChanged(); } /** * Adds the specified task to this ribbon. * * @param task * The ribbon task to add. * @see #addContextualTaskGroup(RibbonContextualTaskGroup) * @see #getTaskCount() * @see #getTask(int) */ public synchronized void addTask(RibbonTask task) { task.setRibbon(this); this.tasks.add(task); if (this.tasks.size() == 1) { this.setSelectedTask(task); } this.fireStateChanged(); } /** * Configures the help button of this ribbon. * * @param helpIcon * The icon for the help button. * @param helpActionListener * The action listener for the help button. * @see #getHelpIcon() * @see #getHelpActionListener() */ public synchronized void configureHelp(ResizableIcon helpIcon, ActionListener helpActionListener) { this.helpIcon = helpIcon; this.helpActionListener = helpActionListener; this.fireStateChanged(); } /** * Returns the icon for the help button. Will return null if * the help button has not been configured with the * {@link #configureHelp(ResizableIcon, ActionListener)} API. * * @return The icon for the help button. * @see #configureHelp(ResizableIcon, ActionListener) * @see #getHelpActionListener() */ public ResizableIcon getHelpIcon() { return this.helpIcon; } /** * Returns the action listener for the help button. Will return * null if the help button has not been configured with the * {@link #configureHelp(ResizableIcon, ActionListener)} API. * * @return The action listener for the help button. * @see #configureHelp(ResizableIcon, ActionListener) * @see #getHelpIcon() */ public ActionListener getHelpActionListener() { return this.helpActionListener; } /** * Adds the specified contextual task group to this ribbon. * * @param group * Task group to add. * @see #addTask(RibbonTask) * @see #setVisible(RibbonContextualTaskGroup, boolean) * @see #isVisible(RibbonContextualTaskGroup) */ public synchronized void addContextualTaskGroup( RibbonContextualTaskGroup group) { group.setRibbon(this); this.contextualTaskGroups.add(group); this.groupVisibilityMap.put(group, Boolean.valueOf(false)); this.fireStateChanged(); } /** * Returns the number of regular tasks in this ribbon. * * @return Number of regular tasks in this ribbon. * @see #getTask(int) * @see #addTask(RibbonTask) */ public synchronized int getTaskCount() { return this.tasks.size(); } /** * Retrieves the regular task at specified index. * * @param index * Task index. * @return Task that matches the specified index. * @see #getTaskCount() * @see #addTask(RibbonTask) */ public synchronized RibbonTask getTask(int index) { return this.tasks.get(index); } /** * Returns the number of contextual task groups in this ribbon. * * @return Number of contextual task groups in this ribbon. * @see #addContextualTaskGroup(RibbonContextualTaskGroup) * @see #getContextualTaskGroup(int) */ public synchronized int getContextualTaskGroupCount() { return this.contextualTaskGroups.size(); } /** * Retrieves contextual task group at specified index. * * @param index * Group index. * @return Group that matches the specified index. * @see #addContextualTaskGroup(RibbonContextualTaskGroup) * @see #getContextualTaskGroupCount() */ public synchronized RibbonContextualTaskGroup getContextualTaskGroup( int index) { return this.contextualTaskGroups.get(index); } /** * Selects the specified task. The task can be either regular (added with * {@link #addTask(RibbonTask)}) or a task in a visible contextual task * group (added with * {@link #addContextualTaskGroup(RibbonContextualTaskGroup)}. Fires a * selectedTask property change event. * * @param task * Task to select. * @throws IllegalArgumentException * If the specified task is not in the ribbon or not visible. * @see #getSelectedTask() */ public synchronized void setSelectedTask(RibbonTask task) { boolean valid = this.tasks.contains(task); if (!valid) { for (int i = 0; i < this.getContextualTaskGroupCount(); i++) { RibbonContextualTaskGroup group = this .getContextualTaskGroup(i); if (!this.isVisible(group)) continue; for (int j = 0; j < group.getTaskCount(); j++) { if (group.getTask(j) == task) { valid = true; break; } } if (valid) break; } } if (!valid) { throw new IllegalArgumentException( "The specified task to be selected is either not " + "part of this ribbon or not marked as visible"); } for (AbstractRibbonBand ribbonBand : this.bands) { ribbonBand.setVisible(false); } this.bands.clear(); for (int i = 0; i < task.getBandCount(); i++) { AbstractRibbonBand ribbonBand = task.getBand(i); ribbonBand.setVisible(true); this.bands.add(ribbonBand); } RibbonTask old = this.currentlySelectedTask; this.currentlySelectedTask = task; this.revalidate(); this.repaint(); this .firePropertyChange("selectedTask", old, this.currentlySelectedTask); } /** * Returns the currently selected task. * * @return The currently selected task. * @see #setSelectedTask(RibbonTask) */ public synchronized RibbonTask getSelectedTask() { return this.currentlySelectedTask; } /* * (non-Javadoc) * * @see javax.swing.JComponent#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI(UIManager.getUI(this)); } else { setUI(new BasicRibbonUI()); } for (Component comp : this.taskbarComponents) { SwingUtilities.updateComponentTreeUI(comp); } } /** * Returns the UI object which implements the L&F for this component. * * @return a RibbonUI object * @see #setUI */ public RibbonUI getUI() { return (RibbonUI) ui; } /* * (non-Javadoc) * * @see javax.swing.JComponent#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /** * Gets an unmodifiable list of all taskbar components of this * ribbon. * * @return All taskbar components of this ribbon. * @see #addTaskbarComponent(Component) * @see #removeTaskbarComponent(Component) */ public synchronized List getTaskbarComponents() { return Collections.unmodifiableList(this.taskbarComponents); } /** * Adds the specified change listener to track changes to this ribbon. * * @param l * Change listener to add. * @see #removeChangeListener(ChangeListener) */ public void addChangeListener(ChangeListener l) { this.listenerList.add(ChangeListener.class, l); } /** * Removes the specified change listener from tracking changes to this * ribbon. * * @param l * Change listener to remove. * @see #addChangeListener(ChangeListener) */ public void removeChangeListener(ChangeListener l) { this.listenerList.remove(ChangeListener.class, l); } /** * Notifies all registered listeners that the state of this ribbon has * changed. */ protected void fireStateChanged() { // Guaranteed to return a non-null array Object[] listeners = this.listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event ChangeEvent event = new ChangeEvent(this); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { ((ChangeListener) listeners[i + 1]).stateChanged(event); } } } /** * Sets the visibility of ribbon tasks in the specified contextual task * group. Visibility of all ribbon tasks in the specified group is affected. * Note that the ribbon can show ribbon tasks of multiple groups at the same * time. * * @param group * Contextual task group. * @param isVisible * If true, all ribbon tasks in the specified group * will be visible. If false, all ribbon tasks in * the specified group will be hidden. * @see #isVisible(RibbonContextualTaskGroup) */ public synchronized void setVisible(RibbonContextualTaskGroup group, boolean isVisible) { this.groupVisibilityMap.put(group, Boolean.valueOf(isVisible)); // special handling of selected tab if (!isVisible) { boolean isSelectedBeingHidden = false; for (int i = 0; i < group.getTaskCount(); i++) { if (this.getSelectedTask() == group.getTask(i)) { isSelectedBeingHidden = true; break; } } if (isSelectedBeingHidden) { this.setSelectedTask(this.getTask(0)); } } this.fireStateChanged(); this.revalidate(); SwingUtilities.getWindowAncestor(this).repaint(); } /** * Returns the visibility of ribbon tasks in the specified contextual task * group. * * @param group * Contextual task group. * @return true if the ribbon tasks in the specified group are * visible, false otherwise. */ public synchronized boolean isVisible(RibbonContextualTaskGroup group) { return this.groupVisibilityMap.get(group); } /** * Sets the application menu for this ribbon. If null is * passed, the application menu button is hidden. Fires an * applicationMenu property change event. * * @param applicationMenu * The new application menu. Can be null. * @see #getApplicationMenu() */ public synchronized void setApplicationMenu( RibbonApplicationMenu applicationMenu) { RibbonApplicationMenu old = this.applicationMenu; if (old != applicationMenu) { this.applicationMenu = applicationMenu; if (this.applicationMenu != null) { this.applicationMenu.setFrozen(); } this.firePropertyChange("applicationMenu", old, this.applicationMenu); } } /** * Returns the application menu of this ribbon. * * @return The application menu of this ribbon. * @see #setApplicationMenu(RibbonApplicationMenu) */ public synchronized RibbonApplicationMenu getApplicationMenu() { return this.applicationMenu; } /** * Sets the rich tooltip of the application menu button. Fires an * applicationMenuRichTooltip property change event. * * @param tooltip * The rich tooltip of the application menu button. * @see #getApplicationMenuRichTooltip() * @see #setApplicationMenu(RibbonApplicationMenu) */ public synchronized void setApplicationMenuRichTooltip(RichTooltip tooltip) { RichTooltip old = this.applicationMenuRichTooltip; this.applicationMenuRichTooltip = tooltip; this.firePropertyChange("applicationMenuRichTooltip", old, this.applicationMenuRichTooltip); } /** * Returns the rich tooltip of the application menu button. * * @return The rich tooltip of the application menu button. * @see #setApplicationMenuRichTooltip(RichTooltip) * @see #setApplicationMenu(RibbonApplicationMenu) */ public synchronized RichTooltip getApplicationMenuRichTooltip() { return this.applicationMenuRichTooltip; } /** * Sets the key tip of the application menu button. Fires an * applicationMenuKeyTip property change event. * * @param keyTip * The new key tip for the application menu button. * @see #setApplicationMenu(RibbonApplicationMenu) * @see #getApplicationMenuKeyTip() */ public synchronized void setApplicationMenuKeyTip(String keyTip) { String old = this.applicationMenuKeyTip; this.applicationMenuKeyTip = keyTip; this.firePropertyChange("applicationMenuKeyTip", old, this.applicationMenuKeyTip); } /** * Returns the key tip of the application menu button. * * @return The key tip of the application menu button. * @see #setApplicationMenuKeyTip(String) * @see #setApplicationMenu(RibbonApplicationMenu) */ public synchronized String getApplicationMenuKeyTip() { return this.applicationMenuKeyTip; } /** * Returns the indication whether this ribbon is minimized. * * @return true if this ribbon is minimized, false * otherwise. * @see #setMinimized(boolean) */ public synchronized boolean isMinimized() { return this.isMinimized; } /** * Changes the minimized state of this ribbon. Fires a * minimized property change event. * * @param isMinimized * if true, this ribbon becomes minimized, otherwise * it is unminimized. */ public synchronized void setMinimized(boolean isMinimized) { // System.out.println("Ribbon minimized -> " + isMinimized); boolean old = this.isMinimized; if (old != isMinimized) { this.isMinimized = isMinimized; this.firePropertyChange("minimized", old, this.isMinimized); } } /** * Returns the ribbon frame that hosts this ribbon. The result can be * null. * * @return The ribbon frame that hosts this ribbon. */ public JRibbonFrame getRibbonFrame() { return this.ribbonFrame; } /* * (non-Javadoc) * * @see javax.swing.JComponent#setVisible(boolean) */ @Override public void setVisible(boolean flag) { if (!flag && (getRibbonFrame() != null)) throw new IllegalArgumentException( "Can't hide ribbon on JRibbonFrame"); super.setVisible(flag); } } src/org/pushingpixels/flamingo/api/ribbon/JFlowRibbonBand.java0000644000175000017500000000724311401230444023526 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.ActionListener; import javax.swing.JComponent; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies; import org.pushingpixels.flamingo.internal.ui.ribbon.JFlowBandControlPanel; /** * Flow ribbon band component. Hosts components added with * {@link #addFlowComponent(JComponent)} in flowing rows. Depending on the * current resize policy, the content is shown in either two or three rows. * * @author Kirill Grouchnikov */ public class JFlowRibbonBand extends AbstractRibbonBand { /** * Creates a new flow ribbon band. * * @param title * Band title. * @param icon * Associated icon (for collapsed state). */ public JFlowRibbonBand(String title, ResizableIcon icon) { this(title, icon, null); } /** * Creates a new flow ribbon band. * * @param title * Band title. * @param icon * Associated icon (for collapsed state). * @param expandActionListener * Expand action listener (can be null). */ public JFlowRibbonBand(String title, ResizableIcon icon, ActionListener expandActionListener) { super(title, icon, expandActionListener, new JFlowBandControlPanel()); this.resizePolicies = CoreRibbonResizePolicies .getCoreFlowPoliciesRestrictive(this, 3); updateUI(); } /** * Adds the specified component to this flow ribbon band. * * @param comp * Component to add. */ public void addFlowComponent(JComponent comp) { this.controlPanel.addFlowComponent(comp); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.ribbon.AbstractRibbonBand#cloneBand() */ @Override public AbstractRibbonBand cloneBand() { AbstractRibbonBand result = new JFlowRibbonBand( this.getTitle(), this.getIcon(), this.getExpandActionListener()); result.applyComponentOrientation(this.getComponentOrientation()); return result; } } src/org/pushingpixels/flamingo/api/ribbon/JRibbonFrame.java0000644000175000017500000005146711426360556023112 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.*; import java.awt.event.*; import java.lang.reflect.Method; import java.util.*; import java.util.List; import java.util.concurrent.CountDownLatch; import javax.swing.*; import org.pushingpixels.flamingo.api.common.AsynchronousLoadListener; import org.pushingpixels.flamingo.api.common.AsynchronousLoading; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; import org.pushingpixels.flamingo.internal.ui.ribbon.*; import org.pushingpixels.flamingo.internal.utils.*; import org.pushingpixels.flamingo.internal.utils.KeyTipManager.KeyTipEvent; /** *

* Ribbon frame. Provides the same functionality as a regular {@link JFrame}, * but with a {@link JRibbon} component in the top location. *

* *

* This is the only officially supported way to use the {@link JRibbon} * container. While {@link JRibbon#JRibbon()} constructor is public, it is * provided only for the applications that are absolutely prevented from using * {@link JRibbonFrame} class. *

* *

* The implementation enforces that a {@link JRibbon} component is always at the * {@link BorderLayout#NORTH} location, throwing * {@link IllegalArgumentException} on attempts to set a custom layout manager, * add another component at {@link BorderLayout#NORTH}, remove the * {@link JRibbon} component, set a custom menu bar, content pane or any other * operation that inteferes with the intended hierarchy of this frame. *

* * @author Kirill Grouchnikov */ public class JRibbonFrame extends JFrame { /** * The ribbon component. */ private JRibbon ribbon; private ResizableIcon appIcon; private boolean wasSetIconImagesCalled; /** * Custom layout manager that enforces the {@link JRibbon} location at * {@link BorderLayout#NORTH}. * * @author Kirill Grouchnikov */ private class RibbonFrameLayout extends BorderLayout { @Override public void addLayoutComponent(Component comp, Object constraints) { if ((constraints != null) && constraints.equals(BorderLayout.NORTH)) { if (getLayoutComponent(BorderLayout.NORTH) != null) { throw new IllegalArgumentException( "Already has a NORTH JRibbon component"); } if (!(comp instanceof JRibbon)) { throw new IllegalArgumentException( "Can't add non-JRibbon component to NORTH location"); } } super.addLayoutComponent(comp, constraints); } @Override public void removeLayoutComponent(Component comp) { if (comp instanceof JRibbon) { throw new IllegalArgumentException( "Can't remove JRibbon component"); } super.removeLayoutComponent(comp); } } /** * A custom layer that shows the currently visible key tip chain. * * @author Kirill Grouchnikov */ private class KeyTipLayer extends JComponent { /** * Creates a new key tip layer. */ public KeyTipLayer() { this.setOpaque(false); // Support placing heavyweight components in the ribbon frame. See // http://today.java.net/article/2009/11/02/transparent-panel-mixing-heavyweight-and-lightweight-components. try { Class awtUtilitiesClass = Class .forName("com.sun.awt.AWTUtilities"); Method mSetComponentMixing = awtUtilitiesClass.getMethod( "setComponentMixingCutoutShape", Component.class, Shape.class); mSetComponentMixing.invoke(null, this, new Rectangle()); } catch (Throwable t) { } } @Override public synchronized void addMouseListener(MouseListener l) { } @Override public synchronized void addMouseMotionListener(MouseMotionListener l) { } @Override public synchronized void addMouseWheelListener(MouseWheelListener l) { } @Override public synchronized void addKeyListener(KeyListener l) { } @Override protected void paintComponent(Graphics g) { JRibbonFrame ribbonFrame = (JRibbonFrame) SwingUtilities .getWindowAncestor(this); if (!ribbonFrame.isShowingKeyTips()) return; // don't show keytips on inactive windows if (!ribbonFrame.isActive()) return; Collection keyTips = KeyTipManager .defaultManager().getCurrentlyShownKeyTips(); if (keyTips != null) { Graphics2D g2d = (Graphics2D) g.create(); RenderingUtils.installDesktopHints(g2d); for (KeyTipManager.KeyTipLink keyTip : keyTips) { // don't display keytips on components in popup panels if (SwingUtilities.getAncestorOfClass(JPopupPanel.class, keyTip.comp) != null) continue; // don't display key tips on hidden components Rectangle compBounds = keyTip.comp.getBounds(); if (!keyTip.comp.isShowing() || (compBounds.getWidth() == 0) || (compBounds.getHeight() == 0)) continue; Dimension pref = KeyTipRenderingUtilities.getPrefSize(g2d .getFontMetrics(), keyTip.keyTipString); Point prefCenter = keyTip.prefAnchorPoint; Point loc = SwingUtilities.convertPoint(keyTip.comp, prefCenter, this); Container bandControlPanel = SwingUtilities .getAncestorOfClass(AbstractBandControlPanel.class, keyTip.comp); if (bandControlPanel != null) { // special case for controls in threesome // ribbon band rows if (hasClientPropertySetToTrue(keyTip.comp, BasicBandControlPanelUI.TOP_ROW)) { loc = SwingUtilities.convertPoint(keyTip.comp, prefCenter, bandControlPanel); loc.y = 0; loc = SwingUtilities.convertPoint(bandControlPanel, loc, this); // prefCenter.y = 0; } if (hasClientPropertySetToTrue(keyTip.comp, BasicBandControlPanelUI.MID_ROW)) { loc = SwingUtilities.convertPoint(keyTip.comp, prefCenter, bandControlPanel); loc.y = bandControlPanel.getHeight() / 2; loc = SwingUtilities.convertPoint(bandControlPanel, loc, this); // prefCenter.y = keyTip.comp.getHeight() / 2; } if (hasClientPropertySetToTrue(keyTip.comp, BasicBandControlPanelUI.BOTTOM_ROW)) { loc = SwingUtilities.convertPoint(keyTip.comp, prefCenter, bandControlPanel); loc.y = bandControlPanel.getHeight(); loc = SwingUtilities.convertPoint(bandControlPanel, loc, this); // prefCenter.y = keyTip.comp.getHeight(); } } KeyTipRenderingUtilities .renderKeyTip(g2d, this, new Rectangle(loc.x - pref.width / 2, loc.y - pref.height / 2, pref.width, pref.height), keyTip.keyTipString, keyTip.enabled); } g2d.dispose(); } } /** * Checks whether the specified component or one of its ancestors has * the specified client property set to {@link Boolean#TRUE}. * * @param c * Component. * @param clientPropName * Client property name. * @return true if the specified component or one of its * ancestors has the specified client property set to * {@link Boolean#TRUE}, false otherwise. */ private boolean hasClientPropertySetToTrue(Component c, String clientPropName) { while (c != null) { if (c instanceof JComponent) { JComponent jc = (JComponent) c; if (Boolean.TRUE.equals(jc .getClientProperty(clientPropName))) return true; } c = c.getParent(); } return false; } @Override public boolean contains(int x, int y) { // pass the mouse events to the underlying layers for // showing the correct cursor. See // http://weblogs.java.net/blog/alexfromsun/archive/2006/09/ // a_wellbehaved_g.html return false; } } /** * Creates a new ribbon frame with no title. * * @throws HeadlessException * If GraphicsEnvironment.isHeadless() returns true. */ public JRibbonFrame() throws HeadlessException { super(); this.initRibbon(); } /** * Creates a new ribbon frame with no title. * * @param gc * Graphics configuration to use. */ public JRibbonFrame(GraphicsConfiguration gc) { super(gc); this.initRibbon(); } /** * Creates a new ribbon frame with the specified title. * * @param title * Ribbon frame title. * @throws HeadlessException * If GraphicsEnvironment.isHeadless() returns true. */ public JRibbonFrame(String title) throws HeadlessException { super(title); this.initRibbon(); } /** * Creates a new ribbon frame with the specified title. * * @param title * Ribbon frame title. * @param gc * Graphics configuration to use. * @throws HeadlessException * If GraphicsEnvironment.isHeadless() returns true. */ public JRibbonFrame(String title, GraphicsConfiguration gc) { super(title, gc); this.initRibbon(); } /* * (non-Javadoc) * * @see javax.swing.JFrame#setLayout(java.awt.LayoutManager) */ @Override public void setLayout(LayoutManager manager) { if (manager.getClass() != RibbonFrameLayout.class) { LayoutManager currManager = getLayout(); if (currManager != null) { throw new IllegalArgumentException( "Can't set a custom layout manager on JRibbonFrame"); } } super.setLayout(manager); } /* * (non-Javadoc) * * @see javax.swing.JFrame#setJMenuBar(javax.swing.JMenuBar) */ @Override public void setJMenuBar(JMenuBar menubar) { throw new IllegalArgumentException( "Can't set a menu bar on JRibbonFrame"); } /* * (non-Javadoc) * * @see javax.swing.JFrame#setContentPane(java.awt.Container) */ @Override public void setContentPane(Container contentPane) { throw new IllegalArgumentException( "Can't set the content pane on JRibbonFrame"); } /** * Initializes the layout and the ribbon. */ private void initRibbon() { this.setLayout(new RibbonFrameLayout()); this.ribbon = new JRibbon(this); this.add(this.ribbon, BorderLayout.NORTH); // this.keyTipManager = new KeyTipManager(this); Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { private boolean prevAltModif = false; @Override public void eventDispatched(AWTEvent event) { Object src = event.getSource(); if (src instanceof Component) { Component c = (Component) src; if ((c == JRibbonFrame.this) || (SwingUtilities.getWindowAncestor(c) == JRibbonFrame.this)) { if (event instanceof KeyEvent) { KeyEvent keyEvent = (KeyEvent) event; // System.out.println(keyEvent.getID() + ":" // + keyEvent.getKeyCode()); switch (keyEvent.getID()) { case KeyEvent.KEY_PRESSED: // if (keyEvent.getKeyCode() == // KeyEvent.VK_ESCAPE) { // keyTipManager.showPreviousChain(); // } break; case KeyEvent.KEY_RELEASED: boolean wasAltModif = prevAltModif; prevAltModif = keyEvent.getModifiersEx() == InputEvent.ALT_DOWN_MASK; if (wasAltModif && keyEvent.getKeyCode() == KeyEvent.VK_ALT) break; char keyChar = keyEvent.getKeyChar(); if (Character.isLetter(keyChar) || Character.isDigit(keyChar)) { // System.out.println("Will handle key press " // + keyChar); KeyTipManager.defaultManager() .handleKeyPress(keyChar); } if ((keyEvent.getKeyCode() == KeyEvent.VK_ALT) || (keyEvent.getKeyCode() == KeyEvent.VK_F10)) { if (keyEvent.getModifiers() != 0 || keyEvent.getModifiersEx() != 0) break; boolean hadPopups = !PopupPanelManager .defaultManager().getShownPath() .isEmpty(); PopupPanelManager.defaultManager() .hidePopups(null); if (hadPopups || KeyTipManager.defaultManager() .isShowingKeyTips()) { KeyTipManager.defaultManager() .hideAllKeyTips(); } else { KeyTipManager.defaultManager() .showRootKeyTipChain( JRibbonFrame.this); } } if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE) { // System.out.println("In KTM"); KeyTipManager.defaultManager() .showPreviousChain(); } break; } } if (event instanceof MouseEvent) { MouseEvent mouseEvent = (MouseEvent) event; switch (mouseEvent.getID()) { case MouseEvent.MOUSE_CLICKED: case MouseEvent.MOUSE_DRAGGED: case MouseEvent.MOUSE_PRESSED: case MouseEvent.MOUSE_RELEASED: KeyTipManager.defaultManager().hideAllKeyTips(); } } } } } }, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK); final KeyTipLayer keyTipLayer = new KeyTipLayer(); JRootPane rootPane = this.getRootPane(); JLayeredPane layeredPane = rootPane.getLayeredPane(); final LayoutManager currLM = rootPane.getLayout(); rootPane.setLayout(new LayoutManager() { public void addLayoutComponent(String name, Component comp) { currLM.addLayoutComponent(name, comp); } public void layoutContainer(Container parent) { currLM.layoutContainer(parent); JRibbonFrame ribbonFrame = JRibbonFrame.this; if (ribbonFrame.getRootPane().getWindowDecorationStyle() != JRootPane.NONE) keyTipLayer .setBounds(ribbonFrame.getRootPane().getBounds()); else keyTipLayer.setBounds(ribbonFrame.getRootPane() .getContentPane().getBounds()); } public Dimension minimumLayoutSize(Container parent) { return currLM.minimumLayoutSize(parent); } public Dimension preferredLayoutSize(Container parent) { return currLM.preferredLayoutSize(parent); } public void removeLayoutComponent(Component comp) { currLM.removeLayoutComponent(comp); } }); // layeredPane.setLayout(new OverlayLayout(layeredPane)); layeredPane.add(keyTipLayer, (Integer) (JLayeredPane.DEFAULT_LAYER + 60)); this.addWindowListener(new WindowAdapter() { @Override public void windowDeactivated(WindowEvent e) { // hide all key tips on window deactivation KeyTipManager keyTipManager = KeyTipManager.defaultManager(); if (keyTipManager.isShowingKeyTips()) { keyTipManager.hideAllKeyTips(); } } }); KeyTipManager.defaultManager().addKeyTipListener( new KeyTipManager.KeyTipListener() { @Override public void keyTipsHidden(KeyTipEvent event) { if (event.getSource() == JRibbonFrame.this) keyTipLayer.setVisible(false); } @Override public void keyTipsShown(KeyTipEvent event) { if (event.getSource() == JRibbonFrame.this) keyTipLayer.setVisible(true); } }); ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false); JPopupMenu.setDefaultLightWeightPopupEnabled(false); super.setIconImages(Arrays.asList(FlamingoUtilities.getBlankImage(16, 16))); } /** * Returns the ribbon component. * * @return Ribbon component. */ public JRibbon getRibbon() { return this.ribbon; } /* * (non-Javadoc) * * @see javax.swing.JFrame#createRootPane() */ @Override protected JRootPane createRootPane() { JRootPane rp = new JRibbonRootPane(); rp.setOpaque(true); return rp; } @Override public synchronized void setIconImages(List icons) { super.setIconImages(icons); this.wasSetIconImagesCalled = true; } public synchronized void setApplicationIcon(final ResizableIcon icon) { new Thread() { @Override public void run() { // still loading? if (icon instanceof AsynchronousLoading) { AsynchronousLoading async = (AsynchronousLoading) icon; if (async.isLoading()) { final CountDownLatch latch = new CountDownLatch(1); final boolean[] status = new boolean[1]; AsynchronousLoadListener all = new AsynchronousLoadListener() { public void completed(boolean success) { status[0] = success; latch.countDown(); } }; async.addAsynchronousLoadListener(all); try { latch.await(); } catch (InterruptedException ie) { } async.removeAsynchronousLoadListener(all); } } setApplicationAndMenuButtonIcon(icon); } }.start(); } private void setApplicationAndMenuButtonIcon(final ResizableIcon icon) { if (System.getProperty("os.name").startsWith("Mac")) { class MacImages { Image icon16; Image icon128; public MacImages(Image icon16, Image icon128) { this.icon16 = icon16; this.icon128 = icon128; } } final Image image16 = getImage(icon, 16); final Image image128 = getImage(icon, 128); SwingUtilities.invokeLater(new Runnable() { public void run() { if (image16 != null) { setLegacyIconImages(Arrays.asList(image16)); } if (image128 != null) { try { Class appClass = Class .forName("com.apple.eawt.Application"); if (appClass != null) { Object appInstance = appClass.newInstance(); Method setDockImageMethod = appClass .getDeclaredMethod("setDockIconImage", Image.class); if (setDockImageMethod != null) { setDockImageMethod.invoke(appInstance, image128); } } } catch (Throwable t) { t.printStackTrace(); // give up } } setMainAppIcon(icon); } }); } else { final List images = new ArrayList(); Image icon16 = getImage(icon, 16); if (icon16 != null) images.add(icon16); Image icon32 = getImage(icon, 32); if (icon32 != null) images.add(icon32); Image icon64 = getImage(icon, 64); if (icon64 != null) images.add(icon64); SwingUtilities.invokeLater(new Runnable() { public void run() { if (!images.isEmpty()) setLegacyIconImages(images); setMainAppIcon(icon); } }); } } private void setMainAppIcon(ResizableIcon icon) { this.appIcon = icon; FlamingoUtilities.updateRibbonFrameIconImages(this); } private void setLegacyIconImages(List images) { if (this.wasSetIconImagesCalled) { return; } super.setIconImages(images); } private static Image getImage(ResizableIcon icon, int size) { icon.setDimension(new Dimension(size, size)); if (icon instanceof AsynchronousLoading) { AsynchronousLoading async = (AsynchronousLoading) icon; if (async.isLoading()) { final CountDownLatch latch = new CountDownLatch(1); final boolean[] status = new boolean[1]; AsynchronousLoadListener all = new AsynchronousLoadListener() { public void completed(boolean success) { status[0] = success; latch.countDown(); } }; async.addAsynchronousLoadListener(all); try { latch.await(); } catch (InterruptedException ie) { } async.removeAsynchronousLoadListener(all); if (!status[0]) { return null; } if (async.isLoading()) { return null; } } } Image result = FlamingoUtilities.getBlankImage(size, size); Graphics2D g2d = (Graphics2D) result.getGraphics().create(); icon.paintIcon(null, g2d, 0, 0); g2d.dispose(); return result; } public synchronized ResizableIcon getApplicationIcon() { return this.appIcon; } /** * Returns indication whether this ribbon frame is showing the key tips. * * @return true if this ribbon frame is showing the key tips, * false otherwise. */ public boolean isShowingKeyTips() { return KeyTipManager.defaultManager().isShowingKeyTips(); } } src/org/pushingpixels/flamingo/api/ribbon/RibbonApplicationMenu.java0000644000175000017500000002440111401230444025003 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.ActionListener; import java.util.*; import org.pushingpixels.flamingo.api.common.JCommandMenuButton; import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback; /** * Metadata description of the application menu of the {@link JRibbon} * component. The ribbon application menu has three parts: * *
 * +-------------------------------------+
 * |           |                         |
 * |           |                         |
 * |  primary  |        secondary        |
 * |   area    |           area          |        
 * |           |                         |
 * |           |                         |
 * |-------------------------------------|
 * |            footer area              |
 * +-------------------------------------+
 * 
* *

* The entries in the primary area are always visible. The secondary area * entries are shown based on the currently active element in the primary area. * There are three different types of primary entries: *

* *
    *
  • Associated {@link ActionListener} passed to the constructor of the * {@link RibbonApplicationMenuEntryPrimary}. When this entry is armed (with * mouse rollover or via keyboard navigation), the contents of the secondary * area are cleared. The Quit menu item is an example of such a * primary menu entry.
  • *
  • Associated {@link PrimaryRolloverCallback} set by the * {@link RibbonApplicationMenuEntryPrimary#setRolloverCallback(PrimaryRolloverCallback)} * . When this entry is armed (with mouse rollover or via keyboard navigation), * the contents of the secondary area are populated by the application callback * implementation of * {@link PrimaryRolloverCallback#menuEntryActivated(javax.swing.JPanel)}. The * Open menu item is an example of such a primary menu entry, * showing a list of recently opened files.
  • *
  • Associated list of {@link RibbonApplicationMenuEntrySecondary}s added * with the * {@link RibbonApplicationMenuEntryPrimary#addSecondaryMenuGroup(String, RibbonApplicationMenuEntrySecondary...)} * API. When this entry is armed (with mouse rollover or via keyboard * navigation), the secondary area shows menu buttons for the registered * secondary menu entries. The Save As menu item is an example of * such a primary menu item, showing a list of default save formats.
  • *
* *

* At runtime, the application menu entries are implemented as * {@link JCommandMenuButton}, but the application code does not operate on that * level. Instead, the application code creates metadata-driven description of * the ribbon application menu, and that description is used to create and * populate the "real" controls of the application menu popup. *

* *

* Note that once a {@link RibbonApplicationMenu} is set on the {@link JRibbon} * with the {@link JRibbon#setApplicationMenu(RibbonApplicationMenu)}, its * contents cannot be changed. An {@link IllegalStateException} will be thrown * from {@link #addMenuEntry(RibbonApplicationMenuEntryPrimary)} and * {@link #addFooterEntry(RibbonApplicationMenuEntryFooter)}. *

* * @author Kirill Grouchnikov */ public class RibbonApplicationMenu { /** * Indicates whether this ribbon application menu has been set on the * {@link JRibbon} with the * {@link JRibbon#setApplicationMenu(RibbonApplicationMenu)}. Once that API * is called, the contents of this menu cannot be changed. An * {@link IllegalStateException} will be thrown from * {@link #addMenuEntry(RibbonApplicationMenuEntryPrimary)} and * {@link #addFooterEntry(RibbonApplicationMenuEntryFooter)}. * * @see #setFrozen(boolean) * @see #addMenuEntry(RibbonApplicationMenuEntryPrimary) * @see #addFooterEntry(RibbonApplicationMenuEntryFooter) */ private boolean isFrozen; /** * Primary menu entries. */ private List> primaryEntries; /** * Footer menu entries. */ private List footerEntries; /** * The default callback to be called when: * *
    *
  • The ribbon application menu is first shown.
  • *
  • The currently active (rollover) primary application menu entry has no * secondary menu entries and no associated rollover callback. *
*/ private PrimaryRolloverCallback defaultCallback; /** * Creates an empty ribbon application menu. */ public RibbonApplicationMenu() { this.primaryEntries = new ArrayList>(); this.primaryEntries .add(new ArrayList()); this.footerEntries = new ArrayList(); } /** * Adds the specified primary menu entry. * * @param entry * Primary menu entry to add. * @throws IllegalStateException * if this ribbon application menu has already been set on the * {@link JRibbon} with the * {@link JRibbon#setApplicationMenu(RibbonApplicationMenu)}. * @see #getPrimaryEntries() * @see #addFooterEntry(RibbonApplicationMenuEntryFooter) */ public synchronized void addMenuEntry( RibbonApplicationMenuEntryPrimary entry) { if (this.isFrozen) { throw new IllegalStateException( "Cannot add menu entries after the menu has been set on the ribbon"); } this.primaryEntries.get(this.primaryEntries.size() - 1).add(entry); } public synchronized void addMenuSeparator() { if (this.isFrozen) { throw new IllegalStateException( "Cannot add menu entries after the menu has been set on the ribbon"); } this.primaryEntries .add(new ArrayList()); } /** * Returns an unmodifiable list of all primary menu entries of this * application menu. The result is guaranteed to be non-null. * * @return An unmodifiable list of all primary menu entries of this * application menu. * @see #addMenuEntry(RibbonApplicationMenuEntryPrimary) * @see #getFooterEntries() */ public List> getPrimaryEntries() { return Collections.unmodifiableList(this.primaryEntries); } /** * Adds the specified footer menu entry. * * @param entry * Footer menu entry to add. * @throws IllegalStateException * if this ribbon application menu has already been set on the * {@link JRibbon} with the * {@link JRibbon#setApplicationMenu(RibbonApplicationMenu)}. * @see #getFooterEntries() * @see #addMenuEntry(RibbonApplicationMenuEntryPrimary) */ public synchronized void addFooterEntry( RibbonApplicationMenuEntryFooter entry) { if (this.isFrozen) { throw new IllegalStateException( "Cannot add footer entries after the menu has been set on the ribbon"); } this.footerEntries.add(entry); } /** * Returns an unmodifiable list of all footer menu entries of this * application menu. The result is guaranteed to be non-null. * * @return An unmodifiable list of all footer menu entries of this * application menu. * @see #addFooterEntry(RibbonApplicationMenuEntryFooter) * @see #getPrimaryEntries() */ public List getFooterEntries() { return Collections.unmodifiableList(this.footerEntries); } /** * Sets the default callback to be called when: * *
    *
  • The ribbon application menu is first shown.
  • *
  • The currently active (rollover) primary application menu entry has no * secondary menu entries and no associated rollover callback. *
* * @param defaultCallback * Default callback. */ public void setDefaultCallback(PrimaryRolloverCallback defaultCallback) { this.defaultCallback = defaultCallback; } /** * Returns the default callback of this ribbon application menu. * * @return The default callback of this ribbon application menu. * @see #setDefaultCallback(PrimaryRolloverCallback) */ public PrimaryRolloverCallback getDefaultCallback() { return defaultCallback; } /** * Marks this application menu as frozen. Subsequent calls to * {@link #addMenuEntry(RibbonApplicationMenuEntryPrimary)} and * {@link #addFooterEntry(RibbonApplicationMenuEntryFooter)} will throw an * {@link IllegalStateException}. * * @see #addMenuEntry(RibbonApplicationMenuEntryPrimary) * @see #addFooterEntry(RibbonApplicationMenuEntryFooter) * @see JRibbon#setApplicationMenu(RibbonApplicationMenu) */ synchronized void setFrozen() { this.isFrozen = true; if (this.primaryEntries.get(this.primaryEntries.size() - 1).isEmpty()) { this.primaryEntries.remove(this.primaryEntries.size() - 1); } } } src/org/pushingpixels/flamingo/api/ribbon/RibbonApplicationMenuEntryPrimary.java0000644000175000017500000002255011412757714027414 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.ActionListener; import java.util.*; import javax.swing.JPanel; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; /** * Metadata description for the primary menu entries of the * {@link RibbonApplicationMenu}. The primary menu entries at runtime are * represented by command menu buttons placed in the left panel of the * application menu. * *

* There are three different types of primary entries: *

* *
    *
  • Associated {@link ActionListener} passed to the * {@link RibbonApplicationMenuEntryPrimary#RibbonApplicationMenuEntryPrimary(ResizableIcon, String, ActionListener, CommandButtonKind)} * . When this entry is armed (with mouse rollover or via keyboard navigation), * the contents of the secondary area are cleared. The Quit menu * item is an example of such a primary menu entry.
  • *
  • Associated {@link PrimaryRolloverCallback} set by the * {@link #setRolloverCallback(PrimaryRolloverCallback)} . When this entry is * armed (with mouse rollover or via keyboard navigation), the contents of the * secondary area are populated by the application callback implementation of * {@link PrimaryRolloverCallback#menuEntryActivated(javax.swing.JPanel)}. The * Open menu item is an example of such a primary menu entry, * showing a list of recently opened files.
  • *
  • Associated list of {@link RibbonApplicationMenuEntrySecondary}s added * with the * {@link #addSecondaryMenuGroup(String, RibbonApplicationMenuEntrySecondary...)} * API. When this entry is armed (with mouse rollover or via keyboard * navigation), the secondary area shows menu buttons for the registered * secondary menu entries. The Save As menu item is an example of * such a primary menu item, showing a list of default save formats.
  • *
* * @author Kirill Grouchnikov */ public class RibbonApplicationMenuEntryPrimary extends RibbonApplicationMenuEntry { /** * An optional rollover callback. It allows the application to place custom * content in the secondary panel of the {@link RibbonApplicationMenu} when * this primary menu entry is activated. * * @see #setRolloverCallback(PrimaryRolloverCallback) * @see #getRolloverCallback() */ protected PrimaryRolloverCallback rolloverCallback; /** * Callback that allows application code to provide custom content on the * secondary panel of the {@link RibbonApplicationMenu}. * * @author Kirill Grouchnikov */ public static interface PrimaryRolloverCallback { /** * Called when the matching primary menu item is activated. * * @param targetPanel * The secondary panel of the {@link RibbonApplicationMenu}. * Note that the application code must not * change the parent hierarchy of this panel. */ public void menuEntryActivated(JPanel targetPanel); } /** * List of titles for all menu groups. */ protected List groupTitles; /** * List of all menu groups. */ protected List> groupEntries; /** * Creates the metadata description of a {@link RibbonApplicationMenu} * primary menu entry. * * @param icon * The icon of this menu entry. Must be non-null. * @param text * The text of this menu entry. Must be non-null. * @param mainActionListener * The main action listener for this menu entry. If the entry * kind is {@link CommandButtonKind#POPUP_ONLY}, this listener * will be ignored. * @param entryKind * The kind of the command button that will represent this menu * entry. Must be non- null. */ public RibbonApplicationMenuEntryPrimary(ResizableIcon icon, String text, ActionListener mainActionListener, CommandButtonKind entryKind) { super(icon, text, mainActionListener, entryKind); this.groupTitles = new ArrayList(); this.groupEntries = new ArrayList>(); } /** * Adds a titled group of secondary menu entries. * * @param groupTitle * The title of the group. * @param entries * The secondary menu entries belonging to this group. * @return The index of the newly added menu group. * @see #getSecondaryGroupCount() * @see #getSecondaryGroupTitleAt(int) * @see #getSecondaryGroupEntries(int) */ public synchronized int addSecondaryMenuGroup(String groupTitle, RibbonApplicationMenuEntrySecondary... entries) { this.groupTitles.add(groupTitle); List entryList = new ArrayList(); this.groupEntries.add(entryList); for (RibbonApplicationMenuEntrySecondary entry : entries) { entryList.add(entry); } return this.groupTitles.size() - 1; } /** * Returns the number of secondary menu groups of this primary menu entry. * * @return The number of secondary menu groups of this primary menu entry. * @see #addSecondaryMenuGroup(String, * RibbonApplicationMenuEntrySecondary...) * @see #getSecondaryGroupTitleAt(int) * @see #getSecondaryGroupEntries(int) */ public int getSecondaryGroupCount() { return this.groupTitles.size(); } /** * Returns the title of the secondary menu group at the specified index. * * @param groupIndex * The index of a secondary menu group. * @return The title of the secondary menu group at the specified index. * @see #addSecondaryMenuGroup(String, * RibbonApplicationMenuEntrySecondary...) * @see #getSecondaryGroupCount() * @see #getSecondaryGroupEntries(int) */ public String getSecondaryGroupTitleAt(int groupIndex) { return this.groupTitles.get(groupIndex); } /** * Returns an unmodifiable list of menu entries of the secondary menu group * at the specified index. * * @param groupIndex * The index of a secondary menu group. * @return An unmodifiable list of menu entries of the secondary menu group * at the specified index. * @see #addSecondaryMenuGroup(String, * RibbonApplicationMenuEntrySecondary...) * @see #getSecondaryGroupCount() * @see #getSecondaryGroupTitleAt(int) */ public List getSecondaryGroupEntries( int groupIndex) { return Collections.unmodifiableList(this.groupEntries.get(groupIndex)); } /** * Sets the rollover callback that allows the application to place custom * content in the secondary panel of the {@link RibbonApplicationMenu} when * this primary menu entry is activated. * * @param rolloverCallback * The new rollover callback for populating the secondary panel * of the {@link RibbonApplicationMenu}. * @see #getRolloverCallback() */ public void setRolloverCallback(PrimaryRolloverCallback rolloverCallback) { this.rolloverCallback = rolloverCallback; } /** * Returns the current application callback that allows placing custom * content in the secondary panel of the {@link RibbonApplicationMenu} when * this primary menu entry is activated. * * @return The current rollover callback for populating the secondary panel * of the {@link RibbonApplicationMenu}. * @see #setRolloverCallback(PrimaryRolloverCallback) */ public PrimaryRolloverCallback getRolloverCallback() { return rolloverCallback; } /** * Changes the title of the specified group. * * @param groupIndex * Group index. * @param newTitle * New title for the specified group. */ public synchronized void setSecondaryGroupTitle(int groupIndex, String newTitle) { this.groupTitles.set(groupIndex, newTitle); } } src/org/pushingpixels/flamingo/api/ribbon/RibbonApplicationMenuEntryFooter.java0000644000175000017500000000572611401230444027215 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.ActionListener; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; /** * Metadata description for the footer entries of the * {@link RibbonApplicationMenu}. The footer entries at runtime are represented * by {@link CommandButtonKind#ACTION_ONLY} command buttons placed in a * right-aligned row along the bottom edge of the ribbon application menu. * * @author Kirill Grouchnikov */ public class RibbonApplicationMenuEntryFooter extends RibbonApplicationMenuEntry { /** * Creates the metadata description of a {@link RibbonApplicationMenu} * footer menu entry. * * @param icon * The icon of this footer menu entry. Must be non- * null. * @param text * The text of this footer menu entry. Must be non- * null. * @param mainActionListener * The main action listener for this footer menu entry. While * this can be null, clicking on the matching button * will have no effect. */ public RibbonApplicationMenuEntryFooter(ResizableIcon icon, String text, ActionListener mainActionListener) { super(icon, text, mainActionListener, CommandButtonKind.ACTION_ONLY); } } src/org/pushingpixels/flamingo/api/ribbon/RibbonTask.java0000644000175000017500000001545111401230444022622 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.util.*; import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizeSequencingPolicies; import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizeSequencingPolicy; /** * Single ribbon task in {@link JRibbon}. This is a logical entity that groups * {@link AbstractRibbonBand} components. * * @author Kirill Grouchnikov */ public class RibbonTask { /** * The associated ribbon. */ private JRibbon ribbon; /** * List of all bands. */ private ArrayList> bands; /** * The title of this task. */ private String title; /** * The group that this band belongs to. For regular ribbon bands this field * is null. */ private RibbonContextualTaskGroup contextualGroup; /** * The current resize sequencing policy. */ private RibbonBandResizeSequencingPolicy resizeSequencingPolicy; /** * The key tip for the task button of this task. */ private String keyTip; /** * Creates a ribbon task that contains the specified bands. * * @param title * Ribbon task title. * @param bands * Bands to add to the ribbon task. */ public RibbonTask(String title, AbstractRibbonBand... bands) { if ((bands == null) || (bands.length == 0)) { throw new IllegalArgumentException("Cannot have empty ribbon task"); } this.title = title; this.bands = new ArrayList>(); for (AbstractRibbonBand band : bands) { band.setRibbonTask(this); this.bands.add(band); } this.resizeSequencingPolicy = new CoreRibbonResizeSequencingPolicies.RoundRobin( this); } /** * Returns the number of bands in this task. * * @return Number of bands in this task. * @see #getBand(int) * @see #getBands() */ public int getBandCount() { return this.bands.size(); } /** * Returns band at the specified index from this task. * * @param index * Band index. * @return Band at the specified index. * @see #getBandCount() * @see #getBands() */ public AbstractRibbonBand getBand(int index) { return this.bands.get(index); } /** * Returns the title of this task. * * @return The title of this task. */ public String getTitle() { return this.title; } /** * Sets the contextual task group for this ribbon task. This method is * package protected and is for internal use only. * * @param contextualGroup * The contextual task group for this ribbon task. * @see #getContextualGroup() */ void setContextualGroup(RibbonContextualTaskGroup contextualGroup) { if (this.contextualGroup != null) { throw new IllegalStateException( "The task already belongs to another contextual task group"); } this.contextualGroup = contextualGroup; } /** * Returns the contextual task group for this ribbon task. Will return * null for general ribbon tasks. * * @return The contextual task group for this ribbon task. */ public RibbonContextualTaskGroup getContextualGroup() { return this.contextualGroup; } /** * Returns an unmodifiable view on the ribbon bands of this task. * * @return Unmodifiable view on the ribbon bands of this task. * @see #getBandCount() * @see #getBand(int) */ public List> getBands() { return Collections.unmodifiableList(this.bands); } /** * Changes the title of this ribbon task. * * @param title * The new title for this ribbon task. */ public void setTitle(String title) { this.title = title; if (this.ribbon != null) this.ribbon.fireStateChanged(); } /** * Associates this ribbon task with the specified ribbon. This method is * package protected and is for internal use only. * * @param ribbon * The associated ribbon. */ void setRibbon(JRibbon ribbon) { if (this.ribbon != null) { throw new IllegalStateException( "The task already belongs to another ribbon"); } this.ribbon = ribbon; } /** * Returns the current resize sequencing policy of this ribbon task. * * @return The current resize sequencing policy of this ribbon task. * @see #setResizeSequencingPolicy(RibbonBandResizeSequencingPolicy) */ public RibbonBandResizeSequencingPolicy getResizeSequencingPolicy() { return this.resizeSequencingPolicy; } /** * Sets the specified parameter as the new resize sequencing policy of this * ribbon task. * * @param resizeSequencingPolicy * The new resize sequencing policy of this ribbon task. * @see #getResizeSequencingPolicy() */ public void setResizeSequencingPolicy( RibbonBandResizeSequencingPolicy resizeSequencingPolicy) { this.resizeSequencingPolicy = resizeSequencingPolicy; } /** * Returns the key tip for the task button of this task. * * @return The key tip for the task button of this task. * @see #setKeyTip(String) */ public String getKeyTip() { return this.keyTip; } /** * Sets the specified parameter to be the new key tip for the task button of * this task. * * @param keyTip * The new key tip for the task button of this task. */ public void setKeyTip(String keyTip) { this.keyTip = keyTip; } } src/org/pushingpixels/flamingo/api/ribbon/JRibbonBand.java0000644000175000017500000003633211413206054022702 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.ActionListener; import java.util.*; import javax.swing.SwingConstants; import org.pushingpixels.flamingo.api.common.*; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.popup.JCommandPopupMenu; import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies; import org.pushingpixels.flamingo.internal.ui.ribbon.*; /** * Ribbon band component. Can host three types of content: * *
    *
  • Command buttons added with * {@link #addCommandButton(AbstractCommandButton, RibbonElementPriority)}.
  • *
  • Wrapped core / 3rd party components added with * {@link #addRibbonComponent(JRibbonComponent)} or * {@link #addRibbonComponent(JRibbonComponent, int)}.
  • *
  • Ribbon galleries added with * {@link #addRibbonGallery(String, List, Map, int, int, RibbonElementPriority)} * .
  • *
* *

* Command buttons are added with associated {@link RibbonElementPriority}. The * higher the priority, the longer the button "stays" in the * {@link CommandButtonDisplayState#BIG} or * {@link CommandButtonDisplayState#MEDIUM} state - depending on the available * resize policies. *

* *

* Wrapped components can span one or multiple rows. Use the * {@link #addRibbonComponent(JRibbonComponent, int)} API to add a wrapped * component that spans more than one row. *

* *

* Once a ribbon gallery is added with * {@link #addRibbonGallery(String, List, Map, int, int, RibbonElementPriority)} * , you can use the following APIs to configure the content and behavior of * that gallery: *

* *
    *
  • {@link #addRibbonGalleryButtons(String, String, JCommandToggleButton...)} *
  • *
  • {@link #removeRibbonGalleryButtons(String, JCommandToggleButton...)}
  • *
  • {@link #setSelectedRibbonGalleryButton(String, JCommandToggleButton)}
  • *
  • {@link #setRibbonGalleryExpandKeyTip(String, String)}
  • *
  • * {@link #setRibbonGalleryPopupCallback(String, RibbonGalleryPopupCallback)}
  • *
* *

* A ribbon band can have multiple visual groups separated with vertical * separator lines. To start a new unnamed group use the {@link #startGroup()} * API. To start a new named group use the {@link #startGroup(String)} API. * Unnamed groups will have three rows of controls. Named groups will have two * rows of controls, with the top row showing the group title. *

* * @author Kirill Grouchnikov */ public class JRibbonBand extends AbstractRibbonBand { /** * This callback allows application code to place additional menu entries in * the popup menu shown when the ribbon gallery expand button is clicked. * Application code should use * {@link JCommandPopupMenu#addMenuButton(JCommandMenuButton)} and * {@link JCommandPopupMenu#addMenuSeparator()} APIs on the passed menu * parameter. * * @author Kirill Grouchnikov */ public static interface RibbonGalleryPopupCallback { /** * Called just before the popup menu is about to be shown. * * @param menu * The popup menu that will be shown. */ public void popupToBeShown(JCommandPopupMenu menu); } /** * Big size with landscape orientation. Used for buttons in in-ribbon * galleries. */ public static final CommandButtonDisplayState BIG_FIXED_LANDSCAPE = new CommandButtonDisplayState( "Big Fixed Landscape", 32) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton button) { return new CommandButtonLayoutManagerBigFixedLandscape(); } }; /** * Big size with landscape orientation. Used for buttons in in-ribbon * galleries. */ public static final CommandButtonDisplayState BIG_FIXED = new CommandButtonDisplayState( "Big Fixed", 32) { @Override public CommandButtonLayoutManager createLayoutManager( AbstractCommandButton button) { return new CommandButtonLayoutManagerBigFixed(); } }; /** * Creates a new ribbon band. * * @param title * Band title. * @param icon * Associated icon (for collapsed state). */ public JRibbonBand(String title, ResizableIcon icon) { this(title, icon, null); } /** * Creates a new ribbon band. * * @param title * Band title. * @param icon * Associated icon (for collapsed state). * @param expandActionListener * Expand action listener (can be null). */ public JRibbonBand(String title, ResizableIcon icon, ActionListener expandActionListener) { super(title, icon, expandActionListener, new JBandControlPanel()); this.resizePolicies = Collections .unmodifiableList(CoreRibbonResizePolicies .getCorePoliciesPermissive(this)); updateUI(); } /** * Adds the specified command button to this band. * * @param commandButton * Command button to add. * @param priority * Priority of the button. */ public void addCommandButton(AbstractCommandButton commandButton, RibbonElementPriority priority) { commandButton.setHorizontalAlignment(SwingConstants.LEFT); this.controlPanel.addCommandButton(commandButton, priority); } public void addRibbonGallery(String galleryName, List>> buttons, Map preferredVisibleButtonCounts, int preferredPopupMaxButtonColumns, int preferredPopupMaxVisibleButtonRows, RibbonElementPriority priority) { this.addRibbonGallery(galleryName, buttons, preferredVisibleButtonCounts, preferredPopupMaxButtonColumns, preferredPopupMaxVisibleButtonRows, JRibbonBand.BIG_FIXED_LANDSCAPE, priority); } /** * Adds a new ribbon gallery to this band. * * @param galleryName * Gallery name. * @param buttons * Button groups. * @param preferredVisibleButtonCounts * Preferred count of visible buttons of the ribbon gallery under * different states. * @param preferredPopupMaxButtonColumns * Preferred maximum columns in the popup gallery associated with * the ribbon gallery. * @param preferredPopupMaxVisibleButtonRows * Preferred maximum visible rows in the popup gallery associated * with the ribbon gallery. * @param priority * The initial ribbon gallery priority. * @see #addRibbonGalleryButtons(String, String, JCommandToggleButton...) * @see #removeRibbonGalleryButtons(String, JCommandToggleButton...) * @see #setSelectedRibbonGalleryButton(String, JCommandToggleButton) */ public void addRibbonGallery(String galleryName, List>> buttons, Map preferredVisibleButtonCounts, int preferredPopupMaxButtonColumns, int preferredPopupMaxVisibleButtonRows, CommandButtonDisplayState ribbonButtonDisplayState, RibbonElementPriority priority) { JRibbonGallery gallery = new JRibbonGallery(); gallery.setButtonDisplayState(ribbonButtonDisplayState); gallery.setName(galleryName); for (Map.Entry prefCountEntry : preferredVisibleButtonCounts .entrySet()) { gallery.setPreferredVisibleButtonCount(prefCountEntry.getKey(), prefCountEntry.getValue()); } gallery.setGroupMapping(buttons); gallery.setPreferredPopupPanelDimension(preferredPopupMaxButtonColumns, preferredPopupMaxVisibleButtonRows); this.controlPanel.addRibbonGallery(gallery, priority); } /** * Adds the specified command toggle buttons to a button group in the * specified ribbon gallery. * * @param galleryName * Ribbon gallery name. * @param buttonGroupName * Button group name. * @param buttons * Buttons to add. * @see #addRibbonGallery(String, List, Map, int, int, * RibbonElementPriority) * @see #removeRibbonGalleryButtons(String, JCommandToggleButton...) * @see #setSelectedRibbonGalleryButton(String, JCommandToggleButton) */ public void addRibbonGalleryButtons(String galleryName, String buttonGroupName, JCommandToggleButton... buttons) { JRibbonGallery gallery = this.controlPanel .getRibbonGallery(galleryName); if (gallery == null) return; gallery.addRibbonGalleryButtons(buttonGroupName, buttons); } /** * Removes command toggle buttons from the specified ribbon gallery. * * @param galleryName * Ribbon gallery name. * @param buttons * Buttons to remove. * @see #addRibbonGallery(String, List, Map, int, int, * RibbonElementPriority) * @see #addRibbonGalleryButtons(String, String, JCommandToggleButton...) * @see #setSelectedRibbonGalleryButton(String, JCommandToggleButton) */ public void removeRibbonGalleryButtons(String galleryName, JCommandToggleButton... buttons) { JRibbonGallery gallery = this.controlPanel .getRibbonGallery(galleryName); if (gallery == null) return; gallery.removeRibbonGalleryButtons(buttons); } /** * Selects the specified command toggle button in the specified ribbon * gallery. * * @param galleryName * Ribbon gallery name. * @param buttonToSelect * Button to select. * @see #addRibbonGallery(String, List, Map, int, int, * RibbonElementPriority) * @see #addRibbonGalleryButtons(String, String, JCommandToggleButton...) * @see #removeRibbonGalleryButtons(String, JCommandToggleButton...) */ public void setSelectedRibbonGalleryButton(String galleryName, JCommandToggleButton buttonToSelect) { JRibbonGallery gallery = this.controlPanel .getRibbonGallery(galleryName); if (gallery == null) return; gallery.setSelectedButton(buttonToSelect); } /** * Sets the display state for the buttons of the specified ribbon gallery. * * @param galleryName * Ribbon gallery name. * @param displayState * Display state for the buttons of the matching ribbon gallery. */ public void setRibbonGalleryButtonDisplayState(String galleryName, CommandButtonDisplayState displayState) { JRibbonGallery gallery = this.controlPanel .getRibbonGallery(galleryName); if (gallery == null) return; gallery.setButtonDisplayState(displayState); } /** * Sets the application callback to place additional entries in the popup * menu shown when the specified ribbon gallery is expanded. * * @param galleryName * Gallery name. * @param popupCallback * Application callback. * @see RibbonGalleryPopupCallback */ public void setRibbonGalleryPopupCallback(String galleryName, RibbonGalleryPopupCallback popupCallback) { JRibbonGallery gallery = this.controlPanel .getRibbonGallery(galleryName); if (gallery == null) return; gallery.setPopupCallback(popupCallback); } /** * Sets the key tip on the expand button of the specified ribbon gallery. * * @param galleryName * Gallery name. * @param expandKeyTip * The key tip on the expand button of the specified ribbon * gallery. */ public void setRibbonGalleryExpandKeyTip(String galleryName, String expandKeyTip) { JRibbonGallery gallery = this.controlPanel .getRibbonGallery(galleryName); if (gallery == null) return; gallery.setExpandKeyTip(expandKeyTip); } /** * Adds the specified ribbon component to this ribbon band. * * @param comp * The ribbon component to add. */ public void addRibbonComponent(JRibbonComponent comp) { this.controlPanel.addRibbonComponent(comp); } /** * Adds the specified ribbon component to this ribbon band. * * @param comp * The ribbon component to add. * @param rowSpan * Row span of the ribbon component. * @throws IllegalArgumentException * if the row span is not legal. Legal row span is 1..3 for * unnamed groups and 1..2 for named groups. * @see #startGroup() * @see #startGroup(String) */ public void addRibbonComponent(JRibbonComponent comp, int rowSpan) { int groupCount = this.controlPanel.getControlPanelGroupCount(); String groupTitle = (groupCount > 0) ? this.controlPanel .getControlPanelGroupTitle(groupCount - 1) : null; int availableRows = (groupTitle == null) ? 3 : 2; if ((rowSpan <= 0) || (rowSpan > availableRows)) { throw new IllegalArgumentException( "Row span value not supported. Should be in 1.." + availableRows + " range"); } this.controlPanel.addRibbonComponent(comp, rowSpan); } /** * Starts a new unnamed group. * * @return The index of the new group. */ public int startGroup() { return this.controlPanel.startGroup(); } /** * Starts a new named group. * * @param groupTitle * The group title. * @return The index of the new group. */ public int startGroup(String groupTitle) { return this.controlPanel.startGroup(groupTitle); } /** * Changes the title of the specified group. * * @param groupIndex * Group index. * @param groupTitle * The new title for this group. */ public void setGroupTitle(int groupIndex, String groupTitle) { this.controlPanel.setGroupTitle(groupIndex, groupTitle); } public List getRibbonComponents(int groupIndex) { return this.controlPanel.getRibbonComponents(groupIndex); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.ribbon.AbstractRibbonBand#cloneBand() */ @Override public AbstractRibbonBand cloneBand() { AbstractRibbonBand result = new JRibbonBand(this .getTitle(), this.getIcon(), this.getExpandActionListener()); result.applyComponentOrientation(this.getComponentOrientation()); return result; } } src/org/pushingpixels/flamingo/api/ribbon/AbstractRibbonBand.java0000644000175000017500000004207611407771362024272 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.ActionListener; import java.util.Collections; import java.util.List; import javax.swing.JComponent; import javax.swing.UIManager; import org.pushingpixels.flamingo.api.common.RichTooltip; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.ribbon.resize.*; import org.pushingpixels.flamingo.internal.ui.ribbon.*; import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities; /** * Ribbon band. Is part of a logical {@link RibbonTask}. This is an abstract * base class for two types of ribbon bands - flow in {@link JFlowRibbonBand} * and general in {@link JRibbonBand}. * *

* This class provides the following common functionality: *

*
    *
  • Tracking the available and current resize policies.
  • *
  • Tracking the collapsed state of the ribbon band - when there is not * enough horizontal space to show this panel under the smallest resize setting * (see {@link RibbonBandResizePolicy} and {@link CoreRibbonResizePolicies}) - * the band content is replaced by one collapsed button. When that button is * activated, the original ribbon band content is shown in a popup panel.
  • *
  • Associating key tip and rich tooltip with the expand button of the ribbon * band.
  • *
  • Associating key tip with the collapsed button of the ribbon band.
  • *
* * @author Kirill Grouchnikov * @param * Class parameter that specifies the type of band control panel * implementation. */ public abstract class AbstractRibbonBand extends JComponent { /** * The UI class ID string. */ public static final String uiClassID = "RibbonBandUI"; /** * The ribbon task of this ribbon band. */ RibbonTask ribbonTask; /** * Band title. * * @see #getTitle() * @see #setTitle(String) */ private String title; /** * Optional expand action listener. If present, the title pane * shows button with plus sign. The action listener on the button will be * this listener. * * @see #getExpandActionListener() * @see #AbstractRibbonBand(String, ResizableIcon, ActionListener, * AbstractBandControlPanel) */ private ActionListener expandActionListener; /** * Band control panel. When there is not enough horizontal space to show * this panel under the smallest resize setting, the control panel is hidden * and a collapsed button is shown. When this collapsed button is activated, * it shows the {@link #popupRibbonBand} in a popup panel. The collapsed * button itself is implemented as a part of the UI delegate in * {@link BasicRibbonBandUI}. * * @see #popupRibbonBand * @see #icon */ protected T controlPanel; /** * Ribbon band shown in a popup panel when this ribbon band is in a * collapsed state. * * @see #controlPanel * @see #getPopupRibbonBand() * @see #setPopupRibbonBand(AbstractRibbonBand) */ private AbstractRibbonBand popupRibbonBand; /** * Icon for the collapsed state. Is set on the button that represents the * collapsed state of this band. The collapsed button itself is implemented * as a part of the UI delegate in {@link BasicRibbonBandUI}. * * @see #getIcon() */ private ResizableIcon icon; /** * The current resize policy for this band. Must be one of the policies in * the {@link #resizePolicies} list. * * @see #resizePolicies * @see #setCurrentResizePolicy(RibbonBandResizePolicy) * @see #getCurrentResizePolicy() */ private RibbonBandResizePolicy currResizePolicy; /** * The list of available resize policies. * * @see #currResizePolicy * @see #setResizePolicies(List) * @see #getResizePolicies() * @see #getCurrentResizePolicy() */ protected List resizePolicies; /** * The key tip for the ribbon band expand button. Is relevant only when * {@link #expandActionListener} is not null. * * @see #setExpandButtonKeyTip(String) * @see #getExpandButtonKeyTip() */ private String expandButtonKeyTip; /** * The rich tooltip for the ribbon band expand button. Is relevant only when * {@link #expandActionListener} is not null. * * @see #setExpandButtonRichTooltip(RichTooltip) * @see #getExpandButtonRichTooltip() */ private RichTooltip expandButtonRichTooltip; /** * The key tip for the collapsed button which is shown when there is not * enough horizontal space to show the ribbon band content under the most * restrictive resize policy. The collapsed button itself is implemented as * a part of the UI delegate in {@link BasicRibbonBandUI}. * * @see #setCollapsedStateKeyTip(String) * @see #getCollapsedStateKeyTip() */ private String collapsedStateKeyTip; /** * Creates a new ribbon band. * * @param title * Band title. * @param icon * Associated icon (for collapsed state). * @param expandActionListener * Expand action listener (can be null). * @param controlPanel * The control panel of this ribbon band. */ public AbstractRibbonBand(String title, ResizableIcon icon, ActionListener expandActionListener, T controlPanel) { super(); this.title = title; this.icon = icon; this.expandActionListener = expandActionListener; this.controlPanel = controlPanel; this.controlPanel.setRibbonBand(this); this.add(this.controlPanel); updateUI(); } /** * Returns a clone of this ribbon band. * * @return A clone of this ribbon band. */ public abstract AbstractRibbonBand cloneBand(); /** * Returns the UI object which implements the L&F for this component. * * @return a RibbonBandUI object * @see #setUI */ public RibbonBandUI getUI() { return (RibbonBandUI) ui; } /** * Sets the new UI delegate. * * @param ui * New UI delegate. */ public void setUI(RibbonBandUI ui) { super.setUI(ui); } /* * (non-Javadoc) * * @see javax.swing.JComponent#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((RibbonBandUI) UIManager.getUI(this)); } else { setUI(new BasicRibbonBandUI()); } } /* * (non-Javadoc) * * @see javax.swing.JComponent#getUIClassID() */ @Override public String getUIClassID() { return uiClassID; } /** * Returns the title of this band. * * @return Title of this band. * @see #setTitle(String) */ public String getTitle() { return this.title; } /** * Returns the icon for the collapsed state. * * @return The icon for the collapsed state. * @see #AbstractRibbonBand(String, ResizableIcon, ActionListener, * AbstractBandControlPanel) */ public ResizableIcon getIcon() { return this.icon; } /** * Changes the title of this ribbon band. Fires a title * property change event. * * @param title * The new title for this ribbon band. * @see #AbstractRibbonBand(String, ResizableIcon, ActionListener, * AbstractBandControlPanel) * @see #getTitle() */ public void setTitle(String title) { String old = this.title; this.title = title; this.firePropertyChange("title", old, this.title); } /** * Returns the expand action listener of this ribbon band. The * result may be null. * * @return Expand action listener of this ribbon band. * @see #AbstractRibbonBand(String, ResizableIcon, ActionListener, * AbstractBandControlPanel) * @see #setExpandActionListener(ActionListener) */ public ActionListener getExpandActionListener() { return this.expandActionListener; } /** * Sets the specified action listener to be activated when the user clicks * the expand button on this ribbon band. Passing null will * remove the expand button from this ribbon band. * * @param expandActionListener * Expand action listener for this ribbon band. * @see #getExpandActionListener() */ public void setExpandActionListener(ActionListener expandActionListener) { ActionListener old = this.expandActionListener; this.expandActionListener = expandActionListener; this.firePropertyChange("expandActionListener", old, this.expandActionListener); } /** * Returns the control panel of this ribbon band. The result * may be null. * * @return Control panel of this ribbon band. * @see #AbstractRibbonBand(String, ResizableIcon, ActionListener, * AbstractBandControlPanel) * @see #setControlPanel(AbstractBandControlPanel) */ public T getControlPanel() { return this.controlPanel; } /** * Sets the control panel of this ribbon band. The parameter * may be null. This method is for internal use only. * * @param controlPanel * The new control panel for this ribbon band. May * be null. * @see #AbstractRibbonBand(String, ResizableIcon, ActionListener, * AbstractBandControlPanel) * @see #getControlPanel() */ public void setControlPanel(T controlPanel) { if (controlPanel == null) { this.remove(this.controlPanel); } else { this.add(controlPanel); controlPanel.applyComponentOrientation(this .getComponentOrientation()); } this.controlPanel = controlPanel; } /** * Returns the ribbon band shown in a popup panel when this ribbon band is * in a collapsed state. This method is for internal use only and should not * be called by the application code. * * @return The ribbon band shown in a popup panel when this ribbon band is * in a collapsed state. * @see #setPopupRibbonBand(AbstractRibbonBand) */ public AbstractRibbonBand getPopupRibbonBand() { return this.popupRibbonBand; } /** * Sets the specified parameter to be the ribbon band shown in a popup panel * when this ribbon band is in a collapsed state. This method is for * internal use only and should not be called by the application code. * * @param popupRibbonBand * The ribbon band to be shown in a popup panel when this ribbon * band is in a collapsed state. */ public void setPopupRibbonBand(AbstractRibbonBand popupRibbonBand) { this.popupRibbonBand = popupRibbonBand; if (this.popupRibbonBand != null) { popupRibbonBand.applyComponentOrientation(this .getComponentOrientation()); } } /** * Returns the current resize policy of this ribbon band. * * @return The current resize policy of this ribbon band. */ public RibbonBandResizePolicy getCurrentResizePolicy() { return currResizePolicy; } /** * Sets the specified parameter to be the current resize policy of this * ribbon band. This method is for internal use only and should not be * called by the application code. * * @param resizePolicy * The new resize policy for this ribbon band. * @see #getCurrentResizePolicy() * @see #getResizePolicies() */ public void setCurrentResizePolicy(RibbonBandResizePolicy resizePolicy) { this.currResizePolicy = resizePolicy; } /** * Returns an unmodifiable list of available resize policies of this ribbon * band. * * @return An unmodifiable list of available resize policies of this ribbon * band. */ public List getResizePolicies() { return Collections.unmodifiableList(this.resizePolicies); } /** * Sets the specified parameter as the available resize policies of this * ribbon band. The order of the resize policies in this list is important. * The first entry in the list must be the most permissive policies that * returns the largest value from its * {@link RibbonBandResizePolicy#getPreferredWidth(int, int)}. Each * successive entry in the list must return the value smaller than its * predecessors. If {@link IconRibbonBandResizePolicy} is in the list, it * must be the last entry. * * @param resizePolicies * The new available resize policies of this ribbon band. */ public void setResizePolicies(List resizePolicies) { this.resizePolicies = Collections.unmodifiableList(resizePolicies); if (this.ribbonTask != null) { FlamingoUtilities.checkResizePoliciesConsistency(this); } } /** * Returns the key tip for the expand button of this ribbon band. * * @return The key tip for the expand button of this ribbon band. * @see #setExpandButtonKeyTip(String) */ public String getExpandButtonKeyTip() { return this.expandButtonKeyTip; } /** * Changes the key tip for the expand button of this ribbon band. Fires an * expandButtonKeyTip property change event. * * @param expandButtonKeyTip * The new key tip for the expand button of this ribbon band. * @see #getExpandButtonKeyTip() */ public void setExpandButtonKeyTip(String expandButtonKeyTip) { String old = this.expandButtonKeyTip; this.expandButtonKeyTip = expandButtonKeyTip; this.firePropertyChange("expandButtonKeyTip", old, this.expandButtonKeyTip); } /** * Returns the rich tooltip for the expand button of this ribbon band. * * @return The rich tooltip for the expand button of this ribbon band. * @see #setExpandButtonRichTooltip(RichTooltip) */ public RichTooltip getExpandButtonRichTooltip() { return this.expandButtonRichTooltip; } /** * Changes the rich tooltip for the expand button of this ribbon band. Fires * an expandButtonRichTooltip property change event. * * @param expandButtonRichTooltip * The new rich tooltip for the expand button of this ribbon * band. * @see #getExpandButtonRichTooltip() */ public void setExpandButtonRichTooltip(RichTooltip expandButtonRichTooltip) { RichTooltip old = this.expandButtonRichTooltip; this.expandButtonRichTooltip = expandButtonRichTooltip; this.firePropertyChange("expandButtonRichTooltip", old, this.expandButtonRichTooltip); } /** * Returns the key tip for the collapsed button which is shown when there is * not enough horizontal space to show the ribbon band content under the * most restrictive resize policy. * * @return The key tip for the collapsed button of this ribbon band. * @see #setCollapsedStateKeyTip(String) */ public String getCollapsedStateKeyTip() { return this.collapsedStateKeyTip; } /** * Changes the key tip for the collapsed button which is shown when there is * not enough horizontal space to show the ribbon band content under the * most restrictive resize policy. Fires a collapsedStateKeyTip * property change event. * * @param collapsedStateKeyTip * The new key tip for the collapsed button of this ribbon band. * @see #getCollapsedStateKeyTip() */ public void setCollapsedStateKeyTip(String collapsedStateKeyTip) { String old = this.collapsedStateKeyTip; this.collapsedStateKeyTip = collapsedStateKeyTip; this.firePropertyChange("collapsedStateKeyTip", old, this.collapsedStateKeyTip); } /** * Associates this ribbon band with the specified ribbon task. * * @param ribbonTask * Ribbon task. * @throws IllegalArgumentException * When this ribbon band has already been associated with a * ribbon task. */ void setRibbonTask(RibbonTask ribbonTask) { if (this.ribbonTask != null) { throw new IllegalArgumentException( "Ribbon band cannot be added to more than one ribbon task"); } this.ribbonTask = ribbonTask; FlamingoUtilities.checkResizePoliciesConsistency(this); } } src/org/pushingpixels/flamingo/api/ribbon/RibbonElementPriority.java0000644000175000017500000000357711401230444025061 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; /** * Priority of ribbon band components. * * @author Kirill Grouchnikov */ public enum RibbonElementPriority { /** * Top priority. */ TOP, /** * Medium priority. */ MEDIUM, /** * Low priority. */ LOW; }src/org/pushingpixels/flamingo/api/ribbon/RibbonApplicationMenuEntrySecondary.java0000644000175000017500000001117711401230444027703 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.ActionListener; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; import org.pushingpixels.flamingo.api.common.popup.PopupPanelCallback; /** * Metadata description for the secondary menu entries of the * {@link RibbonApplicationMenu}. The secondary menu entries at runtime are * represented by command menu buttons placed in the right panel of the * application menu. * * @author Kirill Grouchnikov */ public class RibbonApplicationMenuEntrySecondary extends RibbonApplicationMenuEntry { /** * Extra description text for this secondary menu entry. * * @see #getDescriptionText() * @see #setDescriptionText(String) */ protected String descriptionText; /** * Popup callback for this menu entry. Must be not null if the * menu entry kind has popup part. * * @see #getPopupCallback() * @see #setPopupCallback(PopupPanelCallback) */ protected PopupPanelCallback popupCallback; /** * Creates the metadata description of a {@link RibbonApplicationMenu} * secondary menu entry. * * @param icon * The icon of this menu entry. Must be non-null. * @param text * The text of this menu entry. Must be non-null. * @param mainActionListener * The main action listener for this menu entry. If the entry * kind is {@link CommandButtonKind#POPUP_ONLY}, this listener * will be ignored. * @param entryKind * The kind of the command button that will represent this menu * entry. Must be non- null. */ public RibbonApplicationMenuEntrySecondary(ResizableIcon icon, String text, ActionListener mainActionListener, CommandButtonKind entryKind) { super(icon, text, mainActionListener, entryKind); } /** * Returns the description text of this secondary menu entry. * * @return The description text of this secondary menu entry. * @see #setDescriptionText(String) */ public String getDescriptionText() { return this.descriptionText; } /** * Sets the new description text for this secondary menu entry. * * @param descriptionText * The new description text for this secondary menu entry. * @see #getDescriptionText() */ public void setDescriptionText(String descriptionText) { this.descriptionText = descriptionText; } /** * Sets the popup callback for this secondary menu entry. * * @param popupCallback * The popup callback for this secondary menu entry. * @see #getPopupCallback() */ public void setPopupCallback(PopupPanelCallback popupCallback) { this.popupCallback = popupCallback; } /** * Returns the current popup callback of this secondary menu entry. * * @return The current popup callback of this secondary menu entry. * @see #setPopupCallback(PopupPanelCallback) */ public PopupPanelCallback getPopupCallback() { return popupCallback; } } src/org/pushingpixels/flamingo/api/ribbon/RibbonContextualTaskGroup.java0000644000175000017500000001275211401230444025707 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.Color; import java.util.ArrayList; /** * A contextual group of {@link RibbonTask}s. The contextual ribbon task groups * allow showing and hiding ribbon tasks based on the current selection in the * application. For example, Word only shows the table tasks when a table is * selected in the document. By default, tasks belonging to the groups added by * {@link JRibbon#addContextualTaskGroup(RibbonContextualTaskGroup)} are not * visible. To show the tasks belonging to the specific group, call * {@link JRibbon#setVisible(RibbonContextualTaskGroup, boolean)} API. Note that * you can have multiple task groups visible at the same time. This class is a * logical entity that groups ribbon tasks belonging to the same contextual * group. * * @author Kirill Grouchnikov */ public class RibbonContextualTaskGroup { /** * The ribbon that contains this task group. */ private JRibbon ribbon; /** * List of all tasks. * * @see #RibbonContextualTaskGroup(String, Color, RibbonTask...) * @see #getTaskCount() * @see #getTask(int) */ private ArrayList tasks; /** * Group title. * * @see #RibbonContextualTaskGroup(String, Color, RibbonTask...) * @see #getTitle() * @see #setTitle(String) */ private String title; /** * Hue color for this group. * * @see #RibbonContextualTaskGroup(String, Color, RibbonTask...) * @see #getHueColor() */ private Color hueColor; /** * Alpha factor for colorizing the toggle tab buttons of tasks in contextual * groups. */ public static final double HUE_ALPHA = 0.25; /** * Creates a task contextual group that contains the specified tasks. * * @param title * Group title. * @param hueColor * Hue color for this group. Should be a saturated non-dark color * for good visuals. * @param tasks * Tasks to add to the group. */ public RibbonContextualTaskGroup(String title, Color hueColor, RibbonTask... tasks) { this.title = title; this.hueColor = hueColor; this.tasks = new ArrayList(); for (RibbonTask ribbonTask : tasks) { ribbonTask.setContextualGroup(this); this.tasks.add(ribbonTask); } } /** * Returns the number of tasks in this group. * * @return Number of tasks in this group. * @see #getTask(int) */ public int getTaskCount() { return this.tasks.size(); } /** * Returns task at the specified index from this group. * * @param index * Task index. * @return Task at the specified index. * @see #getTaskCount() */ public RibbonTask getTask(int index) { return this.tasks.get(index); } /** * Returns the name of this group. * * @return The name of this group. * @see #setTitle(String) */ public String getTitle() { return this.title; } /** * Returns the hue color for this group. * * @return The hue color for this group. */ public Color getHueColor() { return this.hueColor; } /** * Changes the title of this ribbon contextual task group. * * @param title * The new title for this ribbon contextual task group. * @see #getTitle() */ public void setTitle(String title) { this.title = title; if (this.ribbon != null) this.ribbon.fireStateChanged(); } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return getTitle() + " (" + getTaskCount() + " tasks)"; } /** * Associates this ribbon contextual task group with the specified ribbon. * This method is package protected and is for internal use only. * * @param ribbon * The associated ribbon. */ void setRibbon(JRibbon ribbon) { if (this.ribbon != null) { throw new IllegalStateException( "The contextual task group already belongs to another ribbon"); } this.ribbon = ribbon; } } src/org/pushingpixels/flamingo/api/ribbon/RibbonApplicationMenuEntry.java0000644000175000017500000002036511422070756026045 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.ribbon; import java.awt.event.ActionListener; import org.pushingpixels.flamingo.api.common.JCommandMenuButton; import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; /** * Basic metadata for entries in the ribbon application menu. * *

* At runtime, the application menu entries are implemented as * {@link JCommandMenuButton}, but the application code does not operate on that * level. Instead, the application code creates metadata-driven description of * the ribbon application menu, and that description is used to create and * populate the "real" controls of the application menu popup. *

* * @author Kirill Grouchnikov * @see RibbonApplicationMenu * @see JRibbon#setApplicationMenu(RibbonApplicationMenu) */ abstract class RibbonApplicationMenuEntry { /** * The menu icon. */ protected ResizableIcon icon; /** * The menu icon for disabled state. Optional, can be null. */ protected ResizableIcon disabledIcon; /** * The menu text. */ protected String text; /** * The main action listener for this menu entry. */ protected ActionListener mainActionListener; /** * The kind of the command button that represents this menu entry. */ protected CommandButtonKind entryKind; /** * Enabled state of this menu. */ protected boolean isEnabled; /** * Optional key tip for the action area of the command button that * represents this menu entry. */ protected String actionKeyTip; /** * Optional key tip for the popup area of the command button that represents * this menu entry. */ protected String popupKeyTip; /** * Creates the basic metadata description of a {@link RibbonApplicationMenu} * menu entry. * * @param icon * The icon of this menu entry. Must be non-null. * @param text * The text of this menu entry. Must be non-null. * @param mainActionListener * The main action listener for this menu entry. If the entry * kind is {@link CommandButtonKind#POPUP_ONLY}, this listener * will be ignored. * @param entryKind * The kind of the command button that will represent this menu * entry. Must be non- null. */ public RibbonApplicationMenuEntry(ResizableIcon icon, String text, ActionListener mainActionListener, CommandButtonKind entryKind) { super(); this.icon = icon; this.text = text; this.mainActionListener = mainActionListener; this.entryKind = entryKind; this.isEnabled = true; } /** * Returns the icon of this application menu entry. * * @return The icon of this application menu entry. */ public ResizableIcon getIcon() { return this.icon; } /** * Returns the text of this application menu entry. * * @return The text of this application menu entry. * @see #setText(String) */ public String getText() { return this.text; } /** * Sets the new text for this application menu entry. * * @param text * The new text for this application menu entry. * @see #getText() */ public void setText(String text) { this.text = text; } /** * Returns the main action listener associated with this application menu * entry. * * @return The main action listener associated with this application menu * entry. */ public ActionListener getMainActionListener() { return this.mainActionListener; } /** * Returns the kind of the command button that represents this menu entry. * * @return The kind of the command button that represents this menu entry. */ public CommandButtonKind getEntryKind() { return this.entryKind; } /** * Sets the enabled state of the command button that represents this menu * entry. * * @param isEnabled * If true, the command button that represents this * menu entry will be enabled, if false, the command * button will be disabled. * @see #isEnabled */ public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } /** * Returns the enabled state of the command button that represents this menu * entry. * * @return true if the command button that represents this menu * entry is enabled, false otherwise. */ public boolean isEnabled() { return this.isEnabled; } /** * Returns the key tip for the action area of the command button that * represents this menu entry. * * @return The key tip for the action area of the command button that * represents this menu entry. * @see #setActionKeyTip(String) * @see #getPopupKeyTip() */ public String getActionKeyTip() { return this.actionKeyTip; } /** * Sets the new value for the key tip for the action area of the command * button that represents this menu entry. * * @param actionKeyTip * The new value for the key tip for the action area of the * command button that represents this menu entry. * @see #getActionKeyTip() * @see #setPopupKeyTip(String) */ public void setActionKeyTip(String actionKeyTip) { this.actionKeyTip = actionKeyTip; } /** * Returns the key tip for the popup area of the command button that * represents this menu entry. * * @return The key tip for the popup area of the command button that * represents this menu entry. * @see #setPopupKeyTip(String) * @see #getActionKeyTip() */ public String getPopupKeyTip() { return this.popupKeyTip; } /** * Sets the new value for the key tip for the popup area of the command * button that represents this menu entry. * * @param popupKeyTip * The new value for the key tip for the popup area of the * command button that represents this menu entry. * @see #getPopupKeyTip() * @see #setActionKeyTip(String) */ public void setPopupKeyTip(String popupKeyTip) { this.popupKeyTip = popupKeyTip; } /** * Returns the disabled icon for the command button that represents this * menu entry. * * @return The disabled icon for the command button that represents this * menu entry. * @see #setDisabledIcon(ResizableIcon) */ public ResizableIcon getDisabledIcon() { return this.disabledIcon; } /** * Sets the disabled icon for the command button that represents this menu * entry. * * @param disabledIcon * The disabled icon for the command button that represents this * menu entry. * @see #getDisabledIcon() */ public void setDisabledIcon(ResizableIcon disabledIcon) { this.disabledIcon = disabledIcon; } } src/org/pushingpixels/flamingo/api/bcb/0000755000175000017500000000000011401230442017164 5ustar tonytonysrc/org/pushingpixels/flamingo/api/bcb/BreadcrumbBarExceptionHandler.java0000644000175000017500000000410711401230442025701 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb; /** * Generic exception handler for the breadcrumb bar. * * @author Kirill Grouchnikov */ public interface BreadcrumbBarExceptionHandler { /** * Called when an exceptional condition occurs in the breadcrumb bar. * * @param t * Throwable. */ public void onException(Throwable t); } src/org/pushingpixels/flamingo/api/bcb/core/0000755000175000017500000000000011401230442020114 5ustar tonytonysrc/org/pushingpixels/flamingo/api/bcb/core/BreadcrumbSvnSelector.java0000644000175000017500000002465711401230442025233 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb.core; import java.io.*; import java.util.*; import org.pushingpixels.flamingo.api.bcb.*; import org.pushingpixels.flamingo.api.common.StringValuePair; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory; import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.io.SVNRepositoryFactory; import org.tmatesoft.svn.core.wc.SVNWCUtil; /** * Breadcrumb bar that allows browsing a single local / remote SVN repository. * The implementation uses SVNKit library. * Make sure to read the licensing terms before * using this component in your application. * * @author Kirill Grouchnikov */ public class BreadcrumbSvnSelector extends JBreadcrumbBar { /** * SVN-specific implementation of the {@link BreadcrumbBarCallBack}. * * @author Kirill Grouchnikov */ public static class PathCallback extends BreadcrumbBarCallBack { /** * Repository URL. */ private String url; /** * User name. */ private String userName; /** * Password. */ private String password; /** * The actual SVN repository. */ private SVNRepository repository; /** * Creates a new callback. * * @param url * Repository URL. * @param userName * User name. * @param password * Password. */ public PathCallback(String url, String userName, String password) { this.url = url; this.userName = userName; this.password = password; } /* * (non-Javadoc) * * @see org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#setup() */ @Override public void setup() throws BreadcrumbBarException { DAVRepositoryFactory.setup(); SVNRepositoryFactoryImpl.setup(); FSRepositoryFactory.setup(); try { this.repository = SVNRepositoryFactory.create(SVNURL .parseURIEncoded(this.url)); ISVNAuthenticationManager authManager = SVNWCUtil .createDefaultAuthenticationManager(this.userName, this.password); this.repository.setAuthenticationManager(authManager); } catch (SVNException svne) { if (this.throwsExceptions) { throw new BreadcrumbBarException(svne); } } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getPathChoices(java. * util.List) */ @Override public List> getPathChoices( List> path) throws BreadcrumbBarException { String lastPath = (path == null) ? "" : path.get(path.size() - 1) .getData(); try { return BreadcrumbSvnSelector.getPathChoices(this.repository, lastPath); } catch (SVNException svne) { if (this.throwsExceptions) { throw new BreadcrumbBarException(svne); } return null; } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getLeafs(java.util.List) */ @Override public List> getLeafs( List> path) throws BreadcrumbBarException { String lastPath = (path == null) ? "" : path.get(path.size() - 1) .getData(); try { return BreadcrumbSvnSelector .getLeafs(this.repository, lastPath); } catch (SVNException svne) { svne.printStackTrace(); return null; } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getLeafContent(java. * lang.Object) */ @Override public InputStream getLeafContent(String leaf) throws BreadcrumbBarException { try { return BreadcrumbSvnSelector.getLeafContent(this.repository, leaf); } catch (SVNException svne) { return null; } } } /** * Creates a new empty breadcrumb bar that is not connected to any SVN * repository. */ public BreadcrumbSvnSelector() { super(null); } /** * Creates a new breadcrumb bar to browse the specified SVN repository. The * actual connection is done off EDT so that the application remains * responsive. * * @param url * Repository URL. * @param userName * User name. * @param password * Password. */ public BreadcrumbSvnSelector(String url, String userName, String password) { super(new PathCallback(url, userName, password)); } /** * Sets the connection parameters. The breadcrumb bar path is reset to the * root of thew specified SVN repository. * * @param url * Repository URL. * @param userName * User name. * @param password * Password. */ public void setConnectionParams(String url, String userName, String password) { this.setConnectionParams(url, userName, password, false); } /** * Sets the connection parameters. The breadcrumb bar path is reset to the * root of thew specified SVN repository. * * @param url * Repository URL. * @param userName * User name. * @param password * Password. * @param throwsException * Indicates whether the inner exceptions should be reported to * the registered exception handlers. */ public void setConnectionParams(String url, String userName, String password, boolean throwsException) { this.callback = new PathCallback(url, userName, password); this.callback.setup(); this.callback.setThrowsExceptions(throwsException); this.setPath(new LinkedList>()); } /** * Returns the contents of the specified repository file. * * @param repository * SVN repository. * @param leaf * Full path to the file. * @return Contents of the specified repository file. * @throws SVNException */ @SuppressWarnings("unchecked") protected static InputStream getLeafContent(SVNRepository repository, String leaf) throws SVNException { Map fileProperties = new HashMap(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); repository.getFile(leaf, -1, SVNProperties.wrap(fileProperties), baos); return new ByteArrayInputStream(baos.toByteArray()); } /** * Returns a list of all the files in the specified folder. * * @param repository * SVN repository. * @param path * Full path to the folder. * @return List of all the files in the specified folder. * @throws SVNException */ @SuppressWarnings("unchecked") protected static List> getLeafs( SVNRepository repository, String path) throws SVNException { List> result = new ArrayList>(); Collection entries = repository.getDir(path, -1, null, (Collection) null); Iterator iterator = entries.iterator(); while (iterator.hasNext()) { SVNDirEntry entry = (SVNDirEntry) iterator.next(); if (entry.getKind() == SVNNodeKind.FILE) { String prefix = ((path == null) || (path.length() == 0)) ? "" : path + "/"; StringValuePair leafInfo = new StringValuePair( entry.getName(), prefix + entry.getName()); leafInfo.set("size", entry.getSize()); leafInfo.set("author", entry.getAuthor()); leafInfo.set("date", entry.getDate()); leafInfo.set("revision", entry.getRevision()); result.add(leafInfo); } } Collections.sort(result, new Comparator>() { public int compare(StringValuePair o1, StringValuePair o2) { return o1.getKey().compareTo(o2.getKey()); } }); return result; } /** * Returns a list of all the foldes in the specified folder. * * @param repository * SVN repository. * @param path * Full path to the folder. * @return List of all the folders in the specified folder. * @throws SVNException */ @SuppressWarnings("unchecked") protected static List> getPathChoices( SVNRepository repository, String path) throws SVNException { List> result = new ArrayList>(); Collection entries = repository.getDir(path, -1, null, (Collection) null); Iterator iterator = entries.iterator(); while (iterator.hasNext()) { SVNDirEntry entry = (SVNDirEntry) iterator.next(); if (entry.getKind() == SVNNodeKind.DIR) { String prefix = ((path == null) || (path.length() == 0)) ? "" : path + "/"; result.add(new StringValuePair(entry.getName(), prefix + entry.getName())); } } Collections.sort(result, new Comparator>() { public int compare(StringValuePair o1, StringValuePair o2) { return o1.getKey().compareTo(o2.getKey()); } }); return result; } } src/org/pushingpixels/flamingo/api/bcb/core/BreadcrumbTreeAdapterSelector.java0000644000175000017500000002304411401230442026652 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb.core; import java.awt.Component; import java.io.InputStream; import java.util.LinkedList; import java.util.List; import javax.swing.*; import javax.swing.tree.TreeModel; import org.pushingpixels.flamingo.api.bcb.*; import org.pushingpixels.flamingo.api.common.StringValuePair; /** * Breadcrumb bar that allows wrapping an existing {@link JTree} or a * {@link TreeModel}. * *
    *
  • Use * {@link BreadcrumbTreeAdapterSelector#BreadcrumbTreeAdapterSelector(JTree)} to * wrap an existing tree that has a {@link JLabel} based renderer.
  • *
  • Use a * {@link BreadcrumbTreeAdapterSelector#BreadcrumbTreeAdapterSelector(JTree, TreeAdapter)} * to wrap an existing tree and provide a custom breadcrumb bar path renderer.
  • *
  • Use * {@link BreadcrumbTreeAdapterSelector#BreadcrumbTreeAdapterSelector(TreeModel, TreeAdapter, boolean)} * to wrap an existing tree model.
  • *
* * @author Kirill Grouchnikov */ public class BreadcrumbTreeAdapterSelector extends JBreadcrumbBar { /** * Tree adapter that allows plugging a custom rendering logic. * * @author Kirill Grouchnikov */ public static abstract class TreeAdapter { /** * Returns the caption for the specified tree node. Note that the * extending class must override this method in an * EDT-safe fashion. * * @param node * Tree node. * @return The caption for the specified tree node. */ public abstract String toString(final Object node); /** * Returns the icon for the specified tree node. * * @param node * Tree node. * @return The icon for the specified tree node. */ public Icon getIcon(Object node) { return null; } } /** * Tree-adapter specific implementation of the {@link BreadcrumbBarCallBack} * . * * @author Kirill Grouchnikov */ public static class TreeCallback extends BreadcrumbBarCallBack { /** * The corresponding tree model. */ protected TreeModel treeModel; /** * The corresponding tree adapter. Can not be null. */ protected TreeAdapter treeAdapter; /** * If true, the first selector shows the tree root node. If * false, the first selector shows the tree root child * nodes. */ protected boolean isRootVisible; /** * Creates the callback. * * @param treeModel * The corresponding tree model. * @param treeAdapter * The corresponding tree adapter. Can not be * null. * @param isRootVisible * If true, the first selector shows the tree * root node. If false, the first selector shows * the tree root child nodes. */ public TreeCallback(TreeModel treeModel, TreeAdapter treeAdapter, boolean isRootVisible) { this.treeModel = treeModel; this.treeAdapter = treeAdapter; this.isRootVisible = isRootVisible; } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getPathChoices(java. * util.List) */ @Override public List> getPathChoices( List> path) { if (path == null) { Object root = this.treeModel.getRoot(); List> bRoots = new LinkedList>(); if (isRootVisible) { StringValuePair rootPair = new StringValuePair( this.treeAdapter.toString(root), root); rootPair.set("icon", this.treeAdapter.getIcon(root)); bRoots.add(rootPair); } else { for (int i = 0; i < this.treeModel.getChildCount(root); i++) { Object rootChild = this.treeModel.getChild(root, i); StringValuePair rootPair = new StringValuePair( this.treeAdapter.toString(rootChild), rootChild); rootPair.set("icon", this.treeAdapter .getIcon(rootChild)); bRoots.add(rootPair); } } return bRoots; } if (path.size() == 0) return null; Object lastInPath = path.get(path.size() - 1).getData(); if (this.treeModel.isLeaf(lastInPath)) return null; LinkedList> lResult = new LinkedList>(); for (int i = 0; i < this.treeModel.getChildCount(lastInPath); i++) { Object child = this.treeModel.getChild(lastInPath, i); if (this.treeModel.isLeaf(child)) continue; StringValuePair pair = new StringValuePair( this.treeAdapter.toString(child), child); pair.set("icon", this.treeAdapter.getIcon(child)); lResult.add(pair); } return lResult; } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getLeafs(java.util.List) */ @Override public List> getLeafs( List> path) { Object lastInPath = path.get(path.size() - 1).getData(); if (this.treeModel.isLeaf(lastInPath)) return null; LinkedList> lResult = new LinkedList>(); for (int i = 0; i < this.treeModel.getChildCount(lastInPath); i++) { Object child = this.treeModel.getChild(lastInPath, i); if (!this.treeModel.isLeaf(child)) continue; StringValuePair pair = new StringValuePair( this.treeAdapter.toString(child), child); pair.set("icon", this.treeAdapter.getIcon(child)); lResult.add(pair); } return lResult; } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getLeafContent(java. * lang.Object) */ @Override public InputStream getLeafContent(Object leaf) { return null; } } /** * Creates an adapter for the specified tree model. * * @param treeModel * Tree model. * @param treeAdapter * Tree adapter. Can not be null. * @param isRootVisible * If true, the first selector shows the tree root * node. If false, the first selector shows the tree * root child nodes. */ public BreadcrumbTreeAdapterSelector(TreeModel treeModel, TreeAdapter treeAdapter, boolean isRootVisible) { super(new TreeCallback(treeModel, treeAdapter, isRootVisible)); // SwingWorker>, Void> worker = new // SwingWorker>, Void>() { // @Override // protected List> doInBackground() // throws Exception { // return callback.getPathChoices(null); // } // // @Override // protected void done() { // try { // pushChoices(new BreadcrumbItemChoices(get())); // } catch (Exception exc) { // } // } // }; // worker.execute(); } /** * Creates an adapter for the specified tree. * * @param tree * Tree. * @param treeAdapter * Tree adapter. Can not be null. */ public BreadcrumbTreeAdapterSelector(JTree tree, TreeAdapter treeAdapter) { this(tree.getModel(), treeAdapter, tree.isRootVisible()); } /** * Creates an adapter for the specified tree. Assumes that the tree renderer * extends a {@link JLabel}. Otherwise, the path selectors will have no * captions and no icons. * * @param tree * Tree. */ public BreadcrumbTreeAdapterSelector(final JTree tree) { this(tree, new TreeAdapter() { private JLabel getRenderer(Object node) { Component renderer = tree.getCellRenderer() .getTreeCellRendererComponent(tree, node, false, false, tree.getModel().isLeaf(node), 0, false); if (renderer instanceof JLabel) return (JLabel) renderer; return null; } @Override public String toString(Object node) { JLabel label = getRenderer(node); if (label != null) return label.getText(); return null; } @Override public Icon getIcon(Object node) { JLabel label = getRenderer(node); if (label != null) return label.getIcon(); return null; } }); } } src/org/pushingpixels/flamingo/api/bcb/core/BreadcrumbFileSelector.java0000644000175000017500000002325511401230442025335 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb.core; import java.io.*; import java.util.*; import javax.swing.filechooser.FileSystemView; import org.pushingpixels.flamingo.api.bcb.*; import org.pushingpixels.flamingo.api.common.StringValuePair; /** * Breadcrumb bar that allows browsing the local file system. * * @author Kirill Grouchnikov * @author Brian Young */ public class BreadcrumbFileSelector extends JBreadcrumbBar { /** * If true, the path selectors will use native icons. */ protected boolean useNativeIcons; /** * Local file system specific implementation of the * {@link BreadcrumbBarCallBack}. * * @author Kirill Grouchnikov */ public static class DirCallback extends BreadcrumbBarCallBack { /** * File system view. */ protected FileSystemView fsv; /** * If true, the path selectors will use native icons. */ protected boolean useNativeIcons; /** * Creates a new callback. * * @param useNativeIcons * If true, the path selectors will use native * icons. */ public DirCallback(boolean useNativeIcons) { this(FileSystemView.getFileSystemView(), useNativeIcons); } /** * Creates a new callback. * * @param fileSystemView * File system view to use. * @param useNativeIcons * If true, the path selectors will use native * icons. */ public DirCallback(FileSystemView fileSystemView, boolean useNativeIcons) { this.fsv = fileSystemView; this.useNativeIcons = useNativeIcons; } /* * (non-Javadoc) * * @see org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#setup() */ @Override public void setup() { } @Override public List> getPathChoices( List> path) { synchronized (fsv) { if (path == null) { LinkedList> bRoots = new LinkedList>(); for (File root : fsv.getRoots()) { if (fsv.isHiddenFile(root)) continue; String systemName = fsv.getSystemDisplayName(root); if (systemName.length() == 0) systemName = root.getAbsolutePath(); StringValuePair rootPair = new StringValuePair( systemName, root); if (useNativeIcons) rootPair.set("icon", fsv.getSystemIcon(root)); bRoots.add(rootPair); } return bRoots; } if (path.size() == 0) return null; File lastInPath = path.get(path.size() - 1).getData(); if (!lastInPath.exists()) return new ArrayList>(); if (!lastInPath.isDirectory()) return null; LinkedList> lResult = new LinkedList>(); for (File child : lastInPath.listFiles()) { // ignore regular files and hidden directories if (!child.isDirectory()) continue; if (fsv.isHiddenFile(child)) continue; String childFileName = fsv.getSystemDisplayName(child); if ((childFileName == null) || childFileName.isEmpty()) childFileName = child.getName(); StringValuePair pair = new StringValuePair( childFileName, child); if (useNativeIcons) pair.set("icon", fsv.getSystemIcon(child)); lResult.add(pair); } Collections.sort(lResult, new Comparator>() { @Override public int compare(StringValuePair o1, StringValuePair o2) { String key1 = fsv.isFileSystemRoot(o1 .getValue()) ? o1.getValue() .getAbsolutePath() : o1.getKey(); String key2 = fsv.isFileSystemRoot(o2 .getValue()) ? o2.getValue() .getAbsolutePath() : o2.getKey(); return key1.toLowerCase().compareTo( key2.toLowerCase()); } @Override public boolean equals(Object obj) { return super.equals(obj); } }); return lResult; } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getLeafs(org.jvnet.flamingo * .bcb.BreadcrumbItem[]) */ @Override public List> getLeafs( List> path) { synchronized (fsv) { if ((path == null) || (path.size() == 0)) return null; File lastInPath = path.get(path.size() - 1).getData(); if (!lastInPath.exists()) return new ArrayList>(); if (!lastInPath.isDirectory()) return null; LinkedList> lResult = new LinkedList>(); for (File child : lastInPath.listFiles()) { // ignore directories and hidden directories if (child.isDirectory()) continue; if (fsv.isHiddenFile(child)) continue; String childFileName = fsv.getSystemDisplayName(child); if ((childFileName == null) || childFileName.isEmpty()) childFileName = child.getName(); StringValuePair pair = new StringValuePair( childFileName, child); if (useNativeIcons) pair.set("icon", fsv.getSystemIcon(child)); lResult.add(pair); } Collections.sort(lResult, new Comparator>() { @Override public int compare(StringValuePair o1, StringValuePair o2) { return o1.getKey().toLowerCase().compareTo( o2.getKey().toLowerCase()); } @Override public boolean equals(Object obj) { return super.equals(obj); } }); return lResult; } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getLeafContent(java. * lang.Object) */ @Override public InputStream getLeafContent(File leaf) { try { return new FileInputStream(leaf); } catch (FileNotFoundException fnfe) { return null; } } } /** * Creates a new breadcrumb bar file selector that uses native icons and the * default file system view. */ public BreadcrumbFileSelector() { this(true); } /** * Creates a new breadcrumb bar file selector that uses the default file * system view. * * @param useNativeIcons * If true, the path selectors will use native * icons. */ public BreadcrumbFileSelector(boolean useNativeIcons) { this(FileSystemView.getFileSystemView(), useNativeIcons); } /** * Creates a new breadcrumb bar file selector. * * @param fileSystemView * File system view. * @param useNativeIcons * If true, the path selectors will use native * icons. */ public BreadcrumbFileSelector(FileSystemView fileSystemView, boolean useNativeIcons) { super(new DirCallback(fileSystemView, useNativeIcons)); this.useNativeIcons = useNativeIcons; } /** * Sets indication whether the path selectors should use native icons. * * @param useNativeIcons * If true, the path selectors will use native * icons. */ public void setUseNativeIcons(boolean useNativeIcons) { this.useNativeIcons = useNativeIcons; } /** * Sets the selected path based of the specified file. If this file is * either null or not a directory, the home directory is * selected. * * @param dir * Points to a directory to be selected. */ public void setPath(File dir) { // System.out.println(dir.getAbsolutePath()); FileSystemView fsv = FileSystemView.getFileSystemView(); synchronized (fsv) { if ((dir == null) || !dir.isDirectory()) { dir = fsv.getHomeDirectory(); } ArrayList> path = new ArrayList>(); File parent = dir; BreadcrumbItem bci = new BreadcrumbItem(fsv .getSystemDisplayName(dir), dir); bci.setIcon(fsv.getSystemIcon(dir)); path.add(bci); while (true) { parent = fsv.getParentDirectory(parent); if (parent == null) break; bci = new BreadcrumbItem( fsv.getSystemDisplayName(parent), parent); bci.setIcon(fsv.getSystemIcon(parent)); path.add(bci); } Collections.reverse(path); this.setPath(path); } } } src/org/pushingpixels/flamingo/api/bcb/core/BreadcrumbMultiSvnSelector.java0000644000175000017500000002620411401230442026234 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb.core; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.pushingpixels.flamingo.api.bcb.*; import org.pushingpixels.flamingo.api.common.StringValuePair; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory; import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.io.SVNRepositoryFactory; import org.tmatesoft.svn.core.wc.SVNWCUtil; /** * Breadcrumb bar that allows browsing multiple local and remote SVN * repositories. The implementation uses SVNKit library. Make sure to read the licensing terms before * using this component in your application. * * @author Kirill Grouchnikov */ public class BreadcrumbMultiSvnSelector extends JBreadcrumbBar { /** * Information on a single SVN repository. * * @author Kirill Grouchnikov */ public static class SvnRepositoryInfo { /** * Repository name. */ public String name; /** * Repository URL. */ public String url; /** * User name. */ public String user; /** * Password. */ public String password; /** * Creates a new info object. * * @param name * Repository name. * @param url * Repository URL. * @param user * User name. * @param password * Password. */ public SvnRepositoryInfo(String name, String url, String user, String password) { this.name = name; this.url = url; this.user = user; this.password = password; } } /** * SVN-specific implementation of the {@link BreadcrumbBarCallBack}. * *

* This is tricky. Unlike other implementations that connect in the setup * method, this implementation connects when the first path selection is * done. The code in * BasicBreadcrumbBar.PopupAction.actionPerformed(ActionEvent) adds two * items - the selected SVN repository and the matching choices. The first * addition requires connecting to the selected repository, which is done * off EDT (using {@link SwingWorker}). The second addition must wait until * the connection has been established since otherwise the repository is not * yet available. In order to make the second addition wait, we use a * {@link CountDownLatch}. *

* *

* It is {@link CountDownLatch#countDown()} in the * SwingWorker.done() that wraps the connection. The * {@link BreadcrumbBarCallBack#getPathChoices(BreadcrumbItem[])} and * {@link BreadcrumbBarCallBack#getLeafs(BreadcrumbItem[])} call * {@link CountDownLatch#await()} on the same latch that blocks until the * connection is done. Since both these methods should be wrapped off EDT in * a separate {@link SwingWorker}, this doesn't block the UI. *

* * @author Kirill Grouchnikov */ protected class PathCallback extends BreadcrumbBarCallBack { /** * Information on all the configured SVN repositories. */ protected List repositories; /** * The current SVN repository. */ private SVNRepository currRepository; /** * Latch to synchronize between connecting to a selected SVN repository * and fetching folder contents. */ private CountDownLatch connectionLatch; /** * Creates a new callback. * * @param repoList * List of all SVN repositories. */ public PathCallback( BreadcrumbMultiSvnSelector.SvnRepositoryInfo... repoList) { this.repositories = new ArrayList(); if (repoList != null) { for (BreadcrumbMultiSvnSelector.SvnRepositoryInfo repository : repoList) { this.addSvnRepositoryInfo(repository); } } getModel().addPathListener(new BreadcrumbPathListener() { @Override public void breadcrumbPathEvent(BreadcrumbPathEvent event) { final List> newPath = getModel() .getItems(); // If one element - an SVN repository has been // selected. Need to connect to it and update the // currRepository field. if (newPath.size() == 1) connectionLatch = new CountDownLatch(1); SwingUtilities.invokeLater(new Runnable() { public void run() { // If one element - an SVN repository has been // selected. Need to connect to it and update // the currRepository field. if (newPath.size() != 1) return; final String newSvnName = newPath.get(0).getData(); // System.out.println("Connecting to " + // newSvnName); // find the connection params for (final SvnRepositoryInfo repoInfo : repositories) { if (newSvnName.equals(repoInfo.name)) { // connect SwingWorker worker = new SwingWorker() { @Override protected SVNRepository doInBackground() throws Exception { try { SVNRepository repository = SVNRepositoryFactory .create(SVNURL .parseURIEncoded(repoInfo.url)); ISVNAuthenticationManager authManager = SVNWCUtil .createDefaultAuthenticationManager( repoInfo.user, repoInfo.password); repository .setAuthenticationManager(authManager); return repository; } catch (SVNException svne) { List handlers = getExceptionHandlers(); for (BreadcrumbBarExceptionHandler handler : handlers) { handler.onException(svne); } return null; } } @Override protected void done() { try { currRepository = get(); connectionLatch.countDown(); } catch (Exception exc) { } } }; worker.execute(); } } } }); } }); } /** * Adds information on a new SVN repository. * * @param repositoryInfo */ public void addSvnRepositoryInfo(SvnRepositoryInfo repositoryInfo) { this.repositories.add(repositoryInfo); } /* * (non-Javadoc) * * @see org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#setup() */ @Override public void setup() { DAVRepositoryFactory.setup(); SVNRepositoryFactoryImpl.setup(); FSRepositoryFactory.setup(); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getPathChoices(java. * util.List) */ @Override public List> getPathChoices( List> path) throws BreadcrumbBarException { try { if (connectionLatch != null) connectionLatch.await(); } catch (InterruptedException ie) { } // System.out.println("Getting choices for " + path); if (path == null) { // root - list all the repositories. List> result = new ArrayList>(); for (SvnRepositoryInfo repoInfo : repositories) { StringValuePair toAdd = new StringValuePair( repoInfo.name, repoInfo.name); result.add(toAdd); } return result; } String lastPath = (path.size() == 1) ? "" : path.get( path.size() - 1).getData(); // System.out.println("Last path is " + lastPath + ", repo " // + this.currRepository); try { return BreadcrumbSvnSelector.getPathChoices( this.currRepository, lastPath); } catch (SVNException svne) { if (this.throwsExceptions) { throw new BreadcrumbBarException(svne); } return null; } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getLeafs(java.util.List) */ @Override public List> getLeafs( List> path) throws BreadcrumbBarException { if (path == null) { // root - no leafs, only repositories. return null; } try { if (connectionLatch != null) connectionLatch.await(); } catch (InterruptedException ie) { } String lastPath = (path.size() == 1) ? "" : path.get( path.size() - 1).getData(); try { return BreadcrumbSvnSelector.getLeafs(this.currRepository, lastPath); } catch (SVNException svne) { if (this.throwsExceptions) { throw new BreadcrumbBarException(svne); } return null; } } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.bcb.BreadcrumbBarCallBack#getLeafContent(java. * lang.Object) */ @Override public InputStream getLeafContent(String leaf) throws BreadcrumbBarException { try { return BreadcrumbSvnSelector.getLeafContent( this.currRepository, leaf); } catch (SVNException svne) { if (this.throwsExceptions) { throw new BreadcrumbBarException(svne); } return null; } } } /** * Creates a new breadcrumb bar with the specified SVN repositories. * * @param repositories * List of all SVN repositories. */ public BreadcrumbMultiSvnSelector( BreadcrumbMultiSvnSelector.SvnRepositoryInfo... repositories) { super(null); this.callback = new PathCallback(repositories); this.callback.setup(); this.getModel().reset(); } } src/org/pushingpixels/flamingo/api/bcb/BreadcrumbPathListener.java0000644000175000017500000000413211401230442024420 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb; import java.util.EventListener; /** * The application listener on breadcrumb path events. */ public interface BreadcrumbPathListener extends EventListener { /** * Called on breadcrumb bar events. * * @param event * Breadcrumb bar event. */ public void breadcrumbPathEvent(BreadcrumbPathEvent event); } src/org/pushingpixels/flamingo/api/bcb/JBreadcrumbBar.java0000644000175000017500000001440511401230442022640 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb; import java.util.*; import javax.swing.*; import org.pushingpixels.flamingo.internal.ui.bcb.BasicBreadcrumbBarUI; import org.pushingpixels.flamingo.internal.ui.bcb.BreadcrumbBarUI; /** * Breadcrumb bar. It is basically a way of lazily navigating around a tree, but * just by manipulating the sections of a path. * * @param * Type of data associated with each breadcrumb bar item. * @author Kirill Grouchnikov */ public class JBreadcrumbBar extends JComponent { /** * Serial version ID. */ private static final long serialVersionUID = 3258407339731400502L; /** * The breadcrumb bar model. */ protected BreadcrumbBarModel model; /** * Application callback. Used to retrieve choices for the activated * selector. */ protected BreadcrumbBarCallBack callback; /** * List of registered exception handlers. */ protected List exceptionHandlers; /** * The UI class ID string. */ public static final String uiClassID = "BreadcrumbBarUI"; /** * Base interface for elements in breadcrumb bar. */ public interface BreadcrumbBarElement { /** * Returns the text presentation of this element. * * @return The text presentation of this element. */ public String getText(); /** * Returns the index of this element. * * @return The index of this element. */ public int getIndex(); } /** * Creates a new breadcrumb bar. * * @param callback * The application callback. */ public JBreadcrumbBar(final BreadcrumbBarCallBack callback) { super(); this.model = new BreadcrumbBarModel(); this.callback = callback; if (this.callback != null) this.callback.setup(); this.exceptionHandlers = new ArrayList(); this.updateUI(); } /** * Sets new path as the current path in this breadcrumb bar. * * @param newPath * New path for this breadcrumb bar. */ public void setPath(List> newPath) { this.getModel().replace(newPath); } /** * Returns the application callback. * * @return The application callback. */ public BreadcrumbBarCallBack getCallback() { return this.callback; } /** * Sets the new UI delegate. * * @param ui * New UI delegate. */ public void setUI(BreadcrumbBarUI ui) { super.setUI(ui); } /* * (non-Javadoc) * * @see javax.swing.JComponent#updateUI() */ @Override public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((BreadcrumbBarUI) UIManager.getUI(this)); } else { setUI(new BasicBreadcrumbBarUI()); } } /** * Returns the UI object which implements the L&F for this component. * * @return a BreadcrumbBarUI object * @see #setUI */ public BreadcrumbBarUI getUI() { return (BreadcrumbBarUI) ui; } /** * Returns the name of the UI class that implements the L&F for this * component. * * @return the string "BreadcrumbBarUI" * @see JComponent#getUIClassID * @see UIDefaults#getUI */ @Override public String getUIClassID() { return uiClassID; } /** * Registers the specified exception handler. * * @param handler * Exception handler. */ public void addExceptionHandler(BreadcrumbBarExceptionHandler handler) { this.exceptionHandlers.add(handler); } /** * Unregisters the specified exception handler. * * @param handler * Exception handler. */ public void removeExceptionHandler(BreadcrumbBarExceptionHandler handler) { this.exceptionHandlers.remove(handler); } /** * Returns the list of currently registered exception handlers. * * @return List of currently registered exception handlers. */ public List getExceptionHandlers() { return Collections.unmodifiableList(this.exceptionHandlers); } /** * Sets the indication whether the operations of this breadcrumb bar will * throw {@link BreadcrumbBarException}. * * @param throwsExceptions * If true, the operations of this breadcrumb bar * will throw {@link BreadcrumbBarException}. */ public void setThrowsExceptions(boolean throwsExceptions) { if (this.callback != null) { this.callback.setThrowsExceptions(throwsExceptions); } } /** * Returns the model of this breadcrumb bar. * * @return The model of this breadcrumb bar. */ public BreadcrumbBarModel getModel() { return this.model; } }src/org/pushingpixels/flamingo/api/bcb/BreadcrumbBarCallBack.java0000644000175000017500000001166711401230442024112 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb; import java.io.InputStream; import java.util.List; import org.pushingpixels.flamingo.api.common.StringValuePair; /** * The application callback that can be set on {@link JBreadcrumbBar}. * * @param * Type of data associated with each breadcrumb bar item. */ public abstract class BreadcrumbBarCallBack { /** * Sets up the callback. * * @throws BreadcrumbBarException * Runtime exception that wraps the cause. Is thrown only when * {@link #setThrowsExceptions(boolean)} has been called with * true parameter. */ public void setup() throws BreadcrumbBarException { } /** * If true, some of the operations will throw * {@link BreadcrumbBarException}. */ protected boolean throwsExceptions; /** * Sets the indication whether the operations of this breadcrumb bar will * throw {@link BreadcrumbBarException}. * * @param throwsExceptions * If true, the operations of this breadcrumb bar * will throw {@link BreadcrumbBarException}. */ public void setThrowsExceptions(boolean throwsExceptions) { this.throwsExceptions = throwsExceptions; } /** * Returns the choice element that corresponds to the specified path. If the * path is empty, null should be returned. If path is * null, the "root" elements should be returned * * @param path * Breadcrumb bar path. * @return The choice element that corresponds to the specified path * @throws BreadcrumbBarException * Runtime exception that wraps the cause. Is thrown only when * {@link #setThrowsExceptions(boolean)} has been called with * true parameter. */ public List> getPathChoices(List> path) throws BreadcrumbBarException { return null; } /** * Returns the choice element that corresponds to the specified path. If the * path is empty, null should be returned. If path is * null, the "root" elements should be returned * * @param path * Breadcrumb bar path. * @return The choice element that corresponds to the specified path * @throws BreadcrumbBarException * Runtime exception that wraps the cause. Is thrown only when * {@link #setThrowsExceptions(boolean)} has been called with * true parameter. */ public List> getLeafs(List> path) throws BreadcrumbBarException { return null; } /** * Returns the input stream with the leaf content. Some implementations may * return null if this is not applicable. * * @param leaf * Leaf. * @return Input stream with the leaf content. May be null if * this is not applicable. * @throws BreadcrumbBarException * Runtime exception that wraps the cause. Is thrown only when * {@link #setThrowsExceptions(boolean)} has been called with * true parameter. */ public InputStream getLeafContent(T leaf) throws BreadcrumbBarException { return null; } } src/org/pushingpixels/flamingo/api/bcb/BreadcrumbBarModel.java0000644000175000017500000001600011401230442023500 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb; import java.util.*; import javax.swing.event.EventListenerList; /** * Model for the breadcrumb bar component ({@link JBreadcrumbBar}). * * @param * Type of data associated with each breadcrumb bar item. * @author Kirill Grouchnikov */ public class BreadcrumbBarModel { /** * The list of breadcrumb items. */ private LinkedList> items; /** * Listener list. */ protected EventListenerList listenerList; /** * Indication whether the model is in cumulative mode. * * @see #setCumulative(boolean) */ protected boolean isCumulative; /** * Smallest index of path change since the last call to * {@link #setCumulative(boolean)} with true. */ protected int smallestCumulativeIndex; /** * Creates a new empty model. */ public BreadcrumbBarModel() { this.items = new LinkedList>(); this.listenerList = new EventListenerList(); this.isCumulative = false; this.smallestCumulativeIndex = -1; } /** * Returns the index of the specified item. * * @param item * Item. * @return Index of the item if it is in the model or -1 if it is not. */ public int indexOf(BreadcrumbItem item) { return this.items.indexOf(item); } /** * Removes the last item in this model. */ public void removeLast() { this.items.removeLast(); this.firePathChanged(this.items.size()); } /** * Resets this model, removing all the items. */ public void reset() { this.items.clear(); this.firePathChanged(0); } /** * Returns an unmodifiable list of the items in this model. * * @return Unmodifiable list of the items in this model. */ public List> getItems() { return Collections.unmodifiableList(this.items); } /** * Returns the number of items in this model. * * @return Number of items in this model. */ public int getItemCount() { return this.items.size(); } /** * Returns the model item at the specified index. * * @param index * Item index. * @return The model item at the specified index. Will return * null if the index is negative or larger than the * number of items. */ public BreadcrumbItem getItem(int index) { if (index < 0) return null; if (index >= this.getItemCount()) return null; return this.items.get(index); } /** * Replaces the current item list with the specified list. * * @param items * New contents of the model. */ public void replace(List> items) { this.items.clear(); for (int i = 0; i < items.size(); i++) { this.items.addLast(items.get(i)); } this.firePathChanged(0); } /** * Adds the specified item at the end of the path. * * @param item * Item to add. */ public void addLast(BreadcrumbItem item) { this.items.addLast(item); this.firePathChanged(this.items.size() - 1); } /** * Starts or ends the cumulative mode. In cumulative mode calls to * {@link #addLast(BreadcrumbItem)}, {@link #removeLast()}, * {@link #replace(List)} and {@link #reset()} will not fire events on the * listeners registered with * {@link #addPathListener(BreadcrumbPathListener)}. * * @param isCumulative * If true, the model enters cumulative mode. If * false, the model exist cumulative mode and fires * a path event on all registered listeners with the smallest * index of all changes that have happened since the last time * this method was called with true. */ public void setCumulative(boolean isCumulative) { boolean toFire = this.isCumulative && !isCumulative; this.isCumulative = isCumulative; if (toFire) { this.firePathChanged(Math.max(0, this.smallestCumulativeIndex)); this.smallestCumulativeIndex = -1; } } /** * Adds the specified path listener to this model. * * @param l * Path listener to add. */ public void addPathListener(BreadcrumbPathListener l) { this.listenerList.add(BreadcrumbPathListener.class, l); } /** * Removes the specified path listener from this model. * * @param l * Path listener to remove. */ public void removePathListener(BreadcrumbPathListener l) { this.listenerList.remove(BreadcrumbPathListener.class, l); } /** * Fires a {@link BreadcrumbPathEvent}. * * @param indexOfFirstChange * Index of the first item that has changed in the model. */ protected void firePathChanged(int indexOfFirstChange) { if (this.isCumulative) { if (this.smallestCumulativeIndex == -1) this.smallestCumulativeIndex = indexOfFirstChange; else this.smallestCumulativeIndex = Math.min( this.smallestCumulativeIndex, indexOfFirstChange); return; } BreadcrumbPathEvent event = new BreadcrumbPathEvent(this, indexOfFirstChange); // Guaranteed to return a non-null array Object[] listeners = this.listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == BreadcrumbPathListener.class) { ((BreadcrumbPathListener) listeners[i + 1]) .breadcrumbPathEvent(event); } } } } src/org/pushingpixels/flamingo/api/bcb/BreadcrumbBarException.java0000644000175000017500000000413711401230442024406 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb; /** * Generic runtime exception for the breadcrumb bar. * * @author Kirill Grouchnikov */ public class BreadcrumbBarException extends RuntimeException { /** * Creates the new exception instance. * * @param cause * Exception cause. */ public BreadcrumbBarException(Throwable cause) { super(cause); } } src/org/pushingpixels/flamingo/api/bcb/BreadcrumbItem.java0000644000175000017500000000710011401230442022712 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. in All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb; import javax.swing.Icon; /** * A single item in the {@link JBreadcrumbBar} model. * * @param * Type of associated data. */ public final class BreadcrumbItem { /** * Display key for this item. */ protected String key; /** * Data value for this item. */ protected T data; /** * The index of this item. */ private int index = 0; /** * The optional icon. */ private Icon icon; /** * Creates a new item. * * @param key * Item key. * @param data * Item data. */ public BreadcrumbItem(String key, T data) { this.key = key; this.data = data; } /** * Creates a new item. * * @param s * String that will be used for display purposes. */ public BreadcrumbItem(String s) { this(s, null); } public String getKey() { return key; } public T getData() { return data; } public void setKey(String key) { this.key = key; } /** * Returns the index of this item. * * @return The index of this item. */ public int getIndex() { return this.index; } /** * Sets the index of this item. * * @param index * The new index of this item. */ public void setIndex(int index) { this.index = index; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return getKey() + ":" + getData(); } /** * Returns the icon of this item. * * @return The icon of this item. */ public Icon getIcon() { return icon; } /** * Sets the new icon on this item. * * @param icon * The new icon for this item. */ public void setIcon(Icon icon) { this.icon = icon; } } src/org/pushingpixels/flamingo/api/bcb/BreadcrumbPathEvent.java0000644000175000017500000000535311401230442023722 0ustar tonytony/* * Copyright (c) 2003-2010 Flamingo Kirill Grouchnikov * and Topologi. * Contributed by Rick Jelliffe of Topologi * in January 2006. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov Topologi nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.bcb; /** * Event on the breadcrumb bar path. */ public class BreadcrumbPathEvent { /** * The object that fired this event. */ private Object src; /** * The index of the first path item that has changed. */ private int indexOfFirstChange; /** * Creates a new breadcrumb bar path event. * * @param src * Event source. * @param indexOfFirstChange * The index of the first path item that has changed. */ public BreadcrumbPathEvent(Object src, int indexOfFirstChange) { this.src = src; this.indexOfFirstChange = indexOfFirstChange; } /** * Returns the index of the first path item that has changed. * * @return The index of the first path item that has changed. */ public int getIndexOfFirstChange() { return this.indexOfFirstChange; } /** * Returns the source of this event. * * @return The source of this event. */ public Object getSource() { return this.src; } } src/org/pushingpixels/flamingo/api/svg/0000755000175000017500000000000011407771362017256 5ustar tonytonysrc/org/pushingpixels/flamingo/api/svg/SvgBatikIcon.java0000644000175000017500000002451011407771362022446 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.svg; import java.awt.*; import java.awt.geom.Dimension2D; import java.awt.image.BufferedImage; import java.io.*; import java.util.*; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.swing.Icon; import org.apache.batik.bridge.InterruptedBridgeException; import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.swing.gvt.*; import org.apache.batik.transcoder.*; import org.apache.batik.transcoder.image.ImageTranscoder; import org.apache.batik.util.EventDispatcher; import org.apache.batik.util.EventDispatcher.Dispatcher; /** * A Swing Icon that draws an SVG image. * * @author Cameron McCormack * @authot Kirill Grouchnikov */ abstract class SvgBatikIcon extends UserAgentAdapter implements Icon { /** * Contains all precomputed images. */ protected Map cachedImages = new HashMap(); /** * The width of the rendered image. */ protected int width; /** * The height of the rendered image. */ protected int height; /** * SVG byte array. */ protected byte[] svgBytes; /** * The listeners. */ protected List listeners; private static ExecutorService loadService = Executors .newFixedThreadPool(5); /** * Create a new SVG icon. * * @param inputStream * The input stream to read the SVG document from. * @param w * The width of the icon. * @param h * The height of the icon. * @throws IOException * in case any I/O operation failed. */ public SvgBatikIcon(InputStream inputStream, int w, int h) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] b = new byte[1024]; while (true) { int count = inputStream.read(b); if (count < 0) break; baos.write(b, 0, count); } this.svgBytes = baos.toByteArray(); this.width = w; this.height = h; this.listeners = Collections.synchronizedList(new LinkedList()); this.renderGVTTree(this.width, this.height); } /** * A transcoder that generates a BufferedImage. */ public static class BufferedImageTranscoder extends ImageTranscoder { /** * The BufferedImage generated from the SVG document. */ protected BufferedImage bufferedImage; /** * Creates a new ARGB image with the specified dimension. * * @param width * the image width in pixels * @param height * the image height in pixels */ @Override public BufferedImage createImage(int width, int height) { return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); } /** * Writes the specified image to the specified output. * * @param img * the image to write * @param output * the output where to store the image * @param TranscoderException * if an error occured while storing the image */ @Override public void writeImage(BufferedImage img, TranscoderOutput output) throws TranscoderException { bufferedImage = img; } /** * Returns the {@link BufferedImage} generated from the SVG document. * * @return {@link BufferedImage} generated from the SVG document. */ public BufferedImage getBufferedImage() { return bufferedImage; } /** * Set the dimensions to be used for the image. * * @param w * Width. * @param h * Height. */ public void setDimensions(int w, int h) { hints.put(KEY_WIDTH, new Float(w)); hints.put(KEY_HEIGHT, new Float(h)); } } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ public int getIconWidth() { return width; } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ public int getIconHeight() { return height; } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ public void paintIcon(Component c, Graphics g, int x, int y) { BufferedImage image = this.cachedImages.get(this.getIconWidth() + ":" + this.getIconHeight()); if (image != null) { int dx = (this.width - image.getWidth()) / 2; int dy = (this.height - image.getHeight()) / 2; g.drawImage(image, x + dx, y + dy, null); } } // UserAgent ///////////////////////////////////////////////////////////// /** * Returns the default size of this user agent. */ @Override public Dimension2D getViewportSize() { return new Dimension(width, height); } /** * Sets the preferred size for this icon. The rendering is * scheduled automatically. * * @param dim * Preferred size. */ public synchronized void setPreferredSize(Dimension dim) { if ((this.width == dim.width) && (this.height == dim.height)) return; this.width = dim.width; this.height = dim.height; this.renderGVTTree(this.width, this.height); } /** * Returns the SVG bytes of the loaded SVG image. * * @return SVG bytes of the loaded SVG image. */ public byte[] getSvgBytes() { return this.svgBytes; } /** * Fires event. * * @param dispatcher * Event dispatcher. * @param event * Event data. */ public void fireEvent(Dispatcher dispatcher, Object event) { EventDispatcher.fireEvent(dispatcher, listeners, event, true); } /** * Renders the GVT tree. * * @param renderWidth * Requested rendering width. * @param renderHeight * Requested rendering height. * @return If true, the image is already computed and cached. */ protected synchronized boolean renderGVTTree(final int renderWidth, final int renderHeight) { String cacheKey = renderWidth + ":" + renderHeight; if (this.cachedImages.containsKey(cacheKey)) { return true; } Runnable load = new Runnable() { @Override public void run() { GVTTreeRendererEvent ev = new GVTTreeRendererEvent(this, null); try { ev = new GVTTreeRendererEvent(this, null); fireEvent(startedDispatcher, ev); BufferedImageTranscoder t = new BufferedImageTranscoder(); if (renderWidth != 0 && renderHeight != 0) { t.setDimensions(renderWidth, renderHeight); } InputStream is = new ByteArrayInputStream(svgBytes); TranscoderInput ti = new TranscoderInput(is); t.transcode(ti, null); BufferedImage bufferedImage = t.getBufferedImage(); String key = bufferedImage.getWidth() + ":" + bufferedImage.getHeight(); if (bufferedImage != null) { synchronized (SvgBatikIcon.this) { cachedImages.put(key, bufferedImage); } ev = new GVTTreeRendererEvent(this, bufferedImage); fireEvent(completedDispatcher, ev); } } catch (InterruptedBridgeException e) { // this sometimes happens with SVG Fonts since the glyphs // are not built till the rendering stage fireEvent(cancelledDispatcher, ev); } catch (ThreadDeath td) { fireEvent(failedDispatcher, ev); throw td; } catch (Throwable t) { fireEvent(failedDispatcher, ev); } } }; loadService.execute(load); return false; } /** * Adds a {@link GVTTreeRendererListener} to this {@link GVTTreeRenderer}. * * @param l * Listener to add. */ @SuppressWarnings("unchecked") public void addGVTTreeRendererListener(GVTTreeRendererListener l) { listeners.add(l); } /** * Removes a {@link GVTTreeRendererListener}ner from this * {@link GVTTreeRenderer}. * * @param l * Listener to remove. */ public void removeGVTTreeRendererListener(GVTTreeRendererListener l) { listeners.remove(l); } /** * Dispatcher for GVT tree rendering completion. */ static Dispatcher completedDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((GVTTreeRendererListener) listener) .gvtRenderingCompleted((GVTTreeRendererEvent) event); } }; /** * Dispatcher for GVT tree rendering start. */ static Dispatcher startedDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((GVTTreeRendererListener) listener) .gvtRenderingStarted((GVTTreeRendererEvent) event); } }; /** * Dispatcher for GVT tree rendering fail. */ static Dispatcher failedDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((GVTTreeRendererListener) listener) .gvtRenderingFailed((GVTTreeRendererEvent) event); } }; /** * Dispatcher for GVT tree rendering cancel. */ static Dispatcher cancelledDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((GVTTreeRendererListener) listener) .gvtRenderingCancelled((GVTTreeRendererEvent) event); } }; } src/org/pushingpixels/flamingo/api/svg/SvgBaseTranscoder.java0000644000175000017500000006250011401230442023462 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.svg; import java.awt.*; import java.awt.geom.*; import java.io.*; import java.lang.reflect.Field; import org.apache.batik.ext.awt.LinearGradientPaint; import org.apache.batik.ext.awt.MultipleGradientPaint; import org.apache.batik.ext.awt.RadialGradientPaint; import org.apache.batik.ext.awt.MultipleGradientPaint.ColorSpaceEnum; import org.apache.batik.ext.awt.MultipleGradientPaint.CycleMethodEnum; import org.apache.batik.ext.awt.geom.ExtendedGeneralPath; import org.apache.batik.gvt.*; /** * SVG to Java2D transcoder. * * @author Kirill Grouchnikov. */ abstract class SvgBaseTranscoder { /** * Listener. */ protected TranscoderListener listener; /** * Print writer that wraps the {@link TranscoderListener#getWriter()} of the * registered {@link #listener}. */ protected PrintWriter externalPrintWriter; /** * Print writer that wraps the {@link TranscoderListener#getWriter()} of the * registered {@link #listener}. */ protected PrintWriter printWriter; /** * Class name for the generated Java2D code. */ protected String javaClassName; /** * Package name for the generated Java2D code. */ protected String javaPackageName; protected boolean javaToImplementResizableIconInterface; protected final static String TOKEN_PACKAGE = "TOKEN_PACKAGE"; protected final static String TOKEN_CLASSNAME = "TOKEN_CLASSNAME"; protected final static String TOKEN_PAINTING_CODE = "TOKEN_PAINTING_CODE"; protected final static String TOKEN_ORIG_X = "TOKEN_ORIG_X"; protected final static String TOKEN_ORIG_Y = "TOKEN_ORIG_Y"; protected final static String TOKEN_ORIG_WIDTH = "TOKEN_ORIG_WIDTH"; protected final static String TOKEN_ORIG_HEIGHT = "TOKEN_ORIG_HEIGHT"; /** * Creates a new transcoder. * * @param uri * URI of the SVG image. * @param javaClassname * Classname for the generated Java2D code. */ public SvgBaseTranscoder(String javaClassname) { this.javaClassName = javaClassname; this.javaToImplementResizableIconInterface = false; } public void setJavaToImplementResizableIconInterface( boolean javaToImplementResizableIconInterface) { this.javaToImplementResizableIconInterface = javaToImplementResizableIconInterface; } public void setJavaPackageName(String javaPackageName) { this.javaPackageName = javaPackageName; } /** * Sets the listener. * * @param listener * Listener. */ public void setListener(TranscoderListener listener) { this.listener = listener; this.setPrintWriter(new PrintWriter(this.listener.getWriter())); } public void setPrintWriter(PrintWriter printWriter) { this.externalPrintWriter = printWriter; } /** * Transcodes the SVG image into Java2D code. */ public void transcode(GraphicsNode gvtRoot) { String template = this.javaToImplementResizableIconInterface ? "SvgTranscoderTemplateResizable.templ" : "SvgTranscoderTemplatePlain.templ"; // load the template InputStream templateStream = SvgBaseTranscoder.class .getResourceAsStream(template); StringBuffer templateBuffer = new StringBuffer(); BufferedReader templateReader = new BufferedReader( new InputStreamReader(templateStream)); try { while (true) { String line = templateReader.readLine(); if (line == null) break; templateBuffer.append(line + "\n"); } templateReader.close(); } catch (IOException ioe) { ioe.printStackTrace(); } String templateString = templateBuffer.toString(); if (javaPackageName != null) { templateString = templateString.replaceAll(TOKEN_PACKAGE, "package " + javaPackageName + ";"); } else { templateString = templateString.replaceAll(TOKEN_PACKAGE, ""); } templateString = templateString.replaceAll(TOKEN_CLASSNAME, javaClassName); templateString = templateString.replaceAll(TOKEN_CLASSNAME, javaClassName); templateString = templateString.replaceAll(TOKEN_CLASSNAME, javaClassName); ByteArrayOutputStream paintingCodeStream = new ByteArrayOutputStream(); this.printWriter = new PrintWriter(paintingCodeStream); transcodeGraphicsNode(gvtRoot, ""); this.printWriter.close(); String paintingCode = new String(paintingCodeStream.toByteArray()); templateString = templateString.replaceAll(TOKEN_PAINTING_CODE, paintingCode); Rectangle2D bounds = gvtRoot.getBounds(); templateString = templateString.replaceAll(TOKEN_ORIG_X, "" + (int) Math.ceil(bounds.getX())); templateString = templateString.replaceAll(TOKEN_ORIG_Y, "" + (int) Math.ceil(bounds.getY())); templateString = templateString.replaceAll(TOKEN_ORIG_WIDTH, "" + (int) Math.ceil(bounds.getWidth())); templateString = templateString.replaceAll(TOKEN_ORIG_HEIGHT, "" + (int) Math.ceil(bounds.getHeight())); this.externalPrintWriter.println(templateString); this.externalPrintWriter.close(); if (listener != null) listener.finished(); } /** * Transcodes the specified path iterator. * * @param pathIterator * Path iterator. */ private void transcodePathIterator(PathIterator pathIterator) { float[] coords = new float[6]; printWriter.println("shape = new GeneralPath();"); for (; !pathIterator.isDone(); pathIterator.next()) { int type = pathIterator.currentSegment(coords); switch (type) { case PathIterator.SEG_CUBICTO: // offset(offset + 1); // printWriter.println("CUBICTO " + coords[0] + ":" + coords[1] // + ":" + coords[2] + ":" + coords[3] + ":" // + coords[4] + ":" + coords[5]); printWriter.println("((GeneralPath)shape).curveTo(" + coords[0] + ", " + coords[1] + ", " + coords[2] + ", " + coords[3] + ", " + coords[4] + ", " + coords[5] + ");"); break; case PathIterator.SEG_QUADTO: // offset(offset + 1); // printWriter.println("QUADTO " + coords[0] + ":" + coords[1] // + ":" + coords[2] + ":" + coords[3]); printWriter.println("((GeneralPath)shape).quadTo(" + coords[0] + ", " + coords[1] + ", " + coords[2] + ", " + coords[3] + ");"); break; case PathIterator.SEG_MOVETO: // offset(offset + 1); // printWriter.println("MOVETO " + coords[0] + ":" + coords[1]); printWriter.println("((GeneralPath)shape).moveTo(" + coords[0] + ", " + coords[1] + ");"); break; case PathIterator.SEG_LINETO: // offset(offset + 1); // printWriter.println("LINETO " + coords[0] + ":" + coords[1]); printWriter.println("((GeneralPath)shape).lineTo(" + coords[0] + ", " + coords[1] + ");"); break; // through case PathIterator.SEG_CLOSE: // offset(offset + 1); // printWriter.println("CLOSE"); printWriter.println("((GeneralPath)shape).closePath();"); break; } } } /** * Transcodes the specified shape. * * @param shape * Shape. * @throws UnsupportedOperationException * if the shape is unsupported. */ private void transcodeShape(Shape shape) throws UnsupportedOperationException { if (shape instanceof ExtendedGeneralPath) { transcodePathIterator(((ExtendedGeneralPath) shape) .getPathIterator(null)); return; } if (shape instanceof GeneralPath) { transcodePathIterator(((GeneralPath) shape).getPathIterator(null)); return; } if (shape instanceof Rectangle2D) { Rectangle2D rect = (Rectangle2D) shape; printWriter.println("shape = new Rectangle2D.Double(" + rect.getX() + ", " + rect.getY() + ", " + rect.getWidth() + ", " + rect.getHeight() + ");"); return; } if (shape instanceof RoundRectangle2D) { RoundRectangle2D rRect = (RoundRectangle2D) shape; printWriter.println("shape = new RoundRectangle2D.Double(" + rRect.getX() + ", " + rRect.getY() + ", " + rRect.getWidth() + ", " + rRect.getHeight() + ", " + rRect.getArcWidth() + ", " + rRect.getArcHeight() + ");"); return; } if (shape instanceof Ellipse2D) { Ellipse2D ell = (Ellipse2D) shape; printWriter.println("shape = new Ellipse2D.Double(" + ell.getX() + ", " + ell.getY() + ", " + ell.getWidth() + ", " + ell.getHeight() + ");"); return; } if (shape instanceof Line2D.Float) { Line2D.Float l2df = (Line2D.Float) shape; printWriter.format("shape = new Line2D.Float(%ff,%ff,%ff,%ff);\n", l2df.x1, l2df.y1, l2df.x2, l2df.y2); return; } throw new UnsupportedOperationException(shape.getClass() .getCanonicalName()); } /** * Transcodes the specified linear gradient paint. * * @param paint * Linear gradient paint. * @throws IllegalArgumentException * if the fractions are not strictly increasing. */ private void transcodeLinearGradientPaint(LinearGradientPaint paint) throws IllegalArgumentException { Point2D startPoint = paint.getStartPoint(); Point2D endPoint = paint.getEndPoint(); float[] fractions = paint.getFractions(); Color[] colors = paint.getColors(); CycleMethodEnum cycleMethod = paint.getCycleMethod(); ColorSpaceEnum colorSpace = paint.getColorSpace(); AffineTransform transform = paint.getTransform(); float previousFraction = -1.0f; for (float currentFraction : fractions) { if (currentFraction < 0f || currentFraction > 1f) { throw new IllegalArgumentException("Fraction values must " + "be in the range 0 to 1: " + currentFraction); } if (currentFraction < previousFraction) { throw new IllegalArgumentException("Keyframe fractions " + "must be non-decreasing: " + currentFraction); } previousFraction = currentFraction; } StringBuffer fractionsRep = new StringBuffer(); if (fractions == null) { fractionsRep.append("null"); } else { String sep = ""; fractionsRep.append("new float[] {"); previousFraction = -1.0f; for (float currentFraction : fractions) { fractionsRep.append(sep); if (currentFraction == previousFraction) currentFraction += 0.000000001f; fractionsRep.append(currentFraction + "f"); sep = ","; previousFraction = currentFraction; } fractionsRep.append("}"); } StringBuffer colorsRep = new StringBuffer(); if (fractions == null) { colorsRep.append("null"); } else { String sep = ""; colorsRep.append("new Color[] {"); for (Color color : colors) { colorsRep.append(sep); colorsRep.append("new Color(" + color.getRed() + ", " + color.getGreen() + ", " + color.getBlue() + ", " + color.getAlpha() + ")"); sep = ","; } colorsRep.append("}"); } String cycleMethodRep = null; if (cycleMethod == MultipleGradientPaint.NO_CYCLE) { cycleMethodRep = "MultipleGradientPaint.CycleMethod.NO_CYCLE"; } if (cycleMethod == MultipleGradientPaint.REFLECT) { cycleMethodRep = "MultipleGradientPaint.CycleMethod.REFLECT"; } if (cycleMethod == MultipleGradientPaint.REPEAT) { cycleMethodRep = "MultipleGradientPaint.CycleMethod.REPEAT"; } String colorSpaceRep = null; if (colorSpace == MultipleGradientPaint.SRGB) { colorSpaceRep = "MultipleGradientPaint.ColorSpaceType.SRGB"; } if (colorSpace == MultipleGradientPaint.LINEAR_RGB) { colorSpaceRep = "MultipleGradientPaint.ColorSpaceType.LINEAR_RGB"; } double[] transfMatrix = new double[6]; transform.getMatrix(transfMatrix); this.printWriter .println("paint = new LinearGradientPaint(new Point2D.Double(" + startPoint.getX() + ", " + startPoint.getY() + "), new Point2D.Double(" + endPoint.getX() + ", " + endPoint.getY() + "), " + fractionsRep.toString() + ", " + colorsRep.toString() + ", " + cycleMethodRep + ", " + colorSpaceRep + ", new AffineTransform(" + transfMatrix[0] + "f, " + transfMatrix[1] + "f, " + transfMatrix[2] + "f, " + transfMatrix[3] + "f, " + transfMatrix[4] + "f, " + transfMatrix[5] + "f));"); // offset(offset); // printWriter.println("LinearGradientPaint"); // // offset(offset + 1); // printWriter.println("START : " + paint.getStartPoint()); // // offset(offset + 1); // printWriter.println("END : " + paint.getEndPoint()); // // offset(offset + 1); // printWriter.println("FRACTIONS : " + paint.getFractions()); // // offset(offset + 1); // printWriter.println("CYCLE_METHOD : " + paint.getCycleMethod()); // // offset(offset + 1); // printWriter.println("COLOR_SPACE : " + paint.getColorSpace()); // // offset(offset + 1); // printWriter.println("GRADIENT_TRANSFORM : " + paint.getTransform()); } /** * Transcodes the specified radial gradient paint. * * @param paint * Radial gradient paint. * @throws IllegalArgumentException * if the fractions are not strictly increasing. */ private void transcodeRadialGradientPaint(RadialGradientPaint paint) throws IllegalArgumentException { // offset(offset); Point2D centerPoint = paint.getCenterPoint(); float radius = paint.getRadius(); Point2D focusPoint = paint.getFocusPoint(); float[] fractions = paint.getFractions(); Color[] colors = paint.getColors(); CycleMethodEnum cycleMethod = paint.getCycleMethod(); ColorSpaceEnum colorSpace = paint.getColorSpace(); AffineTransform transform = paint.getTransform(); float previousFraction = -1.0f; for (float currentFraction : fractions) { if (currentFraction < 0f || currentFraction > 1f) { throw new IllegalArgumentException("Fraction values must " + "be in the range 0 to 1: " + currentFraction); } if (currentFraction < previousFraction) { throw new IllegalArgumentException("Keyframe fractions " + "must be non-decreasing: " + currentFraction); } previousFraction = currentFraction; } StringBuffer fractionsRep = new StringBuffer(); if (fractions == null) { fractionsRep.append("null"); } else { String sep = ""; fractionsRep.append("new float[] {"); previousFraction = -1.0f; for (float currentFraction : fractions) { fractionsRep.append(sep); if (currentFraction == previousFraction) currentFraction += 0.000000001f; fractionsRep.append(currentFraction + "f"); sep = ","; previousFraction = currentFraction; } fractionsRep.append("}"); } StringBuffer colorsRep = new StringBuffer(); if (fractions == null) { colorsRep.append("null"); } else { String sep = ""; colorsRep.append("new Color[] {"); for (Color color : colors) { colorsRep.append(sep); colorsRep.append("new Color(" + color.getRed() + ", " + color.getGreen() + ", " + color.getBlue() + ", " + color.getAlpha() + ")"); sep = ","; } colorsRep.append("}"); } String cycleMethodRep = null; if (cycleMethod == MultipleGradientPaint.NO_CYCLE) { cycleMethodRep = "MultipleGradientPaint.CycleMethod.NO_CYCLE"; } if (cycleMethod == MultipleGradientPaint.REFLECT) { cycleMethodRep = "MultipleGradientPaint.CycleMethod.REFLECT"; } if (cycleMethod == MultipleGradientPaint.REPEAT) { cycleMethodRep = "MultipleGradientPaint.CycleMethod.REPEAT"; } String colorSpaceRep = null; if (colorSpace == MultipleGradientPaint.SRGB) { colorSpaceRep = "MultipleGradientPaint.ColorSpaceType.SRGB"; } if (colorSpace == MultipleGradientPaint.LINEAR_RGB) { colorSpaceRep = "MultipleGradientPaint.ColorSpaceType.LINEAR_RGB"; } double[] transfMatrix = new double[6]; transform.getMatrix(transfMatrix); this.printWriter .println("paint = new RadialGradientPaint(new Point2D.Double(" + centerPoint.getX() + ", " + centerPoint.getY() + "), " + radius + "f, new Point2D.Double(" + focusPoint.getX() + ", " + focusPoint.getY() + "), " + fractionsRep.toString() + ", " + colorsRep.toString() + ", " + cycleMethodRep + ", " + colorSpaceRep + ", new AffineTransform(" + transfMatrix[0] + "f, " + transfMatrix[1] + "f, " + transfMatrix[2] + "f, " + transfMatrix[3] + "f, " + transfMatrix[4] + "f, " + transfMatrix[5] + "f));"); // // printWriter.println("RadialGradientPaint"); // // offset(offset + 1); // printWriter.println("CENTER : " + paint.getCenterPoint()); // // offset(offset + 1); // printWriter.println("RADIUS : " + paint.getRadius()); // // offset(offset + 1); // printWriter.println("FOCUS : " + paint.getFocusPoint()); // // offset(offset + 1); // printWriter.println("FRACTIONS : " + paint.getFractions()); // // offset(offset + 1); // printWriter.println("COLORS : " + paint.getColors()); // // offset(offset + 1); // printWriter.println("CYCLE_METHOD : " + paint.getCycleMethod()); // // offset(offset + 1); // printWriter.println("COLOR_SPACE : " + paint.getColorSpace()); // // offset(offset + 1); // printWriter.println("GRADIENT_TRANSFORM : " + paint.getTransform()); } /** * Transcodes the specified paint. * * @param paint * Paint. * @throws UnsupportedOperationException * if the paint is unsupported. */ private void transcodePaint(Paint paint) throws UnsupportedOperationException { if (paint instanceof RadialGradientPaint) { transcodeRadialGradientPaint((RadialGradientPaint) paint); return; } if (paint instanceof LinearGradientPaint) { transcodeLinearGradientPaint((LinearGradientPaint) paint); return; } if (paint instanceof Color) { // offset(offset); // printWriter.println((Color) paint); Color c = (Color) paint; printWriter.println("paint = new Color(" + c.getRed() + ", " + c.getGreen() + ", " + c.getBlue() + ", " + c.getAlpha() + ");"); return; } if (paint == null) { // offset(offset); printWriter.println("No paint"); return; } throw new UnsupportedOperationException(paint.getClass() .getCanonicalName()); } /** * Transcodes the specified shape painter. * * @param painter * Shape painter. * @throws UnsupportedOperationException * if the shape painter is unsupported. */ private void transcodeShapePainter(ShapePainter painter) throws UnsupportedOperationException { if (painter == null) return; if (painter instanceof CompositeShapePainter) { transcodeCompositeShapePainter((CompositeShapePainter) painter); return; } if (painter instanceof FillShapePainter) { transcodeFillShapePainter((FillShapePainter) painter); return; } if (painter instanceof StrokeShapePainter) { transcodeStrokeShapePainter((StrokeShapePainter) painter); return; } throw new UnsupportedOperationException(painter.getClass() .getCanonicalName()); } /** * Transcodes the specified composite shape painter. * * @param painter * Composite shape painter. */ private void transcodeCompositeShapePainter(CompositeShapePainter painter) { // offset(offset); // printWriter.println("CompositeShapePainter"); for (int i = 0; i < painter.getShapePainterCount(); i++) { transcodeShapePainter(painter.getShapePainter(i)); } } /** * Transcodes the specified fill shape painter. * * @param painter * Fill shape painter. */ private void transcodeFillShapePainter(FillShapePainter painter) { try { Field paintFld = FillShapePainter.class.getDeclaredField("paint"); paintFld.setAccessible(true); Paint paint = (Paint) paintFld.get(painter); if (paint == null) return; transcodePaint(paint); } catch (Exception exc) { exc.printStackTrace(); } Shape shape = painter.getShape(); // offset(offset); // printWriter.println("FillShapePainter"); transcodeShape(shape); printWriter.println("g.setPaint(paint);"); printWriter.println("g.fill(shape);"); } /** * Transcodes the specified stroke shape painter. * * @param painter * Stroke shape painter. */ private void transcodeStrokeShapePainter(StrokeShapePainter painter) { Shape shape = painter.getShape(); // offset(offset); // printWriter.println("StrokeShapePainter"); try { Field paintFld = StrokeShapePainter.class.getDeclaredField("paint"); paintFld.setAccessible(true); Paint paint = (Paint) paintFld.get(painter); if (paint == null) return; transcodePaint(paint); // offset(offset + 1); // printWriter.println(paint); } catch (Exception exc) { exc.printStackTrace(); } try { Field strokeFld = StrokeShapePainter.class .getDeclaredField("stroke"); strokeFld.setAccessible(true); Stroke stroke = (Stroke) strokeFld.get(painter); // offset(offset + 1); BasicStroke bStroke = (BasicStroke) stroke; float width = bStroke.getLineWidth(); int cap = bStroke.getEndCap(); int join = bStroke.getLineJoin(); float miterlimit = bStroke.getMiterLimit(); float[] dash = bStroke.getDashArray(); float dash_phase = bStroke.getDashPhase(); StringBuffer dashRep = new StringBuffer(); if (dash == null) { dashRep.append("null"); } else { String sep = ""; dashRep.append("new float[] {"); for (float _dash : dash) { dashRep.append(sep); dashRep.append(_dash + "f"); sep = ","; } dashRep.append("}"); } printWriter.println("stroke = new BasicStroke(" + width + "f," + cap + "," + join + "," + miterlimit + "f," + dashRep + "," + dash_phase + "f);"); } catch (Exception exc) { exc.printStackTrace(); } transcodeShape(shape); printWriter.println("g.setPaint(paint);"); printWriter.println("g.setStroke(stroke);"); printWriter.println("g.draw(shape);"); } /** * Transcodes the specified shape node. * * @param node * Shape node. * @param comment * Comment (for associating the Java2D section with the * corresponding SVG section). */ private void transcodeShapeNode(ShapeNode node, String comment) { // offset(offset); // printWriter.println("ShapeNode"); printWriter.println("// " + comment); ShapePainter sPainter = node.getShapePainter(); transcodeShapePainter(sPainter); } /** * Transcodes the specified composite graphics node. * * @param node * Composite graphics node. * @param comment * Comment (for associating the Java2D section with the * corresponding SVG section). */ private void transcodeCompositeGraphicsNode(CompositeGraphicsNode node, String comment) { printWriter.println("// " + comment); // offset(offset); // printWriter.println("CompositeGraphicsNode"); int count = 0; for (Object obj : node.getChildren()) { transcodeGraphicsNode((GraphicsNode) obj, comment + "_" + count); count++; } } /** * Transcodes the specified graphics node. * * @param node * Graphics node. * @param comment * Comment (for associating the Java2D section with the * corresponding SVG section). * @throws UnsupportedOperationException * if the graphics node is unsupported. */ private void transcodeGraphicsNode(GraphicsNode node, String comment) throws UnsupportedOperationException { AlphaComposite composite = (AlphaComposite) node.getComposite(); if (composite != null) { int rule = composite.getRule(); float alpha = composite.getAlpha(); printWriter.println("g.setComposite(AlphaComposite.getInstance(" + rule + ", " + alpha + "f * origAlpha));"); } AffineTransform transform = node.getTransform(); printWriter.println("AffineTransform defaultTransform_" + comment + " = g.getTransform();"); if (transform != null) { // printWriter.println("g.transform(new AffineTransform());"); // } else { double[] transfMatrix = new double[6]; transform.getMatrix(transfMatrix); printWriter.println("g.transform(new AffineTransform(" + transfMatrix[0] + "f, " + transfMatrix[1] + "f, " + transfMatrix[2] + "f, " + transfMatrix[3] + "f, " + transfMatrix[4] + "f, " + transfMatrix[5] + "f));"); } try { if (node instanceof ShapeNode) { transcodeShapeNode((ShapeNode) node, comment); return; } if (node instanceof CompositeGraphicsNode) { transcodeCompositeGraphicsNode((CompositeGraphicsNode) node, comment); return; } throw new UnsupportedOperationException(node.getClass() .getCanonicalName()); } finally { printWriter.println("g.setTransform(defaultTransform_" + comment + ");"); } } } src/org/pushingpixels/flamingo/api/svg/SvgBatikResizableIcon.java0000644000175000017500000002041611407771362024310 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.svg; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.*; import java.net.URL; import java.util.zip.GZIPInputStream; import javax.swing.event.EventListenerList; import org.apache.batik.swing.gvt.GVTTreeRendererAdapter; import org.apache.batik.swing.gvt.GVTTreeRendererEvent; import org.apache.batik.util.EventDispatcher.Dispatcher; import org.pushingpixels.flamingo.api.common.AsynchronousLoadListener; import org.pushingpixels.flamingo.api.common.AsynchronousLoading; import org.pushingpixels.flamingo.api.common.icon.ResizableIcon; /** * SVG-based implementation of {@link ResizableIcon} based on Apache Batik * library. * * @author Kirill Grouchnikov. */ public class SvgBatikResizableIcon extends SvgBatikIcon implements ResizableIcon, AsynchronousLoading { /** * The listeners. */ protected EventListenerList listenerList; /** * Constructs an input stream with uncompressed contents from the specified * input stream with compressed contents. * * @param zippedStream * Input stream with compressed contents. * @return Input stream with uncompressed contents. * @throws IOException * in case any I/O operation failed. */ protected static InputStream constructFromZipStream(InputStream zippedStream) throws IOException { GZIPInputStream gis = new GZIPInputStream(zippedStream); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[2048]; int len; while ((len = gis.read(buf)) != -1) { baos.write(buf, 0, len); } return new ByteArrayInputStream(baos.toByteArray()); } /** * Returns the icon for the specified URL. The URL is assumed to point to * uncompressed SVG contents. * * @param location * Icon URL. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static SvgBatikResizableIcon getSvgIcon(URL location, final Dimension initialDim) { try { return new SvgBatikResizableIcon(location.openStream(), initialDim); } catch (IOException ioe) { return null; } } /** * Returns the icon for the specified input stream. The stream is assumed to * contain uncompressed SVG contents. * * @param inputStream * Icon stream. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static SvgBatikResizableIcon getSvgIcon(InputStream inputStream, final Dimension initialDim) { if (inputStream == null) return null; try { return new SvgBatikResizableIcon(inputStream, initialDim); } catch (IOException ioe) { return null; } } /** * Returns the icon for the specified URL. The URL is assumed to point to * compressed SVG contents. * * @param location * Icon URL. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static SvgBatikResizableIcon getSvgzIcon(URL location, final Dimension initialDim) { try { return new SvgBatikResizableIcon(constructFromZipStream(location .openStream()), initialDim); } catch (IOException ioe) { return null; } } /** * Returns the icon for the specified input stream. The stream is assumed to * contain compressed SVG contents. * * @param inputStream * Icon stream. * @param initialDim * Initial dimension of the icon. * @return Icon instance. */ public static SvgBatikResizableIcon getSvgzIcon(InputStream inputStream, final Dimension initialDim) { try { return new SvgBatikResizableIcon( constructFromZipStream(inputStream), initialDim); } catch (IOException ioe) { return null; } } /** * Creates a new resizable icon based on SVG content. * * @param inputStream * Input stream with uncompressed SVG content. * @param initialDim * Initial dimension. * @throws IOException * in case any I/O operation failed. */ @SuppressWarnings("unchecked") private SvgBatikResizableIcon(InputStream inputStream, final Dimension initialDim) throws IOException { super(inputStream, initialDim.width, initialDim.height); this.listenerList = new EventListenerList(); this.addGVTTreeRendererListener(new GVTTreeRendererAdapter() { @Override public void gvtRenderingCompleted(GVTTreeRendererEvent e) { fireAsyncCompleted(Boolean.valueOf(true)); } @Override public void gvtRenderingFailed(GVTTreeRendererEvent arg0) { fireAsyncCompleted(Boolean.valueOf(false)); } }); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.icon.ResizableIcon#setDimension(java.awt.Dimension * ) */ public void setDimension(Dimension dim) { this.setPreferredSize(dim); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#addAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ @SuppressWarnings("unchecked") public void addAsynchronousLoadListener(AsynchronousLoadListener l) { listenerList.add(AsynchronousLoadListener.class, l); } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.AsynchronousLoading#removeAsynchronousLoadListener * (org.jvnet.flamingo.common.AsynchronousLoadListener) */ public void removeAsynchronousLoadListener(AsynchronousLoadListener l) { listenerList.remove(AsynchronousLoadListener.class, l); } @Override protected boolean renderGVTTree(int renderWidth, int renderHeight) { boolean cached = super.renderGVTTree(renderWidth, renderHeight); if (cached) { this.fireAsyncCompleted(Boolean.valueOf(true)); } return cached; } /** * Fires the asynchronous load event. * * @param event * Event object. */ protected void fireAsyncCompleted(Boolean event) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == AsynchronousLoadListener.class) { ((AsynchronousLoadListener) listeners[i + 1]).completed(event); } } } /** * Dispatcher for GVT tree rendering completion. */ static Dispatcher asyncCompletedDispatcher = new Dispatcher() { public void dispatch(Object listener, Object event) { ((AsynchronousLoadListener) listener).completed((Boolean) event); } }; /* * (non-Javadoc) * * @see org.jvnet.flamingo.common.AsynchronousLoading#isLoading() */ @Override public synchronized boolean isLoading() { String cacheKey = this.getIconWidth() + ":" + this.getIconHeight(); BufferedImage image = this.cachedImages.get(cacheKey); return (image == null); } } src/org/pushingpixels/flamingo/api/svg/SvgStreamTranscoder.java0000644000175000017500000000612211401230442024041 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.svg; import java.io.BufferedInputStream; import java.io.InputStream; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.transcoder.SVGAbstractTranscoder; import org.apache.batik.transcoder.TranscoderInput; /** * SVG to Java2D transcoder. * * @author Kirill Grouchnikov. */ public class SvgStreamTranscoder extends SvgBaseTranscoder { public static class RawTranscoder extends SVGAbstractTranscoder { public GraphicsNode getGVTRoot() { return this.root; } } /** * Input stream of the SVG image. */ protected InputStream is; /** * Creates a new transcoder. * * @param is * Input stream of the SVG image. * @param javaClassname * Classname for the generated Java2D code. */ public SvgStreamTranscoder(InputStream is, String javaClassname) { super(javaClassname); this.is = is; } /** * Transcodes the SVG image into Java2D code. Does nothing if the * {@link #listener} is null. */ public void transcode() { if (this.externalPrintWriter == null) return; // SvgStreamCanvas canvas = new SvgStreamCanvas(); RawTranscoder transcoder = new RawTranscoder(); BufferedInputStream bis = new BufferedInputStream(is); TranscoderInput ti = new TranscoderInput(bis); try { transcoder.transcode(ti, null); this.transcode(transcoder.getGVTRoot()); } catch (Exception exc) { exc.printStackTrace(); } finally { this.externalPrintWriter.close(); } } } src/org/pushingpixels/flamingo/api/svg/SvgTranscoder.java0000644000175000017500000000630211401230442022665 0ustar tonytony/* * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.flamingo.api.svg; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.batik.bridge.*; import org.apache.batik.gvt.GraphicsNode; import org.w3c.dom.Document; /** * SVG to Java2D transcoder. * * @author Kirill Grouchnikov. */ public class SvgTranscoder extends SvgBaseTranscoder { /** * URI of the SVG image. */ protected String uri; /** * Batik bridge context. */ private BridgeContext batikBridgeContext; /** * Creates a new transcoder. * * @param uri * URI of the SVG image. * @param javaClassname * Classname for the generated Java2D code. */ public SvgTranscoder(String uri, String javaClassname) { super(javaClassname); this.uri = uri; } /** * Transcodes the SVG image into Java2D code. Does nothing if the * {@link #listener} is null. */ public void transcode() { if (this.listener == null) return; UserAgentAdapter ua = new UserAgentAdapter(); DocumentLoader loader = new DocumentLoader(ua); batikBridgeContext = new BridgeContext(ua, loader); batikBridgeContext.setDynamicState(BridgeContext.DYNAMIC); ua.setBridgeContext(batikBridgeContext); GVTBuilder builder = new GVTBuilder(); Document svgDoc; try { svgDoc = loader.loadDocument(this.uri); // System.out.println("Building: " + this.uri); GraphicsNode gvtRoot = builder.build(batikBridgeContext, svgDoc); this.transcode(gvtRoot); } catch (IOException ex) { Logger.getLogger(SvgTranscoder.class.getName()).log(Level.SEVERE, null, ex); } } } src/org/pushingpixels/flamingo/api/svg/TranscoderListener.java0000644000175000017500000000061711401230442023716 0ustar tonytonypackage org.pushingpixels.flamingo.api.svg; import java.io.Writer; /** * Transcoder listener. * * @author Kirill Grouchnikov. */ public interface TranscoderListener { /** * Returns the writer for the Java2D contents. * * @return Writer for the Java2D contents. */ public Writer getWriter(); /** * Called when the transcoding process is finished. */ public void finished(); }src/org/pushingpixels/flamingo/api/svg/SvgTranscoderTemplatePlain.templ0000644000175000017500000000371111401230442025546 0ustar tonytonyTOKEN_PACKAGE import java.awt.*; import java.awt.geom.*; /** * This class has been automatically generated using Flamingo SVG transcoder. */ public class TOKEN_CLASSNAME { /** * Paints the transcoded SVG image on the specified graphics context. You * can install a custom transformation on the graphics context to scale the * image. * * @param g * Graphics context. */ public static void paint(Graphics2D g) { Shape shape = null; Paint paint = null; Stroke stroke = null; float origAlpha = 1.0f; Composite origComposite = ((Graphics2D)g).getComposite(); if (origComposite instanceof AlphaComposite) { AlphaComposite origAlphaComposite = (AlphaComposite)origComposite; if (origAlphaComposite.getRule() == AlphaComposite.SRC_OVER) { origAlpha = origAlphaComposite.getAlpha(); } } TOKEN_PAINTING_CODE } /** * Returns the X of the bounding box of the original SVG image. * * @return The X of the bounding box of the original SVG image. */ public static int getOrigX() { return TOKEN_ORIG_X; } /** * Returns the Y of the bounding box of the original SVG image. * * @return The Y of the bounding box of the original SVG image. */ public static int getOrigY() { return TOKEN_ORIG_Y; } /** * Returns the width of the bounding box of the original SVG image. * * @return The width of the bounding box of the original SVG image. */ public static int getOrigWidth() { return TOKEN_ORIG_WIDTH; } /** * Returns the height of the bounding box of the original SVG image. * * @return The height of the bounding box of the original SVG image. */ public static int getOrigHeight() { return TOKEN_ORIG_HEIGHT; } } src/org/pushingpixels/flamingo/api/svg/SvgTranscoderTemplateResizable.templ0000644000175000017500000000656011401230442026430 0ustar tonytonyTOKEN_PACKAGE import java.awt.*; import java.awt.geom.*; /** * This class has been automatically generated using Flamingo SVG transcoder. */ public class TOKEN_CLASSNAME implements org.pushingpixels.flamingo.api.common.icon.ResizableIcon { /** * Paints the transcoded SVG image on the specified graphics context. You * can install a custom transformation on the graphics context to scale the * image. * * @param g * Graphics context. */ public static void paint(Graphics2D g) { Shape shape = null; Paint paint = null; Stroke stroke = null; float origAlpha = 1.0f; Composite origComposite = ((Graphics2D)g).getComposite(); if (origComposite instanceof AlphaComposite) { AlphaComposite origAlphaComposite = (AlphaComposite)origComposite; if (origAlphaComposite.getRule() == AlphaComposite.SRC_OVER) { origAlpha = origAlphaComposite.getAlpha(); } } TOKEN_PAINTING_CODE } /** * Returns the X of the bounding box of the original SVG image. * * @return The X of the bounding box of the original SVG image. */ public static int getOrigX() { return TOKEN_ORIG_X; } /** * Returns the Y of the bounding box of the original SVG image. * * @return The Y of the bounding box of the original SVG image. */ public static int getOrigY() { return TOKEN_ORIG_Y; } /** * Returns the width of the bounding box of the original SVG image. * * @return The width of the bounding box of the original SVG image. */ public static int getOrigWidth() { return TOKEN_ORIG_WIDTH; } /** * Returns the height of the bounding box of the original SVG image. * * @return The height of the bounding box of the original SVG image. */ public static int getOrigHeight() { return TOKEN_ORIG_HEIGHT; } /** * The current width of this resizable icon. */ int width; /** * The current height of this resizable icon. */ int height; /** * Creates a new transcoded SVG image. */ public TOKEN_CLASSNAME() { this.width = getOrigWidth(); this.height = getOrigHeight(); } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconHeight() */ @Override public int getIconHeight() { return height; } /* * (non-Javadoc) * * @see javax.swing.Icon#getIconWidth() */ @Override public int getIconWidth() { return width; } /* * (non-Javadoc) * * @see * org.jvnet.flamingo.common.icon.ResizableIcon#setDimension(java.awt.Dimension * ) */ @Override public void setDimension(Dimension newDimension) { this.width = newDimension.width; this.height = newDimension.height; } /* * (non-Javadoc) * * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, * int, int) */ @Override public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2d = (Graphics2D) g.create(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.translate(x, y); double coef1 = (double) this.width / (double) getOrigWidth(); double coef2 = (double) this.height / (double) getOrigHeight(); double coef = Math.min(coef1, coef2); g2d.scale(coef, coef); paint(g2d); g2d.dispose(); } }