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.beans.PropertyChangeEvent;
34
import java.util.ArrayList;
36
import javax.swing.JSeparator;
37
import javax.swing.SwingConstants;
39
import org.pushingpixels.flamingo.api.common.*;
40
import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind;
41
import org.pushingpixels.flamingo.internal.ui.common.popup.BasicCommandPopupMenuUI;
42
import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities;
44
public class CommandButtonLayoutManagerMedium implements
45
CommandButtonLayoutManager {
47
public int getPreferredIconSize() {
51
protected float getIconTextGapFactor() {
55
private boolean hasIcon(AbstractCommandButton button) {
56
return button.getIcon() != null || Boolean.TRUE.equals(button.getClientProperty(BasicCommandPopupMenuUI.FORCE_ICON));
60
public Dimension getPreferredSize(AbstractCommandButton commandButton) {
61
Insets borderInsets = commandButton.getInsets();
62
int by = borderInsets.top + borderInsets.bottom;
63
FontMetrics fm = commandButton.getFontMetrics(commandButton.getFont());
65
String buttonText = commandButton.getText();
66
int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton);
68
boolean hasIcon = this.hasIcon(commandButton);
69
boolean hasText = (buttonText != null);
70
boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton);
72
int prefIconSize = hasIcon ? this.getPreferredIconSize() : 0;
74
// start with the left insets
75
int width = borderInsets.left;
78
// padding before the icon
81
width += prefIconSize;
82
// padding after the icon
87
// padding before the text
89
width += (int) (layoutHGap * getIconTextGapFactor());
94
width += fm.stringWidth(buttonText);
95
// padding after the text
100
// padding before the popup icon
101
if (hasText && hasIcon) {
102
width += 2 * layoutHGap;
105
width += 1 + fm.getHeight() / 2;
106
// padding after the popup icon
107
width += 2 * layoutHGap;
111
if (commandButton instanceof JCommandButton) {
112
JCommandButton jcb = (JCommandButton) commandButton;
113
CommandButtonKind buttonKind = jcb.getCommandButtonKind();
114
boolean hasSeparator = false;
115
if (buttonKind == CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION
116
&& (hasIcon || hasText)) {
119
if (buttonKind == CommandButtonKind.ACTION_AND_POPUP_MAIN_POPUP
124
// space for a vertical separator
125
width += new JSeparator(JSeparator.VERTICAL).getPreferredSize().width;
130
width += borderInsets.right;
132
// and remove the padding before the first and after the last elements
133
width -= 2 * layoutHGap;
135
return new Dimension(width, by
136
+ Math.max(prefIconSize, fm.getAscent() + fm.getDescent()));
140
public void propertyChange(PropertyChangeEvent evt) {
144
public Point getKeyTipAnchorCenterPoint(AbstractCommandButton commandButton) {
145
Insets ins = commandButton.getInsets();
146
int height = commandButton.getHeight();
147
boolean hasIcon = this.hasIcon(commandButton);
148
int iconSize = this.getPreferredIconSize();
150
// bottom-right corner of the icon area
151
return new Point(ins.left + iconSize, (height + iconSize) / 2);
153
// bottom-left corner of the button
154
return new Point(ins.left, 3 * height / 4);
159
public CommandButtonLayoutInfo getLayoutInfo(
160
AbstractCommandButton commandButton, Graphics g) {
161
CommandButtonLayoutInfo result = new CommandButtonLayoutInfo();
163
result.actionClickArea = new Rectangle(0, 0, 0, 0);
164
result.popupClickArea = new Rectangle(0, 0, 0, 0);
166
Insets ins = commandButton.getInsets();
168
result.iconRect = new Rectangle();
169
result.popupActionRect = new Rectangle();
171
int width = commandButton.getWidth();
172
int height = commandButton.getHeight();
174
String buttonText = commandButton.getText();
175
int iconSize = this.getPreferredIconSize();
177
boolean hasIcon = this.hasIcon(commandButton);
178
boolean hasText = (buttonText != null);
179
boolean hasPopupIcon = FlamingoUtilities.hasPopupAction(commandButton);
181
boolean ltr = commandButton.getComponentOrientation().isLeftToRight();
183
int prefWidth = this.getPreferredSize(commandButton).width;
185
if (commandButton.getHorizontalAlignment() == SwingConstants.CENTER) {
186
if (width > prefWidth) {
187
shiftX = (width - prefWidth) / 2;
191
FontMetrics fm = g.getFontMetrics();
192
int labelHeight = fm.getAscent() + fm.getDescent();
194
JCommandButton.CommandButtonKind buttonKind = (commandButton instanceof JCommandButton) ? ((JCommandButton) commandButton)
195
.getCommandButtonKind()
196
: JCommandButton.CommandButtonKind.ACTION_ONLY;
197
int layoutHGap = FlamingoUtilities.getHLayoutGap(commandButton);
200
int x = ins.left + shiftX - layoutHGap;
206
result.iconRect.x = x;
207
result.iconRect.y = (height - iconSize) / 2;
208
result.iconRect.width = iconSize;
209
result.iconRect.height = iconSize;
211
x += (iconSize + layoutHGap);
217
x += (int) (layoutHGap * getIconTextGapFactor());
222
TextLayoutInfo lineLayoutInfo = new TextLayoutInfo();
223
lineLayoutInfo.text = commandButton.getText();
224
lineLayoutInfo.textRect = new Rectangle();
225
result.textLayoutInfoList = new ArrayList<TextLayoutInfo>();
226
result.textLayoutInfoList.add(lineLayoutInfo);
228
lineLayoutInfo.textRect.x = x;
229
lineLayoutInfo.textRect.y = (height - labelHeight) / 2;
230
lineLayoutInfo.textRect.width = (int) fm.getStringBounds(
231
buttonText, g).getWidth();
232
lineLayoutInfo.textRect.height = labelHeight;
233
x += lineLayoutInfo.textRect.width;
238
int verticalSeparatorWidth = new JSeparator(JSeparator.VERTICAL)
239
.getPreferredSize().width;
241
if (hasText && hasIcon) {
245
result.popupActionRect.x = commandButton.getWidth() - 1 - labelHeight / 2 - 3*layoutHGap + verticalSeparatorWidth;
246
result.popupActionRect.y = (height - labelHeight) / 2 - 1;
247
result.popupActionRect.width = 1 + labelHeight / 2;
248
result.popupActionRect.height = labelHeight + 2;
249
x += result.popupActionRect.width;
254
int xBorderBetweenActionAndPopup;
255
// compute the action and popup click areas
256
switch (buttonKind) {
258
result.actionClickArea.x = 0;
259
result.actionClickArea.y = 0;
260
result.actionClickArea.width = width;
261
result.actionClickArea.height = height;
262
result.isTextInActionArea = true;
265
result.popupClickArea.x = 0;
266
result.popupClickArea.y = 0;
267
result.popupClickArea.width = width;
268
result.popupClickArea.height = height;
269
result.isTextInActionArea = false;
271
case ACTION_AND_POPUP_MAIN_ACTION:
272
// 1. break before popup icon if button has text or icon
273
// 2. no break (all popup) if button has no text and no icon
274
if (hasText || hasIcon) {
275
xBorderBetweenActionAndPopup = result.popupActionRect.x - 2
278
result.actionClickArea.x = 0;
279
result.actionClickArea.y = 0;
280
result.actionClickArea.width = xBorderBetweenActionAndPopup;
281
result.actionClickArea.height = height;
283
result.popupClickArea.x = xBorderBetweenActionAndPopup;
284
result.popupClickArea.y = 0;
285
result.popupClickArea.width = width
286
- xBorderBetweenActionAndPopup;
287
result.popupClickArea.height = height;
289
result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL;
290
result.separatorArea = new Rectangle();
291
result.separatorArea.x = xBorderBetweenActionAndPopup;
292
result.separatorArea.y = 0;
293
result.separatorArea.width = verticalSeparatorWidth;
294
result.separatorArea.height = height;
296
result.isTextInActionArea = true;
298
result.popupClickArea.x = 0;
299
result.popupClickArea.y = 0;
300
result.popupClickArea.width = width;
301
result.popupClickArea.height = height;
303
result.isTextInActionArea = false;
306
case ACTION_AND_POPUP_MAIN_POPUP:
307
// 1. break after icon if button has icon
308
// 2. no break (all popup) if button has no icon
310
// shift text rectangle to the right
311
// to accomodate the vertical separator
312
if (result.textLayoutInfoList != null) {
313
for (TextLayoutInfo textLayoutInfo : result.textLayoutInfoList) {
314
textLayoutInfo.textRect.x += verticalSeparatorWidth;
318
xBorderBetweenActionAndPopup = result.iconRect.x
319
+ result.iconRect.width + layoutHGap;
321
result.actionClickArea.x = 0;
322
result.actionClickArea.y = 0;
323
result.actionClickArea.width = xBorderBetweenActionAndPopup;
324
result.actionClickArea.height = height;
326
result.popupClickArea.x = xBorderBetweenActionAndPopup;
327
result.popupClickArea.y = 0;
328
result.popupClickArea.width = width
329
- xBorderBetweenActionAndPopup;
330
result.popupClickArea.height = height;
332
result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL;
333
result.separatorArea = new Rectangle();
334
result.separatorArea.x = xBorderBetweenActionAndPopup;
335
result.separatorArea.y = 0;
336
result.separatorArea.width = verticalSeparatorWidth;
337
result.separatorArea.height = height;
339
result.isTextInActionArea = false;
341
result.popupClickArea.x = 0;
342
result.popupClickArea.y = 0;
343
result.popupClickArea.width = width;
344
result.popupClickArea.height = height;
346
result.isTextInActionArea = true;
351
int x = width - ins.right - shiftX + layoutHGap;
357
result.iconRect.x = x - iconSize;
358
result.iconRect.y = (height - iconSize) / 2;
359
result.iconRect.width = iconSize;
360
result.iconRect.height = iconSize;
362
x -= (iconSize + layoutHGap);
368
x -= (int) (layoutHGap * getIconTextGapFactor());
373
TextLayoutInfo lineLayoutInfo = new TextLayoutInfo();
374
lineLayoutInfo.text = commandButton.getText();
375
lineLayoutInfo.textRect = new Rectangle();
376
result.textLayoutInfoList = new ArrayList<TextLayoutInfo>();
377
result.textLayoutInfoList.add(lineLayoutInfo);
379
lineLayoutInfo.textRect.width = (int) fm.getStringBounds(
380
buttonText, g).getWidth();
381
lineLayoutInfo.textRect.x = x - lineLayoutInfo.textRect.width;
382
lineLayoutInfo.textRect.y = (height - labelHeight) / 2;
383
lineLayoutInfo.textRect.height = labelHeight;
384
x -= lineLayoutInfo.textRect.width;
390
if (hasText && hasIcon) {
394
result.popupActionRect.width = 1 + labelHeight / 2;
395
result.popupActionRect.x = x - result.popupActionRect.width;
396
result.popupActionRect.x = layoutHGap*2;
397
result.popupActionRect.y = (height - labelHeight) / 2 - 1;
398
result.popupActionRect.height = labelHeight + 2;
399
x -= result.popupActionRect.width;
404
int xBorderBetweenActionAndPopup;
405
int verticalSeparatorWidth = new JSeparator(JSeparator.VERTICAL)
406
.getPreferredSize().width;
407
// compute the action and popup click areas
408
switch (buttonKind) {
410
result.actionClickArea.x = 0;
411
result.actionClickArea.y = 0;
412
result.actionClickArea.width = width;
413
result.actionClickArea.height = height;
414
result.isTextInActionArea = true;
417
result.popupClickArea.x = 0;
418
result.popupClickArea.y = 0;
419
result.popupClickArea.width = width;
420
result.popupClickArea.height = height;
421
result.isTextInActionArea = false;
423
case ACTION_AND_POPUP_MAIN_ACTION:
424
// 1. break before popup icon if button has text or icon
425
// 2. no break (all popup) if button has no text and no icon
426
if (hasText || hasIcon) {
427
// shift popup action rectangle to the left to
428
// accomodate the vertical separator
430
xBorderBetweenActionAndPopup = result.popupActionRect.x
431
+ result.popupActionRect.width + 2 * layoutHGap;
433
result.actionClickArea.x = xBorderBetweenActionAndPopup;
434
result.actionClickArea.y = 0;
435
result.actionClickArea.width = width
436
- xBorderBetweenActionAndPopup;
437
result.actionClickArea.height = height;
439
result.popupClickArea.x = 0;
440
result.popupClickArea.y = 0;
441
result.popupClickArea.width = xBorderBetweenActionAndPopup;
442
result.popupClickArea.height = height;
444
result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL;
445
result.separatorArea = new Rectangle();
446
result.separatorArea.x = xBorderBetweenActionAndPopup;
447
result.separatorArea.y = 0;
448
result.separatorArea.width = verticalSeparatorWidth;
449
result.separatorArea.height = height;
451
result.isTextInActionArea = true;
453
result.popupClickArea.x = 0;
454
result.popupClickArea.y = 0;
455
result.popupClickArea.width = width;
456
result.popupClickArea.height = height;
458
result.isTextInActionArea = false;
461
case ACTION_AND_POPUP_MAIN_POPUP:
462
// 1. break after icon if button has icon
463
// 2. no break (all popup) if button has no icon
465
// shift text rectangle and popup action rectangle to the
466
// left to accomodate the vertical separator
467
if (result.textLayoutInfoList != null) {
468
for (TextLayoutInfo textLayoutInfo : result.textLayoutInfoList) {
469
textLayoutInfo.textRect.x -= verticalSeparatorWidth;
473
xBorderBetweenActionAndPopup = result.iconRect.x
476
result.actionClickArea.x = xBorderBetweenActionAndPopup;
477
result.actionClickArea.y = 0;
478
result.actionClickArea.width = width
479
- xBorderBetweenActionAndPopup;
480
result.actionClickArea.height = height;
482
result.popupClickArea.x = 0;
483
result.popupClickArea.y = 0;
484
result.popupClickArea.width = xBorderBetweenActionAndPopup;
485
result.popupClickArea.height = height;
487
result.separatorOrientation = CommandButtonSeparatorOrientation.VERTICAL;
488
result.separatorArea = new Rectangle();
489
result.separatorArea.x = xBorderBetweenActionAndPopup;
490
result.separatorArea.y = 0;
491
result.separatorArea.width = verticalSeparatorWidth;
492
result.separatorArea.height = height;
494
result.isTextInActionArea = false;
496
result.popupClickArea.x = 0;
497
result.popupClickArea.y = 0;
498
result.popupClickArea.width = width;
499
result.popupClickArea.height = height;
501
result.isTextInActionArea = true;