/*******************************************************************************
* 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.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
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.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.riena.core.marker.IMarker;
import org.eclipse.riena.navigation.IModuleGroupNode;
import org.eclipse.riena.navigation.IModuleNode;
import org.eclipse.riena.navigation.INavigationNode;
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.model.ModuleGroupNode;
import org.eclipse.riena.navigation.model.ModuleNode;
import org.eclipse.riena.navigation.ui.swt.lnf.renderer.ModuleGroupRenderer;
import org.eclipse.riena.navigation.ui.swt.presentation.SwtViewProvider;
import org.eclipse.riena.ui.filter.IUIFilter;
import org.eclipse.riena.ui.swt.facades.SWTFacade;
import org.eclipse.riena.ui.swt.lnf.LnfKeyConstants;
import org.eclipse.riena.ui.swt.lnf.LnfManager;
/**
* View of a module group.
*/
public class ModuleGroupView extends Composite implements INavigationNodeView<ModuleGroupNode> {
private static final int MODULE_GROUP_GAP = 3;
private ModuleGroupNode moduleGroupNode;
private ModuleGroupListener moduleGroupListener;
private ModuleListener moduleListener;
private PaintDelegation paintDelegation;
private final List<IComponentUpdateListener> updateListeners;
private final Map<ModuleNode, ModuleView> registeredModuleViews;
private final List<INavigationNode<?>> disposingNodes;
private int scrollbarWidth;
public ModuleGroupView(final Composite parent, final int style) {
super(parent, style | SWT.DOUBLE_BUFFERED);
updateListeners = new ArrayList<IComponentUpdateListener>();
registeredModuleViews = new LinkedHashMap<ModuleNode, ModuleView>();
disposingNodes = new ArrayList<INavigationNode<?>>();
setData(getClass().getName());
}
private List<ModuleView> getAllModuleViews() {
return new ArrayList<ModuleView>(registeredModuleViews.values());
}
private List<ModuleView> getAllVisibleModuleViews() {
final List<ModuleView> views = new ArrayList<ModuleView>();
for (final ModuleView view : registeredModuleViews.values()) {
if (view.isVisible()) {
views.add(view);
}
}
return views;
}
protected ModuleNode getNodeForView(final ModuleView view) {
for (final Entry<ModuleNode, ModuleView> entry : registeredModuleViews.entrySet()) {
if (view.equals(entry.getValue())) {
return entry.getKey();
}
}
return null;
}
/**
* @since 1.2
*/
@Override
public boolean setFocus() {
// accept focus is this group is the active group
return getNavigationNode().isActivated() ? super.setFocus() : false;
}
/**
* @see org.eclipse.riena.navigation.ui.swt.views.INavigationNodeView#bind(org.eclipse.riena.navigation.INavigationNode)
*/
public void bind(final ModuleGroupNode node) {
moduleGroupNode = node;
addListeners();
}
/**
* Adds listeners to this view and also to node of the module group.
*/
protected void addListeners() {
moduleGroupListener = new ModuleGroupListener();
getNavigationNode().addListener(moduleGroupListener);
moduleListener = new ModuleListener();
paintDelegation = new PaintDelegation();
SWTFacade.getDefault().addPaintListener(this, paintDelegation);
}
/**
* @see org.eclipse.riena.navigation.ui.swt.views.INavigationNodeView#unbind()
*/
public void unbind() {
getNavigationNode().removeListener(moduleGroupListener);
SWTFacade.getDefault().removePaintListener(this, paintDelegation);
moduleGroupNode = null;
}
/**
* @see org.eclipse.riena.navigation.ui.swt.views.INavigationNodeView#getNavigationNode()
*/
public ModuleGroupNode getNavigationNode() {
return moduleGroupNode;
}
/**
* The Listener fires updates, if a child (sub-module) is added or removed.
*/
private final class ModuleListener extends ModuleNodeListener {
@Override
public void childAdded(final IModuleNode source, final ISubModuleNode child) {
super.childAdded(source, child);
fireUpdated(child);
}
@Override
public void beforeDisposed(final IModuleNode source) {
disposingNodes.add(source);
}
@Override
public void disposed(final IModuleNode source) {
disposingNodes.remove(source);
}
@Override
public void childRemoved(final IModuleNode source, final ISubModuleNode child) {
if (disposingNodes.contains(source)) {
return;
}
fireUpdated(child);
}
@Override
public void filterAdded(final IModuleNode source, final IUIFilter filter) {
super.filterAdded(source, filter);
fireUpdated(null);
}
@Override
public void filterRemoved(final IModuleNode source, final IUIFilter filter) {
super.filterRemoved(source, filter);
fireUpdated(null);
}
@Override
public void presentSingleSubModuleChanged(final IModuleNode source) {
super.presentSingleSubModuleChanged(source);
fireUpdated(null);
}
@Override
public void labelChanged(final IModuleNode source) {
super.labelChanged(source);
fireUpdated(null);
}
}
/**
* The Listener fires updates, if a child (module) is added or removed.
*/
private final class ModuleGroupListener extends ModuleGroupNodeListener {
@Override
public void filterAdded(final IModuleGroupNode source, final IUIFilter filter) {
super.filterAdded(source, filter);
fireUpdated(null);
}
@Override
public void filterRemoved(final IModuleGroupNode source, final IUIFilter filter) {
super.filterRemoved(source, filter);
fireUpdated(null);
}
@Override
public void beforeDisposed(final IModuleGroupNode source) {
disposingNodes.add(source);
}
@Override
public void childAdded(final IModuleGroupNode source, final IModuleNode child) {
fireUpdated(child);
}
@Override
public void childRemoved(final IModuleGroupNode source, final IModuleNode child) {
unregisterModuleView(child);
if (disposingNodes.contains(source)) {
return;
}
fireUpdated(child);
}
@Override
public void deactivated(final IModuleGroupNode source) {
super.deactivated(source);
redraw();
}
@Override
public void disposed(final IModuleGroupNode source) {
super.disposed(source);
disposingNodes.remove(source);
unbind();
dispose();
}
@Override
public void nodeIdChange(final IModuleGroupNode source, final NavigationNodeId oldId,
final NavigationNodeId newId) {
if (source.equals(getNavigationNode())) {
SwtViewProvider.getInstance().replaceNavigationNodeId(source, oldId, newId);
}
}
@Override
public void markerChanged(final IModuleGroupNode source, final IMarker marker) {
super.markerChanged(source, marker);
updateEnabled();
}
}
/**
* {@inheritDoc}
*
* @since 4.0
*/
public int calculateHeight(final int widthHint, int heightHint) {
if (isDisposed()) {
return heightHint;
}
Point p = new Point(0, 0);
if (getNavigationNode() != null && getNavigationNode().isVisible()) {
p = computeSize(widthHint, SWT.DEFAULT);
}
final FormData fd = new FormData();
fd.top = new FormAttachment(0, heightHint);
fd.left = new FormAttachment(0, 0);
heightHint += p.y;
fd.width = p.x - getScrollbarWidth();
fd.bottom = new FormAttachment(0, heightHint);
if (!equals(fd, getCurrentFormData())) {
setLayoutData(fd);
layout();
}
if (p.y > 0) {
heightHint += MODULE_GROUP_GAP;
}
return heightHint;
}
/**
* Updates the enabled state of all module views.
*
* @since 3.0
*/
public void updateEnabled() {
for (final ModuleView moduleView : getAllModuleViews()) {
moduleView.updateEnabled();
}
}
/**
* Compares the to given {@code FormData}s.
*
* @param fd1
* first {@code FormData}
* @param fd2
* second {@code FormData}
* @return {@code true} if equals; otherwise false
*/
private boolean equals(final FormData fd1, final FormData fd2) {
if (fd1 == fd2) {
return true;
}
if ((fd1 == null) || (fd2 == null)) {
return false;
}
if (fd1.height != fd2.height) {
return false;
}
if (fd1.width != fd2.width) {
return false;
}
if (!equals(fd1.bottom, fd2.bottom)) {
return false;
}
if (!equals(fd1.left, fd2.left)) {
return false;
}
if (!equals(fd1.right, fd2.right)) {
return false;
}
if (!equals(fd1.top, fd2.top)) {
return false;
}
return true;
}
/**
* Compares the to given {@code FormAttachment}s.
*
* @param fd1
* first {@code FormAttachment}
* @param fd2
* second {@code FormAttachment}
* @return {@code true} if equals; otherwise false
*/
private boolean equals(final FormAttachment fa1, final FormAttachment fa2) {
if (fa1 == fa2) {
return true;
}
if ((fa1 == null) || (fa2 == null)) {
return false;
}
if (fa1.alignment != fa2.alignment) {
return false;
}
if (fa1.denominator != fa2.denominator) {
return false;
}
if (fa1.numerator != fa2.numerator) {
return false;
}
if (fa1.offset != fa2.offset) {
return false;
}
return true;
}
/**
* Returns the current layout data of this view.
*
* @return current layout data; {@code null} if layout data is not a
* {@code FormData}
*/
private FormData getCurrentFormData() {
final Object data = getLayoutData();
if (data instanceof FormData) {
return (FormData) data;
} else {
return null;
}
}
@Override
public Point computeSize(final int wHint, final int hHint) {
final GC gc = new GC(Display.getCurrent());
getRenderer().setItems(getAllVisibleModuleViews());
getRenderer().setNavigationNode(getNavigationNode());
final Point size = getRenderer().computeSize(gc, wHint, hHint);
gc.dispose();
return size;
}
private class PaintDelegation implements PaintListener {
/**
* Computes the size of the widget of the module group and paints it.
*
* @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
*/
public void paintControl(final PaintEvent e) {
setBackground(LnfManager.getLnf().getColor(LnfKeyConstants.MODULE_GROUP_WIDGET_BACKGROUND));
getRenderer().setMarkers(getNavigationNode().getMarkers());
getRenderer().setItems(getAllVisibleModuleViews());
getRenderer().setActive(getNavigationNode().isActivated());
final ModuleGroupView view = (ModuleGroupView) e.getSource();
final Rectangle bounds = view.getBounds();
getRenderer().setBounds(0, 0, bounds.width, bounds.height);
getRenderer().setNavigationNode(getNavigationNode());
getRenderer().paint(e.gc, null);
}
}
private ModuleGroupRenderer getRenderer() {
return (ModuleGroupRenderer) LnfManager.getLnf().getRenderer(LnfKeyConstants.MODULE_GROUP_RENDERER);
}
/**
* Adds the give view to the list of the module views that are belonging to
* this module group.
*
* @param moduleView
* view to register
*/
public void registerModuleView(final ModuleView moduleView) {
moduleView.getNavigationNode().addListener(moduleListener);
// we need that to calculate the bounds of the ModuleGroupView
registeredModuleViews.put(moduleView.getNavigationNode(), moduleView);
// observer moduleView for expand/collapse
moduleView.addUpdateListener(new ModuleViewObserver());
}
/**
* Removes that view that is belonging to the given node from the list of
* the module views.
*
* @param moduleNode
* node whose according view should be unregistered
*/
public void unregisterModuleView(final IModuleNode moduleNode) {
for (final ModuleView moduleView : getAllModuleViews()) {
if (moduleView.getNavigationNode() == moduleNode || moduleView.getNavigationNode() == null) {
unregisterModuleView(moduleView);
break;
}
}
}
/**
* Remove the given view from the list of the module views.
*
* @param moduleView
* view to remove
*/
public void unregisterModuleView(final ModuleView moduleView) {
final ModuleNode node = getNodeForView(moduleView);
if (node != null) {
node.removeListener(moduleListener);
registeredModuleViews.remove(node);
}
}
private class ModuleViewObserver implements IComponentUpdateListener {
public void update(final INavigationNode<?> node) {
fireUpdated(node);
}
}
protected void fireUpdated(final INavigationNode<?> node) {
for (final IComponentUpdateListener listener : updateListeners) {
listener.update(node);
}
}
public void addUpdateListener(final IComponentUpdateListener listener) {
updateListeners.add(listener);
}
/**
* @return the scrollbarWidth
* @since 3.0
*/
public int getScrollbarWidth() {
return scrollbarWidth;
}
/**
* @param scrollbarWidth
* the scrollbarWidth to set
* @since 3.0
*/
public void setScrollbarWidth(final int scrollbarWidth) {
this.scrollbarWidth = scrollbarWidth;
}
}