/******************************************************************************* * Copyright (c) Emil Crumhorn - Hexapixel.com - emil.crumhorn@gmail.com * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * emil.crumhorn@gmail.com - initial API and implementation *******************************************************************************/ package org.eclipse.nebula.widgets.collapsiblebuttons; import java.util.ArrayList; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.MouseTrackListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Layout; /** * <b>CollapsibleButtonsWidget - SWT/JFace Widget - 2005-2007. Version 1.0. * © Emil Crumhorn - emil.crumhorn@gmail.com.</b> * <p> * <b>Website</b><br> * If you want more info or more documentation, please visit: <a * href="http://www.hexapixel.com/">http://www.hexapixel.com</a> * <p> * <b>Description</b><br> * ButtonComposite is a Widget that displays a vertical row of buttons similar * to the way that Microsoft® shows buttons in the bottom left of the * Microsoft Outlook 2005, and 2007 editions. The button bar is a collapsible * bar that contains an image, text, and an associated toolbar at the very * bottom where buttons that are currently "collapsed" are shown. There is also * a menu which is activated by clicking a small arrow icon on the toolbar that * will allow you to do actions that are similar to the actions you can do with * the mouse. * <p> * The bar is resized by dragging the handle-bar which is at the very top. When * the mouse is clicked on the handlebar and dragged either up or down, buttons * will be shown and hidden accordingly. The button bar also has a feature where * - if there's no space to show the actively shown buttons due to progam window * size, buttons will automatically collapse to conserve space, and then * automatically expand back to the original number of visible buttons when the * program window is returned to a size where they can be shown. * <p> * <b>Where to put it</b><br> * It is important to point out that due to the nature of the ButtonComposite, * it is important to put it inside a layout that will allow it to * expand/collapse with the widgets that are around it - as whenever a button is * shown/hidden, the actual physical size of the widget changes, which should * cause surrounding widgets to take up either more or less space. I personally * recommend putting the ButtonBar inside either a ViewForm, SashForm. * <p> * If you still wish to put it on a plain composite, I suggest doing it the * following way: * <p> * <code> * // outer layer wrapper<br> * Composite bcWrapper = new Composite(parentComposite, SWT.None);<br> * GridLayout gl = new GridLayout(1, true);<br> * gl.marginBottom = 0;<br> * gl.marginHeight = 0;<br> * gl.marginWidth = 0;<br> * gl.marginHeight = 0;<br> * inner.setLayout(gl);<br> * <br> * CollapsibleButtons cButtons = new CollapsibleButtons(bcWrapper, SWT.NONE);<br> * // will ensure the composite takes up the appropriate amount of space and gets aligned correctly, we align it at the end as that is where it should live<br> * cButtons.setLayoutData(new GridData(GridData.GRAB_VERTICAL | GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_END));<br> * cButtons.addButton(...);<br> * </code> * <p> * <b>Customizing</b><br> * As many people wish to customize the widget beyond the capabilities it * already has, there are a few ways you may basically take control over as much * or little as you please. First, there are three interfaces that are of * importance (apart from the IButtonListener), one is the IButtonPainter, the * IColorManager and the ISettings. Let's start with the IColorManager. * <p> * <b>IColorManager</b><br> * If you don't specify a color manager, the DefaultColorManager will be used. * The color manager's job is to return colors to the method that is painting * the button when the button is drawn, when you move over a button, when you * select a button, and when you hover over a selected button. The colors that * are returned from the ColorManager will determine everything as far as looks * go. * <p> * <b>IButtonPainter</b><br> * Then there's the IButtonPainter. The IButtonPainter lets you take over 3 * methods, which are how A. The background colors of the button are painted. B. * How the text is drawn, and C. How the image is painted. As this is basically * 100% of how a button is drawn, you can control every aspect of the button * look and feel. By default, if no IButtonPainter is assigned, the * DefaultButtonPainter will be used. The IButtonPainter's paintBackground(...) * method is also used to draw all aspects of the tool bar. That way, the * toolbar automatically gets the same colors etc like the normal buttons. * <p> * <b>ISettings</b><br> * To control a lot of the features, such as, whether to show the toolbar or * not, whether you want certain size buttons, painted borders, and so on, you * can create a class that implements ISettings and set it as the Settings * manager when you create a ButtonComposite. If you don't specify one, * DefaultSettings will be used. * <p> * Each button is esentially a composite inside a custom layout, and the toolbar * is a composite by itself. * <p> * If a toolbar is not wanted, you may control its visibility by implementing * ISettings * <p> * <b>Double Buffering</b><br> * It is also important to note that the widget uses custom double buffering as * neither Windows XP's or SWT's DOUBLE_BUFFER flag do the job well enough. To * do a proper double buffer, the widget is first drawn into a cached Graphics * object that is then copied onto the actual graphics object. This reduces * flickering and other odd widget behavior. If you intend to run the widget on * either Linux or Macintosh (which both know how to double buffer in the OS), * you may turn it off. * * @author Emil Crumhorn * @version 1.0 * */ public class CollapsibleButtons extends Composite implements MouseListener, MouseMoveListener, MouseTrackListener { // cursors we'll use, hand is for mouse-button-overs, size-tool for resize // bar private static final Cursor CURSOR_SIZENS = CursorCache.getCursor(SWT.CURSOR_SIZENS); private static final Cursor CURSOR_HAND = CursorCache.getCursor(SWT.CURSOR_HAND); private int mResizeBarSize; private Rectangle mBounds; private Rectangle mMoveBar; private List mButtons; private int mButtonHeight; private CustomButton mSelectedButton; private boolean mMouseIsDown; private int mStartY = 0; private int mHiddenButtons = 0; private ToolbarComposite mToolBarComposite; // beats the built-in double buffering via SWT.DOUBLE_BUFFER private boolean mEnableDoubleBuffering = true; private boolean mCreated; private int mInvoluntaryButtonLevel = -1; private List mHidden; private Composite mParent; private int mColorTheme = IColorManager.SKIN_AUTO_DETECT; private IColorManager mColorManager; private List mButtonListeners; private ISettings mSettings; private ILanguageSettings mLanguage; private List mMenuListeners; /** * Creates a new ButtonComposite. Add buttons using the addButton(...) * method call. * * @param parent Parent composite * @param style Composite style, SWT.NO_BACKGROUND will be appended to the * style. */ public CollapsibleButtons(Composite parent, int style) { super(parent, checkStyle(style)); this.mParent = parent; init(); } /** * Creates a new ButtonComposite with a given language manager. * * @param parent Parent composite * @param style style * @param language Language manager */ public CollapsibleButtons(Composite parent, int style, ILanguageSettings language) { this(parent, style, IColorManager.SKIN_AUTO_DETECT, null, null, language); } /** * Creates a new ButtonComposite with a given settings and language manager. * * @param parent Parent composite * @param style style * @param settings Settings manager * @param language Language manager */ public CollapsibleButtons(Composite parent, int style, ISettings settings, ILanguageSettings language) { this(parent, style, IColorManager.SKIN_AUTO_DETECT, null, settings, language); } /** * Creates a new ButtonComposite. Add buttons using the addButton(...) * method call. * * By default, unless you set a theme, the theme will be read from whatever * the active color scheme is in Windows XP. If you are using a custom theme * and the color scheme cannot be determined, the fall-back will be the * Windows XP Blue theme. * * NOTE: If you want the Office 2007 theme, you have to set it manually as * there is no way to guess if you have office 2007 installed or 2005. * * @param parent Parent composite * @param style Composite style, SWT.NO_BACKGROUND will be appended to the * style * @param theme IColorManager.STYLE_ */ public CollapsibleButtons(Composite parent, int style, int theme) { super(parent, checkStyle(style)); this.mColorTheme = theme; this.mParent = parent; init(); } /** * Creates a new ButtonComposite. Add buttons using the addButton(...) * method call. * * @param parent Parent composite * @param style Composite style, SWT.NO_BACKGROUND will be appended to the * style * @param colorManager IColorManager implementation. Set to null to use the * default */ public CollapsibleButtons(Composite parent, int style, IColorManager colorManager) { super(parent, checkStyle(style)); this.mParent = parent; init(); } /** * Creates a new ButtonComposite. Add buttons using the addButton(...) * method call. * * By default, unless you set a theme, the theme will be read from whatever * the active color scheme is in Windows XP. If you are using a custom theme * and the color scheme cannot be determined, the fall-back will be the * Windows XP Blue theme. * * NOTE: If you want the Office 2007 theme, you have to set it manually as * there is no way to guess if you have office 2007 installed or 2005. * * @param parent Parent composite * @param style Composite style, SWT.NO_BACKGROUND will be appended to the * style * @param theme IColorManager.STYLE_ * @param colorManager IColorManager implementation. Set to null to use the * default */ public CollapsibleButtons(Composite parent, int style, int theme, IColorManager colorManager) { super(parent, checkStyle(style)); this.mColorTheme = theme; this.mParent = parent; init(); } /** * Creates a new ButtonComposite. Add buttons using the addButton(...) * method call. * * By default, unless you set a theme, the theme will be read from whatever * the active color scheme is in Windows XP. If you are using a custom theme * and the color scheme cannot be determined, the fall-back will be the * Windows XP Blue theme. * * NOTE: If you want the Office 2007 theme, you have to set it manually as * there is no way to guess if you have office 2007 installed or 2005. * * @param parent Parent composite * @param style Composite style, SWT.NO_BACKGROUND will be appended to the * style * @param theme IColorManager.STYLE_ * @param colorManager IColorManager implementation. Set to null to use the * default * @param settings ISettings implementation. Set to null to use the default * @param language ILanguage implementations. Set to null to use the * default. */ public CollapsibleButtons(Composite parent, int style, int theme, IColorManager colorManager, ISettings settings, ILanguageSettings language) { super(parent, checkStyle(style)); this.mColorTheme = theme; this.mColorManager = colorManager; this.mParent = parent; this.mSettings = settings; this.mLanguage = language; init(); } private static int checkStyle(int style) { int mask = SWT.BORDER | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.MULTI | SWT.NO_FOCUS | SWT.CHECK | SWT.VIRTUAL; int newStyle = style & mask; newStyle |= SWT.NO_BACKGROUND; return newStyle; } /** * Adds a new IButtonListener listener that will report clicks and other * events. * * @param listener IButtonListener */ public void addButtonListener(IButtonListener listener) { checkWidget(); if (!mButtonListeners.contains(listener)) mButtonListeners.add(listener); } /** * Removes an IButtonListener * * @param listener IButtonListener */ public void removeButtonListener(IButtonListener listener) { checkWidget(); mButtonListeners.remove(listener); } private void init() { // we need one, or *crash* if (mColorManager == null) mColorManager = new DefaultColorManager(mColorTheme); // same here if (mSettings == null) mSettings = new DefaultSettings(); if (mLanguage == null) mLanguage = new DefaultLanguageManager(); // outlook 2007 specific if (mColorTheme == IColorManager.SKIN_OFFICE_2007) mResizeBarSize = mSettings.getOutlook2007ResizeBarSize(); else mResizeBarSize = mSettings.getOutlook2005ResizeBarSize(); mButtonHeight = mSettings.getButtonHeight(); mMenuListeners = new ArrayList(); mButtons = new ArrayList(); mHidden = new ArrayList(); mButtonListeners = new ArrayList(); // this lets us auto-fit the buttons to the aviaiable space when the // parent composite is resized. // Outlook does the same thing when the buttons don't have enough space. // We hide 1 button per call, // which should be enough as the next call will to 99.9% certainty in // less than 31 pixels (or whatever the button size is). // when the control is manually resized we reset the invuluntary size as // a "new size" has been picked, and that's // the starting point for the next invuluntary size if any. Confusing? // Just try it then look at the code. mParent.addControlListener(new ControlListener() { public void controlMoved(ControlEvent event) { } public void controlResized(ControlEvent event) { int availableHeight = mParent.getClientArea().height; int neededHeight = getBounds().height; if (availableHeight < neededHeight) { if (mInvoluntaryButtonLevel == -1) { mInvoluntaryButtonLevel = getNumVisibleButtons(); } hideNextButton(); } if (mInvoluntaryButtonLevel != -1) { if (availableHeight - mButtonHeight > neededHeight) { if (getNumVisibleButtons() < mInvoluntaryButtonLevel) { showNextButton(); } } } } }); addPaintListener(new PaintListener() { public void paintControl(PaintEvent event) { repaint(event); } }); addFocusListener(new FocusListener() { public void focusGained(FocusEvent event) { } public void focusLost(FocusEvent event) { redraw(); } }); addMouseTrackListener(new MouseTrackAdapter() { public void mouseEnter(MouseEvent event) { } public void mouseExit(MouseEvent event) { setCursor(null); } public void mouseHover(MouseEvent event) { } }); addMouseListener(this); addMouseMoveListener(new MouseMoveListener() { public void mouseMove(MouseEvent event) { Point p = toDisplay(new Point(event.x, event.y)); if (!mMouseIsDown) { if (isInside(event.x, event.y, mMoveBar)) { if (mSettings.allowButtonResizing()) { setCursor(CURSOR_SIZENS); } } else { setCursor(null); } } if (mMouseIsDown) { // reset the "forced size" value, as we resized it to pick // what size we wanted mInvoluntaryButtonLevel = -1; int diff = p.y - mStartY; if (diff > mButtonHeight) { // ensures bar doesn't get smaller unless mouse pointer // is south of the move-bar if (event.y < mMoveBar.y) { return; } hideNextButton(); mStartY = p.y; } else { if (Math.abs(diff) > mButtonHeight) { showNextButton(); mStartY = p.y; } } } } }); setLayout(new VerticalLayout()); } private void repaint(PaintEvent event) { GC gc = event.gc; if (mCreated && mEnableDoubleBuffering) { try { Image buffer = new Image(Display.getDefault(), super.getBounds()); GC gc2 = new GC(buffer); drawOntoGC(gc2); // transfer the image buffer onto this canvas // just drawImage(buffer, w, h) didn't work, so we do the whole // source transfer call Rectangle b = getBounds(); gc.drawImage(buffer, 0, 0, b.width, b.height, 0, 0, b.width, b.height); // dispose the buffer, very important or we'll run out of // address space for buffered images buffer.dispose(); gc2.dispose(); } catch (IllegalArgumentException iea) { // seems to come here for some reason when we switch phases // while the gantt chart is being viewed, I'm not sure why // but no time to figure it out for the demo.. so instead of // buffering, just draw it onto the GC drawOntoGC(gc); } } else { drawOntoGC(gc); mCreated = true; } } private void drawOntoGC(GC gc) { gc.setBackground(mColorManager.getBorderColor()); gc.fillRectangle(getClientArea()); mBounds = super.getBounds(); gc.setBackground(mColorManager.getDarkResizeColor()); gc.setForeground(mColorManager.getLightResizeColor()); gc.fillGradientRectangle(0, 0, mBounds.width, mResizeBarSize, true); mMoveBar = new Rectangle(0, 0, mBounds.width, mResizeBarSize); // office 2007 draws a 1 pixel around the resize bar, let's do that too if (mColorManager.getTheme() == IColorManager.SKIN_OFFICE_2007) { // top line inside is white, rest is gradient down to the next dark // color that is the border gc.setForeground(IColorManager.white); gc.drawLine(0, 0, mBounds.width, 0); // do the gradient gc.setBackground(mColorManager.getDarkResizeColor()); gc.setForeground(mColorManager.getLightResizeColor()); gc.fillGradientRectangle(0, 2, mBounds.width, mResizeBarSize - 2, true); gc.setForeground(mColorManager.getBorderColor()); gc.drawLine(0, 0, mBounds.width, 0); gc.drawLine(0, mResizeBarSize - 1, mBounds.width, mResizeBarSize - 1); if (mSettings.drawBorder()) { gc.drawLine(0, 0, 0, mResizeBarSize); gc.drawLine(mBounds.width - 1, 0, mBounds.width - 1, mResizeBarSize); } } drawMarkers(gc); } private void drawMarkers(GC gc) { int numMarkers; if (mColorManager.getTheme() == IColorManager.SKIN_OFFICE_2007) numMarkers = mSettings.getOutlook2007ResizeDotNumber(); else numMarkers = mSettings.getOutlook2005ResizeDotNumber(); int start = (mBounds.width / 2) - numMarkers * 2; int extra = 0; // -1 is to align int y = (mResizeBarSize / 2) - 1; if (y < 0) y = 0; for (int i = 0; i < numMarkers; i++) { drawMarker(gc, start + extra, y); extra += 4; } } // draws a squared "shaded" marker on the resize bar private void drawMarker(GC gc, int x, int y) { gc.setBackground(mColorManager.getDotDarkColor()); gc.fillRectangle(x, y, 2, 2); gc.setBackground(mColorManager.getDotMiddleColor()); gc.fillRectangle(x + 1, y + 1, 2, 2); gc.setBackground(mColorManager.getDotLightColor()); gc.fillRectangle(x + 1, y + 1, 1, 1); } public void mouseMove(MouseEvent event) { } /** * Returns the number of currently visible buttons. * * @return Number of visible buttons */ public int getNumVisibleButtons() { checkWidget(); int num = 0; for (int i = 0; i < mButtons.size(); i++) { CustomButton b = (CustomButton) mButtons.get(i); if (b.isVisible()) num++; } return num; } /** * Hides the button from the list and the toolbar. * * @param button Button to hide */ public void permanentlyHideButton(CustomButton button) { checkWidget(); if (mHidden.contains(button)) return; mHidden.add(button); for (int i = 0; i < mButtons.size(); i++) { CustomButton b = (CustomButton) mButtons.get(i); if (b == button) { if (b.isVisible()) { b.setVisible(false); // Don't redraw until stuff is laid out, that way, no // toolbar ghosting! mHiddenButtons++; setRedraw(false); // parent needs to re-adjust it's size, which in turn // adjusts our size via (true) as that forces // children (us) to resize. getParent().layout(true); setRedraw(true); } // mToolBarComposite.removeItem(b); mToolBarComposite.hideButton(b); mToolBarComposite.redraw(); break; } } } /** * Un-hides a button that has been hidden from toolbar and ButtonComposite * view. * * @param button Button to show */ public void permanentlyShowButton(CustomButton button) { checkWidget(); mHidden.remove(button); for (int i = 0; i < mButtons.size(); i++) { CustomButton b = (CustomButton) mButtons.get(i); if (b == button) { if (!b.isVisible()) { b.setVisible(true); mHiddenButtons--; // Don't redraw until stuff is laid out, that way, no // toolbar ghosting! setRedraw(false); // parent needs to re-adjust it's size, which in turn // adjusts our size via (true) as that forces // children (us) to resize. getParent().layout(true); setRedraw(true); } mToolBarComposite.removeItem(b); break; } } } /** * Hides the given button (and adds it to the toolbar). * * @param button Button to hide */ public void hideButton(CustomButton button) { checkWidget(); for (int i = 0; i < mButtons.size(); i++) { CustomButton b = (CustomButton) mButtons.get(i); if (b == button) { // if (label.isVisible() == true) { b.setVisible(false); // Don't redraw until stuff is laid out, that way, no toolbar // ghosting! mHiddenButtons++; setRedraw(false); // parent needs to re-adjust it's size, which in turn adjusts // our size via (true) as that forces // children (us) to resize. getParent().layout(true); setRedraw(true); // } mToolBarComposite.addItem(b); break; } } } /** * Shows the given button (and removes it from the toolbar). * * @param button Button to show */ public void showButton(CustomButton button) { checkWidget(); for (int i = 0; i < mButtons.size(); i++) { CustomButton b = (CustomButton) mButtons.get(i); if (b == button) { if (!b.isVisible()) { b.setVisible(true); mHiddenButtons--; // Don't redraw until stuff is laid out, that way, no // toolbar ghosting! setRedraw(false); // parent needs to re-adjust it's size, which in turn // adjusts our size via (true) as that forces // children (us) to resize. getParent().layout(true); setRedraw(true); } mToolBarComposite.addItem(b); mToolBarComposite.redraw(); break; } } } /** * If a button is permanently hidden or permanently shown. * * @param button CustomButton to check * @return true or false */ public boolean isVisible(CustomButton button) { checkWidget(); return !mHidden.contains(button); } /** * Hides the next button furthest down in the list. If there are no more * buttons left to hide, nothing will happen. * */ public void hideNextButton() { checkWidget(); if (!mSettings.allowButtonResizing()) { return; } for (int i = (mButtons.size() - 1); i >= 0; i--) { CustomButton b = (CustomButton) mButtons.get(i); if (mHidden.contains(b)) continue; if (b.isVisible()) { mHiddenButtons++; b.setVisible(false); // laying out the parent with true forces us to layout too, so // don't overdo it by calling layout(true) locally setRedraw(false); getParent().layout(true); setRedraw(true); mToolBarComposite.addItem(b); break; } } } /** * Should you ever need to force a re-layout of the composite, this is the * method to call. It is not recommended to be used. * */ public void forceLayoutUpdate() { checkWidget(); getParent().layout(true); } /** * Shows the next button from the list of buttons that are currently hidden. * If there are no more buttons hiding, nothing will happen. * */ public void showNextButton() { checkWidget(); if (!mSettings.allowButtonResizing()) { return; } for (int i = 0; i < mButtons.size(); i++) { CustomButton b = (CustomButton) mButtons.get(i); if (mHidden.contains(b)) continue; if (!b.isVisible()) { mHiddenButtons--; b.setVisible(true); // Don't redraw until stuff is laid out, that way, no toolbar // ghosting! setRedraw(false); // parent needs to re-adjust it's size, which in turn adjusts // our size via (true) as that forces // children (us) to resize. getParent().layout(true); setRedraw(true); mToolBarComposite.removeItem(b); break; } } } // rectangle intersection, the easier way private boolean isInside(int x, int y, Rectangle rect) { if (rect == null) return false; return x >= rect.x && y >= rect.y && x <= (rect.x + rect.width) && y <= (rect.y + rect.height); } /** * Adds a button to the composite. Button will be added at the bottom of any * previously existing buttons. * * @param name Text that should be displayed on button. May be null. * @param toolTip Tooltip that is displayed when mouse moves over both * button and tool bar icon. Recommended null. * @param bigImage Image displayed on the button. Ideally 24x24 pixels * transparent PNG image. * @param toolbarImage Image displayed on the toolbar and on any menu items. * Ideally 16x16 pixels transparent GIF image. * @return CustomButton for further pre-launch modification. */ public CustomButton addButton(String name, String toolTip, Image bigImage, Image toolbarImage) { checkWidget(); return addButton(name, toolTip, bigImage, toolbarImage, false); } // _addButton private CustomButton addButton(String name, String toolTip, Image bigImage, Image toolbarImage, boolean selected) { checkWidget(); CustomButton cb = new CustomButton(this, SWT.FLAT, name, bigImage, toolbarImage, toolTip, mSettings); cb.addMouseListener(this); cb.addMouseTrackListener(this); mButtons.add(cb); if (mToolBarComposite == null) mToolBarComposite = new ToolbarComposite(this, SWT.NONE); mParent.redraw(); mParent.layout(); reindexButtons(); return cb; } private void reindexButtons() { for (int i = 0; i < mButtons.size(); i++) { ((CustomButton)mButtons.get(i)).setNumber(i); } } public void mouseDoubleClick(MouseEvent event) { checkWidget(); } public void mouseDown(MouseEvent event) { checkWidget(); Point p = toDisplay(new Point(event.x, event.y)); mStartY = p.y; mMouseIsDown = true; if (event.widget instanceof CustomButton) { CustomButton cb = (CustomButton) event.widget; if (event.button == 1) { if (mSelectedButton != null && cb.equals(mSelectedButton)) return; for (int i = 0; i < mButtonListeners.size(); i++) { IButtonListener inav = (IButtonListener) mButtonListeners.get(i); inav.buttonClicked(cb, event); } selectButton(cb); } } } public void mouseUp(MouseEvent event) { checkWidget(); setCursor(null); mMouseIsDown = false; } public Point getSize() { checkWidget(); int bs = mButtons.size() - mHiddenButtons; int y = bs * (mButtonHeight + 1); if (mSettings.showToolBar()) { y += mButtonHeight; } y += mResizeBarSize; return new Point(super.getSize().x, y); } public void mouseEnter(MouseEvent event) { checkWidget(); if (event.widget instanceof CustomButton) { CustomButton cb = (CustomButton) event.widget; setCursor(CURSOR_HAND); cb.updateHover(true); for (int i = 0; i < mButtonListeners.size(); i++) { IButtonListener inav = (IButtonListener) mButtonListeners.get(i); inav.buttonEnter(cb, event); } } } public void mouseExit(MouseEvent event) { checkWidget(); if (event.widget instanceof CustomButton) { CustomButton cb = (CustomButton) event.widget; cb.updateHover(false); setCursor(null); for (int i = 0; i < mButtonListeners.size(); i++) { IButtonListener inav = (IButtonListener) mButtonListeners.get(i); inav.buttonExit(cb, event); } } } public void mouseHover(MouseEvent event) { checkWidget(); if (event.widget instanceof CustomButton) { CustomButton cb = (CustomButton) event.widget; for (int i = 0; i < mButtonListeners.size(); i++) { IButtonListener inav = (IButtonListener) mButtonListeners.get(i); inav.buttonHover(cb, event); } } } /** * Flags a button as selected and pretends it got clicked. * * @param button Button to select and click */ public void selectItemAndLoad(CustomButton button) { checkWidget(); selectItem(button); for (int i = 0; i < mButtonListeners.size(); i++) { IButtonListener inav = (IButtonListener) mButtonListeners.get(i); inav.buttonClicked(getSelection(), null); } } /** * Selects a specific CustomButton. * * @param button CustomButton to select */ public void selectItem(CustomButton button) { checkWidget(); for (int i = 0; i < mButtons.size(); i++) { CustomButton b = (CustomButton) mButtons.get(i); if (b == button) { selectButton(button); break; } } } /** * Deselects all buttons */ public void deselectAll() { if (mSelectedButton != null) { mSelectedButton.updateSelection(false); } mSelectedButton = null; mToolBarComposite.setSelectedItem(null); redraw(); } // selects a button private void selectButton(CustomButton button) { if (mSelectedButton != null) { if (mSelectedButton.equals(button)) return; // clear old selection mSelectedButton.updateSelection(false); } // set new selection button.updateSelection(true); mSelectedButton = button; mToolBarComposite.setSelectedItem(mSelectedButton); } /** * Returns the list of all buttons. * * @return List of buttons */ public List getItems() { checkWidget(); return mButtons; } /** * Returns the current selection, or null if none. * * @return Selected button. */ public CustomButton getSelection() { checkWidget(); return mSelectedButton; } /** * Returns the number of buttons in the list. * * @return Button count */ public int itemCount() { checkWidget(); return mButtons.size(); } /** * Returns the active color manager. * * @return IColorManager */ public IColorManager getColorManager() { checkWidget(); return mColorManager; } /** * Returns the current Settings manager. * * @return ISettings */ public ISettings getSettings() { checkWidget(); return mSettings; } /** * Returns the current Language settings manager. * * @return ILanguageSettings */ public ILanguageSettings getLanguageSettings() { checkWidget(); return mLanguage; } /** * Returns the toolbar composite. * * @return ToolbarComposite */ public ToolbarComposite getToolbarComposite() { checkWidget(); return mToolBarComposite; } /** * Adds a menu listener that is notified before and after the menu popup is shown. * * @param listener Listener to add */ public void addMenuListener(IMenuListener listener) { if (!mMenuListeners.contains(listener)) mMenuListeners.add(listener); } /** * Removes a menu listener. * * @param listener Listener to remove */ public void removeMenuListener(IMenuListener listener) { mMenuListeners.remove(listener); } /** * Removes all buttons. */ public void removeAllButtons() { checkWidget(); // remove them in reverse or we'll have some interesting issues for (int i = mButtons.size()-1; i >= 0; i--) { ((CustomButton) mButtons.get(i)).dispose(); } if (mToolBarComposite != null) mToolBarComposite.removeAll(); mButtonListeners.clear(); mButtons.clear(); mParent.redraw(); mParent.layout(); } /** * Same method that is called when {@link CustomButton#dispose()} is called. * * @param cb CustomButton to remove */ public void removeButton(CustomButton cb) { checkWidget(); remove(cb, true); } // internal remove of button void remove(CustomButton cb, boolean callDispose) { cb.removeMouseListener(this); cb.removeMouseTrackListener(this); mButtons.remove(cb); if (mToolBarComposite != null) mToolBarComposite.removeItem(cb); mParent.redraw(); mParent.layout(); if (callDispose) cb.dispose(); reindexButtons(); } List getMenuListeners() { return mMenuListeners; } // layout class that deals with the actual layout of the buttons, toolbar // and other drawn items class VerticalLayout extends Layout { public VerticalLayout() { } protected Point computeSize(Composite aComposite, int wHint, int hHint, boolean flushCache) { return getSize(); } protected void layout(final Composite aComposite, boolean flushCache) { int top = mResizeBarSize; int left = (mSettings.drawBorder() ? 1 : 0); if (mSettings.showToolBar()) { int toolTop = top; // calculate where toolbar goes first, causes less ghosting for (int i = 0; i < mButtons.size(); i++) { CustomButton button = (CustomButton) mButtons.get(i); if (!button.isVisible()) { continue; } toolTop += mButtonHeight + 1; } if (mToolBarComposite != null) mToolBarComposite.setBounds(left, toolTop, aComposite.getBounds().width - (mSettings.drawBorder() ? 2 : 0), mButtonHeight); } // now set the toolbars for (int i = 0; i < mButtons.size(); i++) { CustomButton button = (CustomButton) mButtons.get(i); if (!button.isVisible()) { continue; } button.setBounds(left, top, aComposite.getBounds().width - (mSettings.drawBorder() ? 2 : 0), mButtonHeight); top += mButtonHeight + 1; } } } }