/******************************************************************************* * 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.views; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; import org.eclipse.riena.core.marker.IMarker; import org.eclipse.riena.core.wire.Wire; import org.eclipse.riena.internal.navigation.ui.swt.Activator; import org.eclipse.riena.navigation.IModuleGroupNode; import org.eclipse.riena.navigation.IModuleNode; import org.eclipse.riena.navigation.INavigationNode; import org.eclipse.riena.navigation.ISubApplicationNode; import org.eclipse.riena.navigation.ISubModuleNode; import org.eclipse.riena.navigation.NavigationNodeId; import org.eclipse.riena.navigation.listener.ModuleGroupNodeListener; import org.eclipse.riena.navigation.listener.ModuleNodeListener; import org.eclipse.riena.navigation.listener.NavigationTreeObserver; import org.eclipse.riena.navigation.listener.SubApplicationNodeListener; import org.eclipse.riena.navigation.listener.SubModuleNodeListener; import org.eclipse.riena.navigation.model.ModuleGroupNode; import org.eclipse.riena.navigation.model.ModuleNode; import org.eclipse.riena.navigation.ui.controllers.ModuleController; import org.eclipse.riena.navigation.ui.swt.facades.NavigationFacade; import org.eclipse.riena.navigation.ui.swt.lnf.renderer.EmbeddedBorderRenderer; import org.eclipse.riena.navigation.ui.swt.lnf.renderer.ModuleGroupRenderer; import org.eclipse.riena.navigation.ui.swt.lnf.renderer.SubModuleViewRenderer; import org.eclipse.riena.navigation.ui.swt.presentation.SwtViewProvider; import org.eclipse.riena.navigation.ui.swt.presentation.stack.TitlelessStackPresentation; import org.eclipse.riena.ui.core.marker.HiddenMarker; import org.eclipse.riena.ui.ridgets.listener.ISelectionListener; import org.eclipse.riena.ui.ridgets.listener.SelectionEvent; import org.eclipse.riena.ui.swt.facades.SWTFacade; import org.eclipse.riena.ui.swt.lnf.LnfKeyConstants; import org.eclipse.riena.ui.swt.lnf.LnfManager; import org.eclipse.riena.ui.swt.uiprocess.SwtUISynchronizer; import org.eclipse.riena.ui.swt.utils.SwtUtilities; public class NavigationViewPart extends ViewPart implements IModuleNavigationComponentProvider { public final static String ID = "org.eclipse.riena.navigation.ui.swt.views.navigationViewPart"; //$NON-NLS-1$ private static final Color NAVIGATION_BACKGROUND = LnfManager.getLnf().getColor(LnfKeyConstants.NAVIGATION_BACKGROUND); private IViewFactory viewFactory; private Composite parent; private ResizeListener resizeListener; private final List<ModuleGroupView> moduleGroupViews = new ArrayList<ModuleGroupView>(); private Composite navigationMainComposite; private INavigationCompositeDelegation navigationCompositeDelegation; private NavigationTreeObserver navigationTreeObserver; private final Map<INavigationNode<?>, ModuleGroupView> moduleGroupNodesToViews; private final Map<INavigationNode<?>, ModuleView> moduleNodesToViews; public NavigationViewPart() { markAsNavigation(); moduleGroupNodesToViews = new HashMap<INavigationNode<?>, ModuleGroupView>(); moduleNodesToViews = new HashMap<INavigationNode<?>, ModuleView>(); } protected IViewFactory getViewFactory() { if (viewFactory == null) { viewFactory = new NavigationViewFactory(); Wire.instance(viewFactory).andStart(Activator.getDefault().getContext()); } return viewFactory; } private void markAsNavigation() { setPartProperty(TitlelessStackPresentation.PROPERTY_NAVIGATION, String.valueOf(Boolean.TRUE)); } public ISubApplicationNode getSubApplicationNode() { final String perspectiveID = getViewSite().getPage().getPerspective().getId(); return SwtViewProvider.getInstance().getNavigationNode(perspectiveID, ISubApplicationNode.class); } @Override public void createPartControl(final Composite parent) { this.parent = parent; initLayoutParts(); // build the view hierarchy buildNavigationViewHierarchy(); initModelObserver(); parent.setData(TitlelessStackPresentation.PROPERTY_SUBAPPLICATION_NODE, getSubApplicationNode()); } private void initLayoutParts() { // configure layout parent.setLayout(new FormLayout()); parent.setBackground(NAVIGATION_BACKGROUND); resizeListener = new ResizeListener(); parent.addControlListener(resizeListener); navigationMainComposite = new Composite(parent, SWT.DOUBLE_BUFFERED); navigationMainComposite.setLayout(new FormLayout()); navigationMainComposite.setBackground(NAVIGATION_BACKGROUND); navigationCompositeDelegation = createNavigationCompositeDelegation(navigationMainComposite); final boolean fastView = NavigationFacade.getDefault().getApplicationUtility().isNavigationFastViewEnabled(); final FormData formData = new FormData(); formData.top = new FormAttachment(0, fastView ? AbstractNavigationCompositeDeligation.BORDER_MARGIN : 0); formData.left = new FormAttachment(0, fastView ? AbstractNavigationCompositeDeligation.BORDER_MARGIN : 0); formData.right = new FormAttachment(100, fastView ? -AbstractNavigationCompositeDeligation.BORDER_MARGIN : 0); formData.bottom = new FormAttachment(100, navigationCompositeDelegation.getBottomOffest() - (fastView ? AbstractNavigationCompositeDeligation.BORDER_MARGIN : 0)); navigationMainComposite.setLayoutData(formData); if (fastView) { SWTFacade.getDefault().addPaintListener(parent, new PaintListener() { public void paintControl(final PaintEvent e) { final SubModuleViewRenderer viewRenderer = (SubModuleViewRenderer) LnfManager.getLnf().getRenderer("SubModuleView.renderer"); //$NON-NLS-1$ if (viewRenderer != null) { final Rectangle bounds = parent.getBounds(); viewRenderer.setBounds(bounds); viewRenderer.paint(e.gc, null); } } }); } } /** * Creates a delegation of the composite for scrolling in the navigation. * * @param superParent * parent of the navigation composite * @param parent * composite of the navigation * @return delegation */ private INavigationCompositeDelegation createNavigationCompositeDelegation(final Composite parent) { final boolean scrollBar = LnfManager.getLnf().getBooleanSetting(LnfKeyConstants.NAVIGATION_SCROLL_BAR, false); if (scrollBar) { return new ScrollBarNavigationCompositeDeligation(parent.getParent(), parent, this); } else { return createButtonsNavigationCompositeDelegation(parent); } } /** * @param parent * @return * @since 5.0 */ protected INavigationCompositeDelegation createButtonsNavigationCompositeDelegation(final Composite parent) { return new ScrollButtonsNavigationCompositeDeligation(parent.getParent(), parent, this); } /** * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ @Override public void dispose() { super.dispose(); if (!SwtUtilities.isDisposed(parent) && resizeListener != null) { parent.removeControlListener(resizeListener); } } private void buildNavigationViewHierarchy() { // get the root of the SubApplication final ISubApplicationNode subApplicationNode = getSubApplicationNode(); for (final IModuleGroupNode moduleGroupNode : subApplicationNode.getChildren()) { createModuleGroupView(moduleGroupNode); } updateNavigationSize(); } private void initModelObserver() { navigationTreeObserver = new NavigationTreeObserver(); navigationTreeObserver.addListener(new SubApplicationListener()); navigationTreeObserver.addListener(new ModuleGroupListener()); navigationTreeObserver.addListener(new ModuleListener()); navigationTreeObserver.addListener(new SubModuleListener()); navigationTreeObserver.addListenerTo(getSubApplicationNode()); } /** * Update the size of the navigation area when the application is resized (fix for bug 270620). */ private final class ResizeListener extends ControlAdapter { @Override public void controlResized(final ControlEvent e) { parent.layout(); updateNavigationSize(); } } /** * The Listener updates the size of the navigation, if a child is added or removed. */ private class SubApplicationListener extends SubApplicationNodeListener { /** * @see org.eclipse.riena.navigation.listener.NavigationNodeListener#childAdded(org.eclipse.riena.navigation.INavigationNode, * org.eclipse.riena.navigation.INavigationNode) */ @Override public void childAdded(final ISubApplicationNode source, final IModuleGroupNode child) { // create moduleGroupView createModuleGroupView(child); updateNavigationSize(); } // @Override // public void filterAdded(ISubApplicationNode source, IUIFilter filter) { // super.filterAdded(source, filter); // updateNavigationSize(); // } // // @Override // public void filterRemoved(ISubApplicationNode source, IUIFilter filter) { // super.filterRemoved(source, filter); // updateNavigationSize(); // } // /** * @see org.eclipse.riena.navigation.listener.NavigationNodeListener#childRemoved(org.eclipse.riena.navigation.INavigationNode, * org.eclipse.riena.navigation.INavigationNode) */ @Override public void childRemoved(final ISubApplicationNode source, final IModuleGroupNode child) { unregisterModuleGroupView(child); if (source.isSelected()) { updateNavigationSize(); } } @Override public void markerChanged(final ISubApplicationNode source, final IMarker marker) { if (marker instanceof HiddenMarker) { updateNavigationSize(); } } } private class ModuleGroupListener extends ModuleGroupNodeListener { // @Override // public void filterAdded(IModuleGroupNode source, IUIFilter filter) { // super.filterAdded(source, filter); // updateNavigationSize(); // } // // @Override // public void filterRemoved(IModuleGroupNode source, IUIFilter filter) { // super.filterRemoved(source, filter); // updateNavigationSize(); // } @Override public void childAdded(final IModuleGroupNode source, final IModuleNode child) { // createModuleView final ModuleGroupView moduleGroupView = getModuleGroupViewForNode(source); createModuleView(child, moduleGroupView); // childAdded.activate(); updateNavigationSize(); } @Override public void childRemoved(final IModuleGroupNode source, final IModuleNode child) { moduleNodesToViews.remove(child); // updateNavigationSize(); } @Override public void disposed(final IModuleGroupNode source) { super.disposed(source); unregisterModuleGroupView(source); // updateNavigationSize(); } @Override public void markerChanged(final IModuleGroupNode source, final IMarker marker) { if (marker instanceof HiddenMarker) { updateNavigationSize(); } } @Override public void nodeIdChange(final IModuleGroupNode source, final NavigationNodeId oldId, final NavigationNodeId newId) { super.nodeIdChange(source, oldId, newId); replaceNavigationNodeId(source, oldId, newId); } } private class SubModuleListener extends SubModuleNodeListener { @Override public void markerChanged(final ISubModuleNode source, final IMarker marker) { if (marker instanceof HiddenMarker) { updateNavigationSize(); } } } private class ModuleListener extends ModuleNodeListener { @Override public void nodeIdChange(final IModuleNode source, final NavigationNodeId oldId, final NavigationNodeId newId) { super.nodeIdChange(source, oldId, newId); replaceNavigationNodeId(source, oldId, newId); } } public ModuleGroupView getModuleGroupViewForNode(final IModuleGroupNode source) { return moduleGroupNodesToViews.get(source); } /** * @since 1.2 */ public ModuleView getModuleViewForNode(final IModuleNode source) { return moduleNodesToViews.get(source); } private void createModuleGroupView(final IModuleGroupNode moduleGroupNode) { // ModuleGroupView are directly rendered into the bodyComposite final ModuleGroupView moduleGroupView = getViewFactory().createModuleGroupView(navigationCompositeDelegation.getNavigationComposite()); NodeIdentificationSupport.setIdentification(moduleGroupView, "moduleGroupView", moduleGroupNode); //$NON-NLS-1$ moduleGroupNodesToViews.put(moduleGroupNode, moduleGroupView); moduleGroupView.addUpdateListener(new ModuleGroupViewObserver()); registerModuleGroupView(moduleGroupView); // create controller. the controller is implicit registered as // presentation in the node // now the ModuleGroupController can be replaced by your own implementation getViewFactory().createModuleGroupController(moduleGroupNode); //new ModuleGroupController(moduleGroupNode); moduleGroupView.bind((ModuleGroupNode) moduleGroupNode); moduleGroupView.setLayout(new FormLayout()); final Composite moduleGroupBody = new Composite(moduleGroupView, SWT.NONE); moduleGroupBody.setBackground(LnfManager.getLnf().getColor(LnfKeyConstants.MODULE_GROUP_WIDGET_BACKGROUND)); final FormData formData = new FormData(); final int padding = getModuleGroupPadding(); formData.top = new FormAttachment(0, padding); formData.left = new FormAttachment(0, padding); formData.bottom = new FormAttachment(100, -padding); formData.right = new FormAttachment(100, -padding); moduleGroupBody.setLayoutData(formData); // now it's time for the module views for (final IModuleNode moduleNode : moduleGroupNode.getChildren()) { createModuleView(moduleNode, moduleGroupView); } } private final class ModuleGroupViewObserver implements IComponentUpdateListener { public void update(final INavigationNode<?> node) { updateNavigationSize(); } } /** * Adds the give view to the list of the module views that are belonging to the sub-application of this navigation. * * @param moduleView * view to register */ private void registerModuleGroupView(final ModuleGroupView moduleGroupView) { moduleGroupViews.add(moduleGroupView); final ModuleGroupViewComparator comparator = new ModuleGroupViewComparator(); Collections.sort(moduleGroupViews, comparator); } class ModuleGroupViewComparator implements Comparator<ModuleGroupView> { public int compare(final ModuleGroupView moduleGroupView1, final ModuleGroupView moduleGroupView2) { final ModuleGroupNode moduleGroupNode1 = moduleGroupView1.getNavigationNode(); final ModuleGroupNode moduleGroupNode2 = moduleGroupView2.getNavigationNode(); return getSubApplicationNode().getIndexOfChild(moduleGroupNode1) < getSubApplicationNode().getIndexOfChild(moduleGroupNode2) ? -1 : 1; } } /** * Removes that view that is belonging to the given node from the list of the module group views. * * @param moduleGroupNode * node whose according view should be unregistered */ public void unregisterModuleGroupView(final IModuleGroupNode moduleGroupNode) { for (final ModuleGroupView moduleGroupView : moduleGroupViews) { if (moduleGroupView.getNavigationNode() == moduleGroupNode) { unregisterModuleGroupView(moduleGroupView, moduleGroupNode); break; } } } /** * Remove the given view from the list of the module group views. * * @param moduleGroupView * view to remove */ private void unregisterModuleGroupView(final ModuleGroupView moduleGroupView, final IModuleGroupNode node) { moduleGroupNodesToViews.remove(node); moduleGroupViews.remove(moduleGroupView); } private void replaceNavigationNodeId(final IModuleGroupNode node, final NavigationNodeId oldId, final NavigationNodeId newId) { node.setNodeId(oldId); final ModuleGroupView view = moduleGroupNodesToViews.remove(node); if (view != null) { node.setNodeId(newId); moduleGroupNodesToViews.put(node, view); } } private void replaceNavigationNodeId(final IModuleNode node, final NavigationNodeId oldId, final NavigationNodeId newId) { node.setNodeId(oldId); final ModuleView view = moduleNodesToViews.remove(node); if (view != null) { node.setNodeId(newId); moduleNodesToViews.put(node, view); } } private void createModuleView(final IModuleNode moduleNode, final ModuleGroupView moduleGroupView) { final Composite moduleGroupBody = (Composite) moduleGroupView.getChildren()[0]; final FormLayout layout = new FormLayout(); moduleGroupBody.setLayout(layout); final ModuleView moduleView = viewFactory.createModuleView(moduleGroupBody); moduleView.setModuleGroupNode(moduleGroupView.getNavigationNode()); NodeIdentificationSupport.setIdentification(moduleView.getTitle(), "titleBar", moduleNode); //$NON-NLS-1$ NodeIdentificationSupport.setIdentification(moduleView.getTree(), "tree", moduleNode); //$NON-NLS-1$ moduleNodesToViews.put(moduleNode, moduleView); final ModuleController controller = getViewFactory().createModuleController(moduleNode); moduleView.bind((ModuleNode) moduleNode); if (controller instanceof SWTModuleController) { ((SWTModuleController) controller).getTree().addSelectionListener(new ISelectionListener() { public void ridgetSelected(final SelectionEvent event) { // *after* the node in the navigation is *really* selected // start scrolling navigationCompositeDelegation.scroll(); } }); } // the size of the module group depends on the module views moduleGroupView.registerModuleView(moduleView); } public void updateNavigationSize() { final int height = updateNavigationHeight(); final boolean widthChanged = updateModuleGroupWidth(height); //navigationMainComposite.layout(true, true); if (widthChanged) { updateNavigationHeight(); } navigationMainComposite.getParent().layout(true, true); navigationCompositeDelegation.scroll(); } /** * @return the new height */ private int updateNavigationHeight() { int height = calculateHeight(); navigationMainComposite.layout(); navigationCompositeDelegation.getNavigationComposite().layout(); if (navigationMainComposite.getBounds().height == 0) { height = 0; } navigationCompositeDelegation.updateSize(height); return height; } /** * If a vertical scroll bar exists, the width of the module groups must be updated. * * @param height * height of the scrolled composite * @return {@code true} width of the module groups has changed */ private boolean updateModuleGroupWidth(final int height) { boolean widthChanged = false; int scrollbarWidth = 0; final Point mainSize = navigationMainComposite.getSize(); if (height > mainSize.x) { scrollbarWidth = navigationCompositeDelegation.getVerticalScrollBarSize().x; } for (final ModuleGroupView moduleGroupView : moduleGroupViews) { if (!moduleGroupView.isDisposed()) { final Point size = moduleGroupView.getSize(); if (mainSize.x > size.x) { moduleGroupView.setSize(mainSize.x, size.y); widthChanged = true; } } if (moduleGroupView.getScrollbarWidth() != scrollbarWidth) { moduleGroupView.setScrollbarWidth(scrollbarWidth); widthChanged = true; } } return widthChanged; } public IModuleGroupNode getActiveModuleGroupNode() { final List<IModuleGroupNode> children = getSubApplicationNode().getChildren(); for (final IModuleGroupNode child : children) { if (child.isActivated()) { return child; } } return null; } /** * @since 4.0 */ public int calculateHeight() { int height = 0; final int widthHint = navigationMainComposite.getSize().x; Collections.sort(moduleGroupViews, new ModuleGroupViewComparator()); for (final ModuleGroupView moduleGroupView : moduleGroupViews) { height = moduleGroupView.calculateHeight(widthHint, height); } return height; } /** * Returns the renderer of the module group. * * @return the renderer instance */ private ModuleGroupRenderer getModuleGroupRenderer() { ModuleGroupRenderer renderer = (ModuleGroupRenderer) LnfManager.getLnf().getRenderer(LnfKeyConstants.MODULE_GROUP_RENDERER); if (renderer == null) { renderer = new ModuleGroupRenderer(); } return renderer; } /** * Returns the renderer of the module group. * * @return the renderer instance */ private EmbeddedBorderRenderer getLnfBorderRenderer() { EmbeddedBorderRenderer renderer = (EmbeddedBorderRenderer) LnfManager.getLnf().getRenderer(LnfKeyConstants.SUB_MODULE_VIEW_BORDER_RENDERER); if (renderer == null) { renderer = new EmbeddedBorderRenderer(); } return renderer; } private int getModuleGroupPadding() { return getModuleGroupRenderer().getModuleGroupPadding() + getLnfBorderRenderer().getBorderWidth(); } @Override public void setFocus() { new SwtUISynchronizer().asyncExec(new Runnable() { public void run() { navigationMainComposite.setFocus(); } }); } public Composite getNavigationComponent() { return navigationMainComposite; } public Composite getScrolledComponent() { return navigationCompositeDelegation.getNavigationComposite(); } }