2
* Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
10
* o Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
14
* o Neither the name of Flamingo Kirill Grouchnikov nor the names of
15
* its contributors may be used to endorse or promote products derived
16
* from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
package org.pushingpixels.flamingo.internal.ui.common;
33
import java.awt.image.BufferedImage;
34
import java.beans.PropertyChangeEvent;
35
import java.util.ArrayList;
36
import java.util.StringTokenizer;
38
import javax.swing.JSeparator;
40
import org.pushingpixels.flamingo.api.common.*;
41
import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind;
42
import org.pushingpixels.flamingo.api.common.icon.ResizableIcon;
43
import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities;
45
public class CommandButtonLayoutManagerBig implements
46
CommandButtonLayoutManager {
47
protected AbstractCommandButton commandButton;
50
* The first part of (possibly) two-lined split of {@link #commandButton}'s
53
protected String titlePart1;
56
* The second part of (possibly) two-lined split of {@link #commandButton}'s
59
protected String titlePart2;
61
public CommandButtonLayoutManagerBig(AbstractCommandButton commandButton) {
62
this.commandButton = commandButton;
63
this.updateTitleStrings();
67
public int getPreferredIconSize() {
72
public Dimension getPreferredSize(AbstractCommandButton commandButton) {
73
Insets borderInsets = (commandButton == null) ? new Insets(0, 0, 0, 0)
74
: commandButton.getInsets();
75
int bx = borderInsets.left + borderInsets.right;
76
FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont());
77
JSeparator jsep = new JSeparator(JSeparator.HORIZONTAL);
78
int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton);
79
int layoutVGap = FlamingoUtilities.getVLayoutGap(commandButton);
81
boolean hasIcon = (commandButton.getIcon() != null);
82
boolean hasText = (this.titlePart1 != null);
83
boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton);
85
int title1Width = (this.titlePart1 == null) ? 0 : fm
86
.stringWidth(this.titlePart1);
87
int title2Width = (this.titlePart2 == null) ? 0 : fm
88
.stringWidth(this.titlePart2);
90
int prefIconSize = hasIcon ? this.getPreferredIconSize() : 0;
92
int width = Math.max(prefIconSize, Math.max(title1Width, title2Width
95
+ jsep.getPreferredSize().height
96
+ (FlamingoUtilities.hasPopupAction(commandButton) ? 1 + fm
97
.getHeight() / 2 : 0)));
99
// start height with the top inset
100
int height = borderInsets.top;
103
// padding above the icon
104
height += layoutVGap;
106
height += prefIconSize;
107
// padding below the icon
108
height += layoutVGap;
112
// padding above the text
113
height += layoutVGap;
114
// text height - two lines
115
height += 2 * (fm.getAscent() + fm.getDescent());
116
// padding below the text
117
height += layoutVGap;
119
// popup icon (no text)?
120
if (!hasText && hasPopupIcon) {
121
// padding above the popup icon
122
height += layoutVGap;
123
// popup icon height - one line of text
124
height += fm.getHeight();
125
// padding below the popup icon
126
height += layoutVGap;
130
if (commandButton instanceof JCommandButton) {
131
JCommandButton jcb = (JCommandButton) commandButton;
132
CommandButtonKind buttonKind = jcb.getCommandButtonKind();
133
if (hasIcon && buttonKind.hasAction() && buttonKind.hasPopup()) {
134
// space for a horizontal separator
135
height += new JSeparator(JSeparator.HORIZONTAL)
136
.getPreferredSize().height;
141
height += borderInsets.bottom;
143
// and remove the padding above the first and below the last elements
144
height -= 2 * layoutVGap;
146
return new Dimension(bx + width, height);
150
public void propertyChange(PropertyChangeEvent evt) {
151
if ("text".equals(evt.getPropertyName())
152
|| "font".equals(evt.getPropertyName())) {
153
this.updateTitleStrings();
158
* Updates the title strings for {@link CommandButtonDisplayState#BIG} and
159
* other relevant states.
161
protected void updateTitleStrings() {
162
// Break the title in two parts (the second part may be empty),
163
// finding the "inflection" point. The inflection point is a space
164
// character that breaks the title in two parts, such that the maximal
165
// length of the first part and the second part + action label icon
166
// is minimal between all possible space characters
167
BufferedImage tempImage = new BufferedImage(30, 30,
168
BufferedImage.TYPE_INT_ARGB);
169
Graphics2D g = (Graphics2D) tempImage.getGraphics();
170
g.setFont(FlamingoUtilities.getFont(this.commandButton, "Ribbon.font",
171
"Button.font", "Panel.font"));
172
FontMetrics fm = g.getFontMetrics();
174
String title = (this.commandButton == null) ? null : this.commandButton
177
StringTokenizer tokenizer = new StringTokenizer(title, " _-", true);
178
if (tokenizer.countTokens() <= 1) {
180
this.titlePart1 = title;
181
this.titlePart2 = null;
183
int currMaxLength = (int) fm.getStringBounds(
184
this.commandButton.getText(), g).getWidth();
185
int actionIconWidth = FlamingoUtilities
186
.hasPopupAction(this.commandButton) ? 0 : 2
187
* FlamingoUtilities.getHLayoutGap(commandButton)
188
+ (fm.getAscent() + fm.getDescent()) / 2;
190
String currLeading = "";
191
while (tokenizer.hasMoreTokens()) {
192
currLeading += tokenizer.nextToken();
193
String part1 = currLeading;
194
String part2 = title.substring(currLeading.length());
196
int len1 = (int) fm.getStringBounds(part1, g).getWidth();
197
int len2 = (int) fm.getStringBounds(part2, g).getWidth()
199
int len = Math.max(len1, len2);
201
if (currMaxLength > len) {
203
this.titlePart1 = part1;
204
this.titlePart2 = part2;
209
this.titlePart1 = null;
210
this.titlePart2 = null;
215
public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) {
216
// center of the bottom edge
217
return new Point(commandButton.getWidth() / 2, commandButton
222
public CommandButtonLayoutInfo getLayoutInfo(
223
AbstractCommandButton commandButton, Graphics g) {
224
CommandButtonLayoutInfo result = new CommandButtonLayoutInfo();
226
result.actionClickArea = new Rectangle(0, 0, 0, 0);
227
result.popupClickArea = new Rectangle(0, 0, 0, 0);
229
Insets ins = commandButton.getInsets();
231
result.iconRect = new Rectangle();
232
result.popupActionRect = new Rectangle();
234
int width = commandButton.getWidth();
235
int height = commandButton.getHeight();
239
boolean ltr = commandButton.getComponentOrientation().isLeftToRight();
241
FontMetrics fm = g.getFontMetrics();
242
int labelHeight = fm.getAscent() + fm.getDescent();
244
JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton)
245
.getCommandButtonKind()
246
: JCommandButton.CommandButtonKind.ACTION_ONLY;
248
ResizableIcon buttonIcon = commandButton.getIcon();
250
boolean hasIcon = (commandButton.getIcon() != null);
251
boolean hasText = (this.titlePart1 != null);
252
boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton);
254
int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton);
255
int layoutVGap = FlamingoUtilities.getVLayoutGap(commandButton);
257
int prefHeight = this.getPreferredSize(commandButton).height;
259
if (height > prefHeight) {
260
shiftY = (height - prefHeight) / 2;
263
int y = ins.top + shiftY - layoutVGap;
269
int iconHeight = buttonIcon.getIconHeight();
270
int iconWidth = buttonIcon.getIconWidth();
272
result.iconRect.x = (width - iconWidth) / 2;
273
result.iconRect.y = y;
274
result.iconRect.width = iconWidth;
275
result.iconRect.height = iconHeight;
277
y += (iconHeight + layoutVGap);
281
if (commandButton instanceof JCommandButton) {
282
// horizontal separator is always after the icon
283
if (hasIcon && buttonKind.hasAction() && buttonKind.hasPopup()) {
284
result.separatorOrientation = CommandButtonLayoutManager.CommandButtonSeparatorOrientation.HORIZONTAL;
286
result.separatorArea = new Rectangle(0, 0, 0, 0);
287
result.separatorArea.x = 0;
288
result.separatorArea.y = y;
289
result.separatorArea.width = width;
290
result.separatorArea.height = new JSeparator(
291
JSeparator.HORIZONTAL).getPreferredSize().height;
293
y += result.separatorArea.height;
297
int lastTextLineWidth = 0;
301
lastTextLineWidth = (this.titlePart1 != null) ? (int) fm
302
.getStringBounds(this.titlePart1, g).getWidth() : 0;
304
TextLayoutInfo line1LayoutInfo = new TextLayoutInfo();
305
line1LayoutInfo.text = this.titlePart1;
306
line1LayoutInfo.textRect = new Rectangle();
308
line1LayoutInfo.textRect.x = ins.left
309
+ (width - lastTextLineWidth - ins.left - ins.right) / 2;
310
line1LayoutInfo.textRect.y = y;
311
line1LayoutInfo.textRect.width = lastTextLineWidth;
312
line1LayoutInfo.textRect.height = labelHeight;
314
if (this.titlePart1 != null) {
318
lastTextLineWidth = (this.titlePart2 != null) ? (int) fm
319
.getStringBounds(this.titlePart2, g).getWidth() : 0;
321
int extraWidth = hasPopupIcon ? 4 * layoutHGap + labelHeight / 2
326
+ (width - lastTextLineWidth - extraWidth - ins.left - ins.right)
333
- +(width - lastTextLineWidth - extraWidth - ins.left - ins.right)
337
TextLayoutInfo line2LayoutInfo = new TextLayoutInfo();
338
line2LayoutInfo.text = this.titlePart2;
339
line2LayoutInfo.textRect = new Rectangle();
341
line2LayoutInfo.textRect.x = x;
342
line2LayoutInfo.textRect.y = y;
343
line2LayoutInfo.textRect.width = lastTextLineWidth;
344
line2LayoutInfo.textRect.height = labelHeight;
346
result.textLayoutInfoList = new ArrayList<TextLayoutInfo>();
347
result.textLayoutInfoList.add(line1LayoutInfo);
348
result.textLayoutInfoList.add(line2LayoutInfo);
352
if (lastTextLineWidth > 0) {
355
x += lastTextLineWidth;
358
x -= labelHeight / 2;
361
x = (width - 1 - labelHeight / 2) / 2;
364
result.popupActionRect.x = x;
365
result.popupActionRect.y = y - 1;
366
result.popupActionRect.width = 1 + labelHeight / 2;
367
result.popupActionRect.height = labelHeight + 2;
370
switch (buttonKind) {
372
result.actionClickArea.x = 0;
373
result.actionClickArea.y = 0;
374
result.actionClickArea.width = width;
375
result.actionClickArea.height = height;
376
result.isTextInActionArea = true;
379
result.popupClickArea.x = 0;
380
result.popupClickArea.y = 0;
381
result.popupClickArea.width = width;
382
result.popupClickArea.height = height;
383
result.isTextInActionArea = false;
385
case ACTION_AND_POPUP_MAIN_ACTION:
386
case ACTION_AND_POPUP_MAIN_POPUP:
387
// 1. break after icon if button has icon
388
// 2. no break (all popup) if button has no icon
390
int yBorderBetweenActionAndPopupAreas = result.iconRect.y
391
+ result.iconRect.height + layoutVGap;
393
result.actionClickArea.x = 0;
394
result.actionClickArea.y = 0;
395
result.actionClickArea.width = width;
396
result.actionClickArea.height = yBorderBetweenActionAndPopupAreas;
398
result.popupClickArea.x = 0;
399
result.popupClickArea.y = yBorderBetweenActionAndPopupAreas;
400
result.popupClickArea.width = width;
401
result.popupClickArea.height = height
402
- yBorderBetweenActionAndPopupAreas;
404
result.isTextInActionArea = false;
406
result.popupClickArea.x = 0;
407
result.popupClickArea.y = 0;
408
result.popupClickArea.width = width;
409
result.popupClickArea.height = height;
411
result.isTextInActionArea = false;