/*******************************************************************************
* Copyright (c) 2008 Laurent Muller.
* 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:
* Laurent Muller - initial API and implementation
*******************************************************************************/
package nu.bibi.breadcrumb;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.forms.FormColors;
/**
* A breadcrumb viewer shows a the parent chain of its input element in a list.
* Each breadcrumb item of that list can be expanded and a sibling of the
* element presented by the breadcrumb item can be selected.
* <p>
* Content providers for breadcrumb viewers must implement the
* <code>ITreeContentProvider</code> interface.
* </p>
* <p>
* Label providers for breadcrumb viewers must implement the
* <code>ILabelProvider</code> interface.
* </p>
*/
public abstract class BreadcrumbViewer extends StructuredViewer {
private static final boolean IS_GTK = "gtk".equals(SWT.getPlatform()); //$NON-NLS-1$
private final Composite container;
private final ArrayList<BreadcrumbItem> items;
private final ArrayList<MenuDetectListener> menuDetectListeners;
private final ArrayList<IMenuSelectionListener> menuSelectionListeners;
private Image backgroundImage;
private BreadcrumbItem selectedItem;
private ILabelProvider toolTipLabelProvider;
private boolean rootVisible = false;
/**
* Creates a breadcrumb viewer under the given parent. The control is
* created using the SWT style bits <code>HORIZONTAL</code>. The viewer has
* no input, no content provider, and no label provider.
*
* @param parent
* the parent control.
*/
public BreadcrumbViewer(final Composite parent) {
this(parent, SWT.HORIZONTAL);
}
/**
* Creates a breadcrumb viewer under the given parent. The control is
* created using the given style bits. The viewer has no input, no content
* provider, and no label provider.
* <p>
* Allowed styles are one of:
* <ul>
* <li>SWT.NONE</li>
* <li>SWT.VERTICAL</li>
* <li>SWT.HORIZONTAL</li>
* </ul>
*
* @param parent
* the parent control.
* @param style
* SWT style bits.
*/
public BreadcrumbViewer(final Composite parent, final int style) {
items = new ArrayList<BreadcrumbItem>();
menuDetectListeners = new ArrayList<MenuDetectListener>();
menuSelectionListeners = new ArrayList<IMenuSelectionListener>();
container = new Composite(parent, style);
container.setBackgroundMode(SWT.INHERIT_DEFAULT);
// add listeners
container.addListener(SWT.Traverse, new Listener() {
public void handleEvent(final Event event) {
event.doit = true;
}
});
container.addListener(SWT.Resize, new Listener() {
public void handleEvent(final Event event) {
final int height = container.getClientArea().height;
// update background image
if (backgroundImage == null
|| backgroundImage.getBounds().height != height) {
if (backgroundImage != null) {
backgroundImage.dispose();
}
backgroundImage = createGradientImage(height, event.display);
container.setBackgroundImage(backgroundImage);
}
// update layout
refresh();
}
});
container.addListener(SWT.Dispose, new Listener() {
public void handleEvent(final Event event) {
if (backgroundImage != null) {
backgroundImage.dispose();
}
}
});
container.addListener(SWT.FocusIn, new Listener() {
public void handleEvent(final Event event) {
if (selectedItem != null) {
selectedItem.setHasFocus(true);
}
}
});
container.addListener(SWT.MouseDown, new Listener() {
public void handleEvent(final Event event) {
if (event.button == 1) {
setFocus();
}
}
});
hookControl(container);
// layout
int columns = 1000;
if ((SWT.VERTICAL & style) != 0) {
columns = 1;
}
final GridLayout gridLayout = new GridLayout(columns, false);
gridLayout.marginWidth = gridLayout.marginHeight = 0;
gridLayout.verticalSpacing = gridLayout.horizontalSpacing = 0;
container.setLayout(gridLayout);
}
/**
* Add the given listener to the set of listeners which will be informed
* when a context menu is requested for a breadcrumb item.
*
* @param listener
* the listener to add
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*/
public void addMenuDetectListener(final MenuDetectListener listener) {
if (listener == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
menuDetectListeners.add(listener);
}
/**
* Add the given listener to the set of listeners which will be informed
* when a selection of the drop down menu occurs.
*
* @param listener
* the listener to add
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*/
public void addMenuSelectionListener(final IMenuSelectionListener listener) {
if (listener == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
menuSelectionListeners.add(listener);
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.ContentViewer#getContentProvider()
*/
@Override
public ITreeContentProvider getContentProvider() {
return (ITreeContentProvider) super.getContentProvider();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.Viewer#getControl()
*/
@Override
public Control getControl() {
return container;
}
/**
* Returns the selection provider which provides the selection of the drop
* down currently opened or <code>null</code> if no drop down is open at the
* moment.
*
* @return the selection provider of the open drop down or <code>null</code>
* .
*/
public ISelectionProvider getDropDownSelectionProvider() {
for (final BreadcrumbItem item : items) {
if (item.isMenuShown()) {
return item.getDropDownSelectionProvider();
}
}
return null;
}
/**
* The Window used for the shown drop down menu or <code>null</code> if no
* drop down is shown at the moment.
*
* @return the drop downs window or <code>null</code>.
*/
public Window getDropDownWindow() {
for (final BreadcrumbItem item : items) {
if (item.isMenuShown()) {
return item.getDropDownWindow();
}
}
return null;
}
/**
* Returns the item at the given, zero-relative index in the receiver.
* Throws an exception if the index is out of range.
*
* @param index
* the index of the item to return.
* @return the item at the given index.
* @throws IndexOutOfBoundsException
* if the index is out of range (
* <tt>index < 0 || index >= getItemCount()</tt>).
*/
public BreadcrumbItem getItem(final int index) {
return items.get(index);
}
/**
* Returns the item at the given display-relative coordinates, or
* <code>null</code> if there is no item at that location.
*
* @param x
* horizontal coordinate
* @param y
* vertical coordinate
* @return the item, or <code>null</code> if there is no item at the given
* coordinates.
*/
@Override
public BreadcrumbItem getItem(final int x, final int y) {
return getItem(new Point(x, y));
}
/**
* Returns the item at the given point in the receiver or null if no such
* item exists. The point is in the coordinate system of the receiver.
*
* @param point
* the point used to locate the item.
* @return the item at the given point, or <code>null</code> if the point is
* not in a selectable item.
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the point is null</li>
* </ul>
*/
public BreadcrumbItem getItem(final Point point) {
if (point == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
for (final BreadcrumbItem item : items) {
if (item.getBounds().contains(point)) {
return item;
}
}
return null;
}
/**
* Returns the number of items contained in the receiver.
*
* @return the number of items.
*/
public int getItemCount() {
return items.size();
}
/**
* Returns a (possibly empty) array of items contained in the receiver.
* <p>
* Note: This is not the actual structure used by the receiver to maintain
* its list of items, so modifying the array will not affect the receiver.
* </p>
*
* @return the items.
*/
public BreadcrumbItem[] getItems() {
return items.toArray(new BreadcrumbItem[0]);
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.ContentViewer#getLabelProvider()
*/
@Override
public ILabelProvider getLabelProvider() {
return (ILabelProvider) super.getLabelProvider();
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.StructuredViewer#getSelection()
*/
@Override
public IStructuredSelection getSelection() {
return (IStructuredSelection) super.getSelection();
}
/**
* Returns the index of the first occurrence of the specified element, or -1
* if this does not contain the element.
*
* @param item
* the item to search for.
* @return the index of the first occurrence of the specified element, or -1
* if this does not contain the element.
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* </ul>
*/
public int indexOf(final BreadcrumbItem item) {
if (item == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
return items.indexOf(item);
}
/**
* Gets a value indicating if any of the items in the viewer is expanded.
*
* @return <code>true</code> if any of the items in the viewer is expanded.
*/
public boolean isDropDownOpen() {
for (final BreadcrumbItem item : items) {
if (item.isMenuShown()) {
return true;
}
}
return false;
}
/**
* Returns the root visible.
*
* @return <code>true</code> if the root item is visible, <code>false</code>
* otherwise.
*/
public boolean isRootVisible() {
return rootVisible;
}
/**
* Display the drop-down menu for the given element
*
* @param element
* the element for open the drop-down menu.
*/
public void openDropDownMenu(final Object element) {
final BreadcrumbItem item = (BreadcrumbItem) doFindItem(element);
if (item != null) {
item.openDropDownMenu();
}
}
/**
* Remove the given listener from the set of menu detect listeners. Does
* nothing if the listener is not element of the set.
*
* @param listener
* the listener to remove.
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*/
public void removeMenuDetectListener(final MenuDetectListener listener) {
if (listener == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
menuDetectListeners.remove(listener);
}
/**
* Remove the given listener from the set of menu selection listeners. Does
* nothing if the listener is not element of the set.
*
* @param listener
* the listener to remove.
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
*/
public void removeMenuSelectionListener(
final IMenuSelectionListener listener) {
if (listener == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
menuSelectionListeners.remove(listener);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object)
*/
@Override
public void reveal(final Object element) {
// all elements are always visible
}
/**
* Causes the receiver to have the <em>keyboard focus</em>, such that all
* keyboard events will be delivered to it. Focus reassignment will respect
* applicable platform constraints.
*/
public void setFocus() {
container.setFocus();
if (selectedItem != null) {
selectedItem.setHasFocus(true);
} else {
final int size = items.size();
if (size == 0) {
return;
}
BreadcrumbItem item = items.get(size - 1);
if (item.element == null) {
if (size < 2) {
return;
}
item = items.get(size - 2);
}
item.setHasFocus(true);
}
}
/**
* Sets the root item visibility.
*
* @param rootVisible
* <code>true</code> to set the root item visible,
* <code>false</code> to hide.
*/
public void setRootVisible(final boolean rootVisible) {
if (this.rootVisible == rootVisible) {
return;
}
this.rootVisible = rootVisible;
if (items.size() != 0) {
final BreadcrumbItem item = items.get(0);
item.setDetailsVisible(rootVisible);
container.layout(true, true);
if (rootVisible || selectedItem == null) {
return;
}
// root selected ?
if (selectedItem == item || selectedItem.equals(item)) {
if (items.size() > 1) {
selectItem(items.get(1));
}
}
}
}
/**
* The tool tip to use for the tool tip labels. <code>null</code> if the
* viewers label provider should be used.
*
* @param toolTipLabelProvider
* the label provider for the tool tips or <code>null</code>.
*/
public void setToolTipLabelProvider(
final ILabelProvider toolTipLabelProvider) {
this.toolTipLabelProvider = toolTipLabelProvider;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#assertContentProviderType(org.eclipse.jface.viewers.IContentProvider)
*/
@Override
protected void assertContentProviderType(final IContentProvider provider) {
super.assertContentProviderType(provider);
Assert.isTrue(provider instanceof ITreeContentProvider);
}
/**
* Configure the given drop down viewer. The given input is used for the
* viewers input. Clients must at least set the label providr and the
* content provider for the viewer.
*
* @param viewer
* the viewer to configure
* @param input
* the input for the viewer.
*/
protected abstract void configureDropDownViewer(TreeViewer viewer,
Object input);
/**
* Creates a new instance of a breadcrumb item.
*
* @return a new instance of a breadcrumb item.
*/
protected BreadcrumbItem createItem() {
final BreadcrumbItem item = new BreadcrumbItem(this, container);
item.setLabelProvider(getLabelProvider());
item.setContentProvider(getContentProvider());
if (toolTipLabelProvider != null) {
item.setToolTipLabelProvider(toolTipLabelProvider);
} else {
item.setToolTipLabelProvider(getLabelProvider());
}
return item;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
*/
@Override
protected Widget doFindInputItem(final Object element) {
if (element == null) {
return null;
}
if (element == getInput() || element.equals(getInput())) {
return doFindItem(element);
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
*/
@Override
protected Widget doFindItem(final Object element) {
if (element == null) {
return null;
}
for (final BreadcrumbItem item : items) {
if (element == item.element
|| element.equals(item.element)) {
return item;
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#doUpdateItem(org.eclipse.swt.widgets.Widget, java.lang.Object, boolean)
*/
@Override
protected void doUpdateItem(final Widget widget, final Object element,
final boolean fullMap) {
if (widget instanceof BreadcrumbItem) {
final BreadcrumbItem item = (BreadcrumbItem) widget;
// remember element we are showing
if (fullMap) {
associate(element, item);
} else {
final Object data = item.element;
if (data != null) {
unmapElement(data, item);
}
item.element = element;
mapElement(element, item);
}
// update
item.refresh();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#getRoot()
*/
@Override
protected Object getRoot() {
if (items.isEmpty()) {
return null;
}
return items.get(0).element;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
*/
@Override
protected List<?> getSelectionFromWidget() {
if (selectedItem == null
|| selectedItem.element == null) {
return Collections.EMPTY_LIST;
}
final ArrayList<Object> list = new ArrayList<Object>();
list.add(selectedItem.element);
return Collections.unmodifiableList(list);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,java.lang.Object)
*/
@Override
protected void inputChanged(final Object fInput, final Object oldInput) {
if (container.isDisposed()) {
return;
}
disableRedraw();
try {
if (items.size() > 0) {
final BreadcrumbItem last = items.get(items.size() - 1);
last.setIsLastItem(false);
}
final int lastIndex = buildItemChain(fInput);
if (lastIndex > 0) {
final BreadcrumbItem last = items.get(lastIndex - 1);
last.setIsLastItem(true);
}
while (lastIndex < items.size()) {
final BreadcrumbItem item = items.remove(items.size() - 1);
if (item == selectedItem) {
selectItem(null);
}
if (item.element != null) {
unmapElement(item.element);
}
item.dispose();
}
updateSize();
container.layout(true, true);
} finally {
enableRedraw();
}
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
*/
@Override
protected void internalRefresh(final Object element) {
disableRedraw();
try {
final BreadcrumbItem item = (BreadcrumbItem) doFindItem(element);
if (item == null) {
for (final BreadcrumbItem current : items) {
current.refresh();
}
} else {
item.refresh();
}
if (updateSize()) {
container.layout(true, true);
}
} finally {
enableRedraw();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List, boolean)
*/
@Override
@SuppressWarnings("unchecked")
protected void setSelectionToWidget(final List list, final boolean reveal) {
boolean focused = container.isFocusControl();
BreadcrumbItem focusItem = null;
for (final BreadcrumbItem item : items) {
if (item.hasFocus()) {
focusItem = item;
}
item.setSelected(false);
focused = focused | item.isFocusControl();
}
if (list == null) {
return;
}
selectedItem = null;
for (final Iterator<?> iter = list.iterator(); iter.hasNext();) {
final Object element = iter.next();
final BreadcrumbItem item = (BreadcrumbItem) doFindItem(element);
if (item != null) {
selectedItem = item;
selectedItem.setSelected(true);
if (item == focusItem) {
item.setHasFocus(true);
}
}
}
if ((focused || reveal) && focusItem == null && selectedItem != null) {
selectedItem.setHasFocus(true);
}
}
/**
* Generates the parent chain of the given element.
*
* @param element
* element to build the parent chain for
* @return the first index of an item in fBreadcrumbItems which is not part
* of the chain
*/
private int buildItemChain(final Object element) {
if (element == null) {
return 0;
}
final ITreeContentProvider contentProvider = getContentProvider();
final Object parent = contentProvider.getParent(element);
final int index = buildItemChain(parent);
BreadcrumbItem item;
if (index < items.size()) {
item = items.get(index);
if (item.element != null) {
unmapElement(item.element);
}
} else {
item = createItem();
items.add(item);
}
if (equals(element, item.element)) {
update(element, null);
} else {
item.element = element;
item.refresh();
}
if (parent == null) {
// don't show the models root
item.setDetailsVisible(rootVisible);
}
mapElement(element, item);
return index + 1;
}
private Color createColor(final int color1, final int color2,
final int ratio, final Display display) {
final RGB rgb1 = display.getSystemColor(color1).getRGB();
final RGB rgb2 = display.getSystemColor(color2).getRGB();
final RGB blend = FormColors.blend(rgb2, rgb1, ratio);
return new Color(display, blend);
}
/**
* The image to use for the breadcrumb background as specified in
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=221477
*
* @param height
* the height of the image to create
* @param display
* the current display
* @return the image for the breadcrumb background
*/
private Image createGradientImage(final int height, final Display display) {
final int width = 10;
final Image image = new Image(display, width, height);
final GC gc = new GC(image);
final Color colorC = createColor(SWT.COLOR_WIDGET_BACKGROUND,
SWT.COLOR_LIST_BACKGROUND, 35, display);
final Color colorD = createColor(SWT.COLOR_WIDGET_BACKGROUND,
SWT.COLOR_LIST_BACKGROUND, 45, display);
final Color colorE = createColor(SWT.COLOR_WIDGET_BACKGROUND,
SWT.COLOR_LIST_BACKGROUND, 80, display);
final Color colorF = createColor(SWT.COLOR_WIDGET_BACKGROUND,
SWT.COLOR_LIST_BACKGROUND, 70, display);
final Color colorG = createColor(SWT.COLOR_WIDGET_BACKGROUND,
SWT.COLOR_WHITE, 45, display);
final Color colorH = createColor(SWT.COLOR_WIDGET_NORMAL_SHADOW,
SWT.COLOR_LIST_BACKGROUND, 35, display);
try {
drawLine(width, 0, colorC, gc);
drawLine(width, 1, colorC, gc);
gc.setForeground(colorD);
gc.setBackground(colorE);
gc.fillGradientRectangle(0, 2, width, 2 + 8, true);
gc.setBackground(colorE);
gc.fillRectangle(0, 2 + 9, width, height - 4);
drawLine(width, height - 3, colorF, gc);
drawLine(width, height - 2, colorG, gc);
drawLine(width, height - 1, colorH, gc);
} finally {
gc.dispose();
colorC.dispose();
colorD.dispose();
colorE.dispose();
colorF.dispose();
colorG.dispose();
colorH.dispose();
}
return image;
}
/**
* Disable redrawing of the breadcrumb.
* <p>
* <strong>A call to this method must be followed by a call to
* {@link #enableRedraw()}</strong>
* </p>
*/
private void disableRedraw() {
if (IS_GTK) {
return;
}
container.setRedraw(false);
}
private void drawLine(final int width, final int position,
final Color color, final GC gc) {
gc.setForeground(color);
gc.drawLine(0, position, width, position);
}
/**
* Enable redrawing of the breadcrumb.
*/
private void enableRedraw() {
if (IS_GTK) {
return;
}
container.setRedraw(true);
}
/**
* Returns the current width of all items in the list.
*
* @return the width of all items.
*/
private int getCurrentWidth() {
int result = 2;
for (final BreadcrumbItem item : items) {
result += item.getCurrentWidth();
}
return result;
}
/**
* Update the size of the items such that all items are visible, if
* possible.
*
* @return true if any item has changed, false otherwise.
*/
private boolean updateSize() {
boolean requiresLayout = false;
int currentWidth = getCurrentWidth();
final int width = container.getClientArea().width;
if (currentWidth > width) {
// hide texts
int index = 0;
while (currentWidth > width && index < items.size() - 1) {
final BreadcrumbItem viewer = items.get(index);
if (viewer.isShowText()) {
viewer.setTextVisible(false);
currentWidth = getCurrentWidth();
requiresLayout = true;
}
index++;
}
} else if (currentWidth < width) {
// display texts
int index = items.size() - 1;
while (currentWidth < width && index >= 0) {
final BreadcrumbItem item = items.get(index);
if (!item.isShowText()) {
item.setTextVisible(true);
currentWidth = getCurrentWidth();
if (currentWidth > width) {
item.setTextVisible(false);
index = 0;
} else {
requiresLayout = true;
}
}
index--;
}
}
return requiresLayout;
}
/**
* Set selection, if possible, to the next or previous element.
*
* @param next
* <code>true</code> for the next element, <code>false</code> for
* the previous element.
*/
void doTraverse(final boolean next) {
if (selectedItem == null) {
return;
}
final int index = items.indexOf(selectedItem);
if (next) {
// last ?
if (index == getItemCount() - 1) {
final BreadcrumbItem current = items.get(index);
final ITreeContentProvider contentProvider = getContentProvider();
if (!contentProvider.hasChildren(current
.element)) {
return;
}
current.openDropDownMenu();
} else {
// next
selectItem(items.get(index + 1));
}
} else {
switch (index) {
case 0:
items.get(0).openDropDownMenu();
return;
case 1:
if (!rootVisible) {
items.get(0).openDropDownMenu();
return;
}
break;
}
selectItem(items.get(Math.max(0, index - 1)));
}
}
/**
* Notify all double click listeners
*/
void fireDoubleClick() {
fireDoubleClick(new DoubleClickEvent(this, getSelection()));
}
/**
* A context menu has been requested for the selected breadcrumb item.
*
* @param event
* the event issued the menu detection
*/
void fireMenuDetect(final Event e) {
final MenuDetectEvent event = new MenuDetectEvent(e);
for (final MenuDetectListener listener : menuDetectListeners) {
SafeRunnable.run(new SafeRunnable() {
public void run() {
listener.menuDetected(event);
}
});
}
}
/**
* Fire event for the given element that was selected from a drop down menu.
*
* @param element
* the selected element.
*/
void fireMenuSelection(final Object element) {
final MenuSelectionEvent event = new MenuSelectionEvent(this,
new StructuredSelection(element));
for (final IMenuSelectionListener listener : menuSelectionListeners) {
SafeRunnable.run(new SafeRunnable() {
public void run() {
listener.menuSelect(event);
}
});
}
// fireOpen(new OpenEvent(this, new StructuredSelection(element)));
}
/**
* Notify all open listeners with the current selection.
*/
void fireOpen() {
fireOpen(new OpenEvent(this, getSelection()));
}
/**
* Set a single selection to the given item, <code>null</code> to deselect
* all.
*
* @param item
* the item to select or <code>null</code>
*/
void selectItem(final BreadcrumbItem item) {
if (selectedItem != null) {
selectedItem.setSelected(false);
}
selectedItem = item;
setSelectionToWidget(getSelection(), false);
if (item != null) {
setFocus();
} else {
for (final BreadcrumbItem current : items) {
current.setHasFocus(false);
}
}
fireSelectionChanged(new SelectionChangedEvent(this, getSelection()));
}
/**
* Set a single selection to the given item, <code>-1</code> to deselect
* all.
*
* @param index
* the item index to select or <code>-1</code>
*/
void selectItem(final int index) {
if (index < 0 || index >= items.size()) {
selectItem(null);
} else {
selectItem(items.get(index));
}
}
}