/*
* 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 <code>this</code> gallery.
*/
protected List<JCommandToggleButton> buttons;
/**
* Button group for ensuring that only one button is selected.
*/
protected CommandToggleButtonGroup buttonSelectionGroup;
/**
* The current display priority of <code>this</code> in-ribbon gallery.
*/
protected RibbonElementPriority displayPriority;
/**
* Preferred widths for each possible display state (set in the user code
* according to design preferences).
*/
protected Map<RibbonElementPriority, Integer> preferredVisibleIconCount;
/**
* Gallery button groups.
*/
protected List<StringValuePair<List<JCommandToggleButton>>> 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 <code>true</code>, 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<JCommandToggleButton>();
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<RibbonElementPriority, Integer>();
// 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 <code>RibbonGalleryUI</code> 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 <code>this</code> in-ribbon gallery.
*
* @param buttonGroup
* Button group.
* @param button
* Gallery button to add.
*/
private void addGalleryButton(
StringValuePair<List<JCommandToggleButton>> 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<List<JCommandToggleButton>> 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 <code>this</code> 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 <code>this</code> 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 <code>this</code> in-ribbon gallery for
* the specified display state.
*
* @param state
* Display state.
* @param availableHeight
* Available height in pixels.
* @return The preferred width of <code>this</code> 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 <code>this</code> in-ribbon gallery.
*
* @param displayPriority
* New display priority for <code>this</code> in-ribbon gallery.
*/
public void setDisplayPriority(RibbonElementPriority displayPriority) {
this.displayPriority = displayPriority;
}
/**
* Returns the current display priority for <code>this</code> in-ribbon
* gallery.
*
* @return The current display priority for <code>this</code> in-ribbon
* gallery.
*/
public RibbonElementPriority getDisplayPriority() {
return this.displayPriority;
}
/**
* Returns the number of button groups in <code>this</code> in-ribbon
* gallery.
*
* @return The number of button groups in <code>this</code> 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<JCommandToggleButton> getButtonGroup(String buttonGroupName) {
for (StringValuePair<List<JCommandToggleButton>> group : this.buttonGroups) {
if (group.getKey().compareTo(buttonGroupName) == 0)
return group.getValue();
}
return null;
}
/**
* Returns the number of gallery buttons in <code>this</code> in-ribbon
* gallery.
*
* @return The number of gallery buttons in <code>this</code> 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<List<JCommandToggleButton>> 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<List<JCommandToggleButton>> 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 <code>true</code> if the popup panel is showing,
* <code>false</code> otherwise.
*/
public boolean isShowingPopupPanel() {
return this.isShowingPopupPanel;
}
/**
* Sets the button groups for this ribbon gallery.
*
* @param buttons
* Button groups.
*/
public void setGroupMapping(
List<StringValuePair<List<JCommandToggleButton>>> buttons) {
this.buttonGroups = new ArrayList<StringValuePair<List<JCommandToggleButton>>>();
boolean hasGroupWithNullTitle = false;
for (StringValuePair<List<JCommandToggleButton>> 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<JCommandToggleButton> buttonGroupCopy = new ArrayList<JCommandToggleButton>();
// add it to the groups list
StringValuePair<List<JCommandToggleButton>> buttonGroupInfo = new StringValuePair<List<JCommandToggleButton>>(
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<List<JCommandToggleButton>> 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<List<JCommandToggleButton>> buttonGroup : this.buttonGroups) {
for (Iterator<JCommandToggleButton> 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);
}
}
}