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.ribbon;
34
import java.util.List;
37
import javax.swing.event.ChangeEvent;
38
import javax.swing.event.ChangeListener;
39
import javax.swing.plaf.ComponentUI;
41
import org.pushingpixels.flamingo.api.common.AbstractCommandButton;
42
import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState;
43
import org.pushingpixels.flamingo.api.ribbon.*;
44
import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy;
45
import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy;
46
import org.pushingpixels.flamingo.internal.ui.ribbon.BasicRibbonBandUI.CollapsedButtonPopupPanel;
49
* Basic UI for control panel of ribbon band {@link JBandControlPanel}.
51
* @author Kirill Grouchnikov
53
public class BasicBandControlPanelUI extends AbstractBandControlPanelUI {
54
private JSeparator[] groupSeparators;
56
private JLabel[] groupLabels;
58
protected ChangeListener changeListener;
63
* @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
65
public static ComponentUI createUI(JComponent c) {
66
return new BasicBandControlPanelUI();
70
* Invoked by <code>installUI</code> to create a layout manager object to
71
* manage the {@link JBandControlPanel}.
73
* @return a layout manager object
76
protected LayoutManager createLayoutManager() {
77
return new ControlPanelLayout();
81
protected void installListeners() {
82
super.installListeners();
84
this.changeListener = new ChangeListener() {
86
public void stateChanged(ChangeEvent e) {
88
controlPanel.revalidate();
91
((JBandControlPanel) this.controlPanel)
92
.addChangeListener(this.changeListener);
96
protected void uninstallListeners() {
97
((JBandControlPanel) this.controlPanel)
98
.removeChangeListener(this.changeListener);
99
this.changeListener = null;
101
super.uninstallListeners();
105
protected void installComponents() {
106
super.installComponents();
108
this.syncGroupHeaders();
112
protected void uninstallComponents() {
113
if (this.groupSeparators != null) {
114
for (JSeparator groupSeparator : this.groupSeparators) {
115
this.controlPanel.remove(groupSeparator);
118
if (this.groupLabels != null) {
119
for (JLabel groupLabel : this.groupLabels) {
120
if (groupLabel != null)
121
this.controlPanel.remove(groupLabel);
125
super.uninstallComponents();
128
protected void syncGroupHeaders() {
129
if (this.groupSeparators != null) {
130
for (JSeparator groupSeparator : this.groupSeparators) {
131
this.controlPanel.remove(groupSeparator);
134
if (this.groupLabels != null) {
135
for (JLabel groupLabel : this.groupLabels) {
136
if (groupLabel != null)
137
this.controlPanel.remove(groupLabel);
141
int groupCount = ((JBandControlPanel) this.controlPanel)
142
.getControlPanelGroupCount();
143
if (groupCount > 1) {
144
this.groupSeparators = new JSeparator[groupCount - 1];
145
for (int i = 0; i < groupCount - 1; i++) {
146
this.groupSeparators[i] = new JSeparator(JSeparator.VERTICAL);
147
this.controlPanel.add(this.groupSeparators[i]);
150
if (groupCount > 0) {
151
this.groupLabels = new JLabel[groupCount];
152
for (int i = 0; i < groupCount; i++) {
153
String title = ((JBandControlPanel) this.controlPanel)
154
.getControlPanelGroupTitle(i);
156
this.groupLabels[i] = new JLabel(title);
157
this.controlPanel.add(this.groupLabels[i]);
165
* Layout for the control panel of ribbon band.
167
* @author Kirill Grouchnikov
169
private class ControlPanelLayout implements LayoutManager {
174
* @see java.awt.LayoutManager#addLayoutComponent(java.lang.String,
175
* java.awt.Component)
178
public void addLayoutComponent(String name, Component c) {
184
* @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component)
187
public void removeLayoutComponent(Component c) {
193
* @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container)
196
public Dimension preferredLayoutSize(Container c) {
197
// The height of ribbon band control panel is
198
// computed based on the preferred height of a command
199
// button in BIG state.
200
int buttonHeight = dummy.getPreferredSize().height;
201
int vGap = getLayoutGap() * 3 / 4;
202
int minusGaps = buttonHeight - 2 * vGap;
203
switch (minusGaps % 3) {
212
Insets ins = c.getInsets();
214
// System.out.println("Control panel pref = "
215
// + (buttonHeight + ins.top + ins.bottom));
217
return new Dimension(c.getWidth(), buttonHeight + ins.top
224
* @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container)
227
public Dimension minimumLayoutSize(Container c) {
228
return this.preferredLayoutSize(c);
234
* @see java.awt.LayoutManager#layoutContainer(java.awt.Container)
237
public void layoutContainer(Container c) {
238
// System.out.println("Control panel real = " + c.getHeight());
240
AbstractRibbonBand ribbonBand = ((JBandControlPanel) c)
242
RibbonBandResizePolicy currentResizePolicy = ribbonBand
243
.getCurrentResizePolicy();
244
if (currentResizePolicy == null)
247
boolean ltr = c.getComponentOrientation().isLeftToRight();
249
// need place for border
250
Insets ins = c.getInsets();
251
int gap = getLayoutGap();
252
int x = ltr ? ins.left + gap / 2 : c.getWidth() - ins.right - gap
254
int availableHeight = c.getHeight() - ins.top - ins.bottom;
256
if (SwingUtilities.getAncestorOfClass(
257
CollapsedButtonPopupPanel.class, c) != null) {
258
// install the most permissive resize policy on the popup
259
// panel of a collapsed ribbon band
260
List<RibbonBandResizePolicy> resizePolicies = ribbonBand
261
.getResizePolicies();
262
resizePolicies.get(0).install(availableHeight, gap);
264
if (currentResizePolicy instanceof IconRibbonBandResizePolicy) {
268
// Installs the resize policy - this updates the display
269
// priority of all the galleries and buttons
270
currentResizePolicy.install(availableHeight, gap);
273
int controlPanelGroupIndex = 0;
274
for (JBandControlPanel.ControlPanelGroup controlPanelGroup : ((JBandControlPanel) controlPanel)
275
.getControlPanelGroups()) {
276
// handle the group separators
277
if (controlPanelGroupIndex > 0) {
278
int prefW = groupSeparators[controlPanelGroupIndex - 1]
279
.getPreferredSize().width;
280
int sepX = ltr ? x - gap + (gap - prefW) / 2 : x + gap / 2
282
groupSeparators[controlPanelGroupIndex - 1].setBounds(sepX,
283
ins.top, prefW, availableHeight);
286
boolean hasLeadingComponent = false;
287
boolean isCoreContent = controlPanelGroup.isCoreContent();
289
// how much vertical space is available in each row?
290
int singleRowHeight = availableHeight / 3;
292
boolean hasTitle = (controlPanelGroup.getGroupTitle() != null);
293
int maxWidthInCurrColumn = 0;
295
JLabel titleLabel = groupLabels[controlPanelGroupIndex];
296
int pw = titleLabel.getPreferredSize().width;
297
int titleLabelHeight = Math.min(singleRowHeight - gap
298
/ 4, titleLabel.getPreferredSize().height);
299
int yNudge = singleRowHeight - titleLabelHeight;
300
int baseline = (titleLabelHeight > 0) ? titleLabel
301
.getBaseline(pw, titleLabelHeight) : 0;
303
titleLabel.setBounds(x + gap, ins.top + yNudge
304
- titleLabelHeight + baseline, pw,
307
titleLabel.setBounds(x - gap - pw, ins.top + yNudge
308
- titleLabelHeight + baseline, pw,
311
maxWidthInCurrColumn = gap + pw;
313
List<JRibbonComponent> ribbonComps = controlPanelGroup
315
Map<JRibbonComponent, Integer> ribbonCompRowSpans = controlPanelGroup
316
.getRibbonCompsRowSpans();
317
List<JRibbonComponent> currColumn = new ArrayList<JRibbonComponent>();
319
// if a group has a title, then the core components in that
320
// group will take two rows instead of three
321
int startingRow = hasTitle ? 1 : 0;
322
int rowIndex = startingRow;
324
for (int i = 0; i < ribbonComps.size(); i++) {
325
JRibbonComponent coreComp = ribbonComps.get(i);
326
int prefWidth = coreComp.getPreferredSize().width;
327
int rowSpan = ribbonCompRowSpans.get(coreComp);
329
// do we need to start a new column?
330
int nextRowIndex = rowIndex + rowSpan;
331
if (nextRowIndex > 3) {
333
if (hasLeadingComponent)
335
x += maxWidthInCurrColumn;
337
if (hasLeadingComponent)
339
x -= maxWidthInCurrColumn;
341
hasLeadingComponent = true;
342
maxWidthInCurrColumn = 0;
343
rowIndex = startingRow;
347
// how much vertical space does a component get?
348
int compHeight = Math.min(rowSpan * singleRowHeight
349
- gap / 4, coreComp.getPreferredSize().height);
350
int yNudge = rowSpan * singleRowHeight - compHeight;
351
int y = rowIndex * singleRowHeight + ins.top;
354
coreComp.setBounds(x, y + yNudge, prefWidth,
357
coreComp.setBounds(x - prefWidth, y + yNudge,
358
prefWidth, compHeight);
360
maxWidthInCurrColumn = Math.max(maxWidthInCurrColumn,
362
currColumn.add(coreComp);
364
coreComp.putClientProperty(
365
AbstractBandControlPanelUI.TOP_ROW, rowIndex == 0);
366
coreComp.putClientProperty(
367
AbstractBandControlPanelUI.MID_ROW, (rowIndex > 0)
369
coreComp.putClientProperty(
370
AbstractBandControlPanelUI.BOTTOM_ROW, rowIndex == 2);
372
// scan the components in this column and make them to
373
// have the same width as the widest component in this
375
for (JRibbonComponent comp : currColumn) {
376
Rectangle bounds = comp.getBounds();
378
comp.setBounds(bounds.x, bounds.y,
379
maxWidthInCurrColumn, bounds.height);
381
comp.setBounds(bounds.x + bounds.width
382
- maxWidthInCurrColumn, bounds.y,
383
maxWidthInCurrColumn, bounds.height);
389
// .println(rowSpan + ":" + coreComp.getBounds());
393
if ((rowIndex > 0) && (rowIndex <= 3)) {
395
if (hasLeadingComponent)
397
x += maxWidthInCurrColumn;
399
if (hasLeadingComponent)
401
x -= maxWidthInCurrColumn;
403
hasLeadingComponent = true;
407
for (RibbonElementPriority elementPriority : RibbonElementPriority
409
for (JRibbonGallery gallery : controlPanelGroup
410
.getRibbonGalleries(elementPriority)) {
411
int pw = gallery.getPreferredWidth(gallery
412
.getDisplayPriority(), availableHeight);
414
gallery.setBounds(x, ins.top, pw,
416
if (hasLeadingComponent)
420
gallery.setBounds(x - pw, ins.top, pw,
422
if (hasLeadingComponent)
426
hasLeadingComponent = true;
430
Map<CommandButtonDisplayState, List<AbstractCommandButton>> buttonMap = new HashMap<CommandButtonDisplayState, List<AbstractCommandButton>>();
431
for (RibbonElementPriority elementPriority : RibbonElementPriority
433
for (AbstractCommandButton commandButton : controlPanelGroup
434
.getRibbonButtons(elementPriority)) {
435
CommandButtonDisplayState state = commandButton
437
if (buttonMap.get(state) == null) {
439
new ArrayList<AbstractCommandButton>());
441
buttonMap.get(state).add(commandButton);
445
List<AbstractCommandButton> bigs = buttonMap
446
.get(CommandButtonDisplayState.BIG);
448
for (AbstractCommandButton bigButton : bigs) {
450
int bigButtonWidth = bigButton.getPreferredSize().width;
451
if (hasLeadingComponent) {
459
bigButton.setBounds(x, ins.top, bigButtonWidth,
462
bigButton.setBounds(x - bigButtonWidth,
463
ins.top, bigButtonWidth,
466
bigButton.putClientProperty(TOP_ROW, Boolean.FALSE);
467
bigButton.putClientProperty(MID_ROW, Boolean.FALSE);
468
bigButton.putClientProperty(BOTTOM_ROW,
475
hasLeadingComponent = true;
480
int vGap = gap * 3 / 4;
481
int medSmallButtonHeight = (availableHeight - 2 * vGap) / 3;
485
List<AbstractCommandButton> mediums = buttonMap
486
.get(CommandButtonDisplayState.MEDIUM);
487
if (mediums != null) {
488
for (AbstractCommandButton mediumButton : mediums) {
489
int medWidth = mediumButton.getPreferredSize().width;
490
maxWidth3 = Math.max(maxWidth3, medWidth);
492
if (hasLeadingComponent && (index3 == 0)) {
499
int buttonTop = (medSmallButtonHeight + vGap)
501
int buttonBottom = (medSmallButtonHeight + vGap)
502
* (index3 + 1) - vGap;
504
mediumButton.setBounds(x, ins.top + buttonTop,
505
medWidth, buttonBottom - buttonTop);
507
mediumButton.setBounds(x - medWidth, ins.top
508
+ buttonTop, medWidth, buttonBottom
511
mediumButton.putClientProperty(TOP_ROW, index3 == 0);
512
mediumButton.putClientProperty(MID_ROW, index3 == 1);
513
mediumButton.putClientProperty(BOTTOM_ROW, index3 == 2);
517
// last button in threesome
524
hasLeadingComponent = true;
529
// at this point, maxWidth3 may be non-null. We can safely
530
// add it, since in this case there will be no buttons
531
// left in lowButtons
538
hasLeadingComponent = true;
543
List<AbstractCommandButton> smalls = buttonMap
544
.get(CommandButtonDisplayState.SMALL);
545
if (smalls != null) {
546
for (AbstractCommandButton smallButton : smalls) {
547
int lowWidth = smallButton.getPreferredSize().width;
548
maxWidth3 = Math.max(maxWidth3, lowWidth);
549
if (hasLeadingComponent && (index3 == 0)) {
556
int buttonTop = (medSmallButtonHeight + vGap)
558
int buttonBottom = (medSmallButtonHeight + vGap)
559
* (index3 + 1) - vGap;
561
smallButton.setBounds(x, ins.top + buttonTop,
562
lowWidth, buttonBottom - buttonTop);
564
smallButton.setBounds(x - lowWidth, ins.top
565
+ buttonTop, lowWidth, buttonBottom
568
smallButton.putClientProperty(TOP_ROW, index3 == 0);
569
smallButton.putClientProperty(MID_ROW, index3 == 1);
570
smallButton.putClientProperty(BOTTOM_ROW, index3 == 2);
574
// last button in threesome
581
hasLeadingComponent = true;
586
if ((index3 < 3) && (maxWidth3 > 0)) {
592
hasLeadingComponent = true;
595
// space for the separator
601
controlPanelGroupIndex++;