/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * 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: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.navigation.ui.swt.component; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.riena.core.marker.IMarker; import org.eclipse.riena.navigation.IApplicationNode; import org.eclipse.riena.navigation.ISubApplicationNode; import org.eclipse.riena.navigation.listener.ApplicationNodeListener; import org.eclipse.riena.navigation.listener.SubApplicationNodeListener; import org.eclipse.riena.navigation.ui.swt.lnf.renderer.SubApplicationSwitcherRenderer; import org.eclipse.riena.ui.core.marker.DisabledMarker; import org.eclipse.riena.ui.core.marker.HiddenMarker; import org.eclipse.riena.ui.swt.facades.SWTFacade; import org.eclipse.riena.ui.swt.lnf.LnfKeyConstants; import org.eclipse.riena.ui.swt.lnf.LnfManager; /** * Control to switch between sub-applications. */ public class SubApplicationSwitcherWidget extends Canvas { private final List<SubApplicationItem> items; private TabSelector tabSelector; private MnemonicListener mnemonicListener; private PaintDelegation paintDelegation; private final Control control; private final ApplicationListener applicationListener; private final SubApplicationListener subApplicationListener; /** * Creates a new widget. * * @param parent * a composite control which will be the parent of the new * instance * @param style * the style of control to construct * @param application * the node of the application */ public SubApplicationSwitcherWidget(final Composite parent, final int style, final IApplicationNode application) { super(parent, style | SWT.DOUBLE_BUFFERED); control = this; items = new ArrayList<SubApplicationItem>(); applicationListener = new ApplicationListener(); subApplicationListener = new SubApplicationListener(); registerItems(application); SWTFacade.getDefault().createSubApplicationToolTip(this); addListeners(); } /** * Adds listeners to the widget. */ private void addListeners() { mnemonicListener = new MnemonicListener(); addTraverseListener(mnemonicListener); tabSelector = new TabSelector(); addMouseListener(tabSelector); paintDelegation = new PaintDelegation(); addPaintListener(paintDelegation); } /** * Removes all the listeners form the widget. */ private void removeListeners() { removePaintListener(paintDelegation); removeMouseListener(tabSelector); removeTraverseListener(mnemonicListener); } /** * Activates the Sub-Application of the given item. * * @param item * item to activate * @return {@code true} if the sub-application was activated; otherwise * {@code false} */ private boolean activateItem(final SubApplicationItem item) { if (isTabEnabled(item)) { item.getSubApplicationNode().activate(); redraw(); return true; } return false; } /** * This listener pay attention that this control is paint correct. */ private class PaintDelegation implements PaintListener { /** * {@inheritDoc} * <p> * Passes the bounds of the parent and the sub-application items to the * renderer and paints the widget. */ public void paintControl(final PaintEvent e) { final GC gc = e.gc; getRenderer().setBounds(getParent().getBounds()); getRenderer().setItems(getItems()); getRenderer().paint(gc, control); } } /** * After the selection of a sub-application it will be activated. */ private class TabSelector extends MouseAdapter { /** * {@inheritDoc} * <p> * Activates the selected sub-application */ @Override public void mouseDown(final MouseEvent e) { final SubApplicationItem item = getItem(new Point(e.x, e.y)); activateItem(item); } } /** * After entering a mnemonic the sub-application it will be activated. */ private final class MnemonicListener implements TraverseListener { public void keyTraversed(final TraverseEvent evt) { if (evt.detail == SWTFacade.TRAVERSE_MNEMONIC) { final SubApplicationItem item = getItem(evt.character); activateItem(item); } } } /** * Returns the sub-application at the given point. * * @param point * point over sub-application item * @return module item; or null, if not item was found */ public SubApplicationItem getItem(final Point point) { for (final SubApplicationItem item : getItems()) { if (item.getBounds().contains(point)) { return item; } } return null; } /** * Returns the sub-application with given mnemonic. * * @param Mnemonic * - * @return module item; or null, if not item was found */ private SubApplicationItem getItem(final char mnemonic) { String mnemonicStrg = "&" + mnemonic; //$NON-NLS-1$ mnemonicStrg = mnemonicStrg.toLowerCase(); for (final SubApplicationItem item : getItems()) { String label = item.getLabel(); if (label != null) { label = label.toLowerCase(); if (label.contains(mnemonicStrg)) { if (label.indexOf('&') == label.indexOf(mnemonicStrg)) { return item; } } } } return null; } /** * Returns whether the given item is enabled and also visible. * * @param item * sub-application item * @return {@code true} if item is enabled and visible; otherwise false. */ private boolean isTabEnabled(final SubApplicationItem item) { if (item == null) { return false; } if (!item.getMarkersOfType(HiddenMarker.class).isEmpty()) { return false; } if (!item.getMarkersOfType(DisabledMarker.class).isEmpty()) { return false; } return true; } /** * Returns the list of every registered item of a sub-application. * * @return list of items. */ private List<SubApplicationItem> getItems() { return items; } /** * Creates for every sub-application of the given application an item and * registers it. * * @param applicationModel * model of the application */ private void registerItems(final IApplicationNode applicationModel) { applicationModel.addListener(applicationListener); final List<ISubApplicationNode> subApps = applicationModel.getChildren(); for (final ISubApplicationNode subApp : subApps) { registerSubApplication(subApp); } } private void registerSubApplication(final ISubApplicationNode subApp) { subApp.addListener(subApplicationListener); final SubApplicationItem item = new SubApplicationItem(this, subApp); getItems().add(item); final IApplicationNode applicationNode = findApplicationNode(getItems()); if (applicationNode != null) { // honor the order of subapplicationNodes in the applicationNode orderItems(applicationNode); } } /** * Finds the {@link IApplicationNode} of the {@link ISubApplicationNode}s. * Consider that it is possible that this method returns null * * @param items * - the {@link SubApplicationItem}s holding the * {@link ISubApplicationNode}s * @return - the {@link IApplicationNode} as the root of the whole model */ private IApplicationNode findApplicationNode(final List<SubApplicationItem> items) { for (final SubApplicationItem item : items) { final ISubApplicationNode node = item.getSubApplicationNode(); if (node.getParent() != null) { return (IApplicationNode) node.getParent(); } } return null; } private void orderItems(final IApplicationNode appNode) { // use comparator to order the items honoring order of subapplicationNodes Collections.sort(getItems(), new SubApplicationItemComparator(appNode)); } private class SubApplicationItemComparator implements Comparator<SubApplicationItem> { private final IApplicationNode appNode; public SubApplicationItemComparator(final IApplicationNode appNode) { this.appNode = appNode; } public int compare(final SubApplicationItem item1, final SubApplicationItem item2) { return appNode.getIndexOfChild(item1.getSubApplicationNode()) < appNode.getIndexOfChild(item2 .getSubApplicationNode()) ? -1 : 1; } } private void unregisterSubApplication(final ISubApplicationNode subApp) { subApp.removeListener(subApplicationListener); SubApplicationItem itemToRemove = null; for (final SubApplicationItem item : getItems()) { if (item.getSubApplicationNode().equals(subApp)) { itemToRemove = item; break; } } if (itemToRemove != null) { getItems().remove(itemToRemove); } } /** * Returns the renderer of the switcher of the sub-applications. * * @return renderer of switcher of sub-applications */ private SubApplicationSwitcherRenderer getRenderer() { return (SubApplicationSwitcherRenderer) LnfManager.getLnf().getRenderer( LnfKeyConstants.SUB_APPLICATION_SWITCHER_RENDERER); } @Override public void dispose() { removeListeners(); super.dispose(); } private final class SubApplicationListener extends SubApplicationNodeListener { @Override public void markerChanged(final ISubApplicationNode source, final IMarker marker) { redraw(); } @Override public void disposed(final ISubApplicationNode source) { unregisterSubApplication(source); redraw(); } } private final class ApplicationListener extends ApplicationNodeListener { @Override public void childRemoved(final IApplicationNode source, final ISubApplicationNode childRemoved) { unregisterSubApplication(childRemoved); redraw(); } @Override public void childAdded(final IApplicationNode source, final ISubApplicationNode childAdded) { registerSubApplication(childAdded); redraw(); } } }