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