/*
* 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<TextLayoutInfo>();
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;
}
}