package org.eclipse.uml2.diagram.common.async;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.uml2.diagram.common.Messages;
import org.eclipse.uml2.diagram.common.genapi.IVisualIDRegistry;
import org.eclipse.uml2.diagram.common.genapi.IVisualIDRegistryExt;
import org.eclipse.uml2.diagram.common.genapi.IVisualIDRegistryExt.MenuTypeHint;
public class SyncModelUI {
private SyncModelNode myRootNode;
private final SyncModelLabelProvider myLabelProvider;
private CheckboxTreeViewer myTreeViewer;
private CheckListener myCheckListener;
private CheckStateInitializer myCheckStateInitializer;
public SyncModelUI(Composite parent, SyncModelLabelProvider labelProvider) {
myLabelProvider = labelProvider;
createContents(parent);
}
private void createContents(Composite composite) {
CheckboxFilteredTree filterTree = new CheckboxFilteredTree(composite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER, new PatternFilter());
myTreeViewer = filterTree.getCheckboxTreeViewer();
myLabelProvider.hookTreeViewer(myTreeViewer);
GridData layoutData = new GridData(GridData.FILL_BOTH);
layoutData.heightHint = 300;
layoutData.widthHint = 300;
myTreeViewer.getTree().setLayoutData(layoutData);
myTreeViewer.setContentProvider(new SyncModelContentProvider());
myTreeViewer.setLabelProvider(myLabelProvider);
myTreeViewer.setComparator(new ViewerComparator());
myCheckStateInitializer = new CheckStateInitializer(myTreeViewer);
myTreeViewer.addTreeListener(myCheckStateInitializer);
myCheckListener = new CheckListener(myTreeViewer);
myTreeViewer.addCheckStateListener(myCheckListener);
}
public void setRootNode(SyncModelNode rootNode) {
myRootNode = rootNode;
myTreeViewer.setInput(new SyncModelNode[] { rootNode });
if (myCheckListener != null) {
myCheckListener.setAlwaysChecked(rootNode);
}
myTreeViewer.setGrayChecked(rootNode, true);
new MenuBuilder(myTreeViewer).attachMenu(myRootNode);
}
public void revealRootChildren() {
if (myRootNode != null) {
myTreeViewer.expandToLevel(2);
TreeExpansionEvent expandEvent = new TreeExpansionEvent(myTreeViewer, myRootNode);
myCheckStateInitializer.treeExpanded(expandEvent);
}
}
public SyncModelNode getRootSyncNode() {
return myRootNode;
}
public CheckboxTreeViewer getUI() {
return myTreeViewer;
}
private static class CheckStateInitializer implements ITreeViewerListener {
private final List<Object> myExpandedNodes = new ArrayList<Object>();
private final CheckboxTreeViewer myViewer;
public CheckStateInitializer(CheckboxTreeViewer viewer) {
myViewer = viewer;
}
public void treeCollapsed(TreeExpansionEvent event) {
//
}
public void treeExpanded(TreeExpansionEvent event) {
Object expanded = event.getElement();
if (myExpandedNodes.contains(expanded)) {
return; // only first expand is interesting
}
myExpandedNodes.add(expanded);
for (Object nextChild : getTreeContentProvider().getChildren(expanded)) {
if (nextChild instanceof SyncModelNode) {
SyncModelNode node = (SyncModelNode) nextChild;
myViewer.setChecked(node, node.isChecked());
}
}
}
private ITreeContentProvider getTreeContentProvider() {
return (ITreeContentProvider) myViewer.getContentProvider();
}
}
private static class CheckListener implements ICheckStateListener {
private final CheckboxTreeViewer myViewer;
private SyncModelNode myAlwaysChecked;
public CheckListener(CheckboxTreeViewer viewer) {
myViewer = viewer;
}
public void setAlwaysChecked(SyncModelNode root) {
myAlwaysChecked = root;
}
public void checkStateChanged(CheckStateChangedEvent event) {
Object subject = event.getElement();
if (myAlwaysChecked != null && subject == myAlwaysChecked) {
// roll back, its always checked
myViewer.setChecked(subject, true);
return;
}
if (false == subject instanceof SyncModelNode) {
return;
}
SyncModelNode node = (SyncModelNode) subject;
node.setChecked(event.getChecked());
boolean refreshNeeded = false;
if (event.getChecked()) {
createTypeHintMenu(node);
for (SyncModelNode next = node.getParent(); next != null; next = next.getParent()) {
refreshNeeded |= !next.isChecked();
next.setChecked(true);
myViewer.setChecked(next, true);
}
if (node.isAutoSynchronized()) {
for (SyncModelNode nextChild : node.getChildren()) {
refreshNeeded |= nextChild.isChecked();
nextChild.setChecked(true);
myViewer.setChecked(nextChild, true);
}
}
} else {
SyncModelNode parent = node.getParent();
refreshNeeded = parent.isAutoSynchronized();
parent.setAutoSynchronized(false);
for (SyncModelNode nextChild : node.getChildren()) {
refreshNeeded |= nextChild.isChecked();
nextChild.setChecked(false);
myViewer.setChecked(nextChild, false);
}
}
if (refreshNeeded) {
myViewer.refresh(true);
}
}
private void createTypeHintMenu(SyncModelNode node) {
IVisualIDRegistry registry = node.getContext().getRegistry();
if (node.getDiagramView() == null && registry instanceof IVisualIDRegistryExt) {
IVisualIDRegistryExt extendedRegistry = (IVisualIDRegistryExt) registry;
String nodeType = node.getSyncModelView().getType();
List<IVisualIDRegistryExt.MenuTypeHint> menuTypeHints = extendedRegistry.getMenuTypeHints(nodeType);
if (menuTypeHints.size() > 1) {
Menu menu = new Menu(myViewer.getControl());
for (final IVisualIDRegistryExt.MenuTypeHint menuTypeHint : menuTypeHints) {
MenuItem item = new MenuItem(menu, SWT.DROP_DOWN);
item.setText(menuTypeHint.getMessage());
item.addListener(SWT.Selection, new ChooseTypeMenuListener(myViewer, node, menuTypeHint.getType()));
}
menu.setVisible(true);
}
}
}
}
private static class ChooseTypeMenuListener implements Listener {
private final CheckboxTreeViewer myTreeViewer;
private final SyncModelNode myNode;
private final String myType;
private ChooseTypeMenuListener(CheckboxTreeViewer treeViewer, SyncModelNode node, String type) {
super();
this.myTreeViewer = treeViewer;
this.myNode = node;
this.myType = type;
}
public void handleEvent(Event event) {
myNode.setChosenSyncModelViewType(myType);
myTreeViewer.refresh();
}
}
private static class MenuBuilder implements IMenuListener {
private final CheckboxTreeViewer myViewer;
private final SyncModelUIAction mySwitchSyncAction;
private final SyncModelUIAction mySelectAllChildrenAction;
private final SyncModelUIAction myUnselectAllChildrenAction;
private final Map<String, IContributionItem> mySetTypeSubmenus;
private final Map<String, List<SyncModelUIAction>> mySetTypeActions;
private boolean myIsSetTypeSubmenuEnabled;
private IContributionItem myCurrentSetTypeSubmenu;
public MenuBuilder(CheckboxTreeViewer viewer) {
myViewer = viewer;
mySwitchSyncAction = new SwitchSynchronizationAction(viewer);
mySelectAllChildrenAction = new BulkSelectAction(viewer, true);
myUnselectAllChildrenAction = new BulkSelectAction(viewer, false);
mySelectAllChildrenAction.setText(Messages.SyncModelUI_action_select_all_children);
myUnselectAllChildrenAction.setText(Messages.SyncModelUI_action_unselect_all_children);
mySetTypeSubmenus = new HashMap<String, IContributionItem>();
mySetTypeActions = new HashMap<String, List<SyncModelUIAction>>();
}
public void attachMenu(SyncModelNode rootNode) {
MenuManager menuManager = new MenuManager();
menuManager.addMenuListener(this);
menuManager.add(mySwitchSyncAction);
menuManager.add(mySelectAllChildrenAction);
menuManager.add(myUnselectAllChildrenAction);
IVisualIDRegistry registry = rootNode.getContext().getRegistry();
if (registry instanceof IVisualIDRegistryExt) {
myIsSetTypeSubmenuEnabled = true;
createSetTypeSubmenus((IVisualIDRegistryExt) registry);
} else {
myIsSetTypeSubmenuEnabled = false;
}
myViewer.getTree().setMenu(menuManager.createContextMenu(myViewer.getTree()));
}
private void createSetTypeSubmenus(IVisualIDRegistryExt registry) {
for (String type : registry.getAllHintedTypes()) {
MenuManager hintMenuManager = new MenuManager(Messages.SyncModelUI_action_show_as);
List<MenuTypeHint> hints = registry.getMenuTypeHints(type);
List<SyncModelUIAction> actions = new LinkedList<SyncModelUIAction>();
for (MenuTypeHint hint : hints) {
if (!type.equals(hint.getType())) {
SetTypeAction action = new SetTypeAction(myViewer, hint);
hintMenuManager.add(action);
actions.add(action);
}
}
mySetTypeActions.put(type, actions);
mySetTypeSubmenus.put(type, hintMenuManager);
}
}
public void menuAboutToShow(IMenuManager manager) {
mySwitchSyncAction.update(myViewer);
mySelectAllChildrenAction.update(myViewer);
myUnselectAllChildrenAction.update(myViewer);
if (myIsSetTypeSubmenuEnabled) {
updateSetTypeSubmenu(manager);
}
}
private void updateSetTypeSubmenu(IMenuManager manager) {
IStructuredSelection selection = (IStructuredSelection) myViewer.getSelection();
if (selection.size() != 1 && myCurrentSetTypeSubmenu != null) {
manager.remove(myCurrentSetTypeSubmenu);
return;
}
SyncModelNode selected = (SyncModelNode) selection.getFirstElement();
String selectedType = selected.getSyncModelView().getType();
IContributionItem newSubmenu = mySetTypeSubmenus.get(selectedType);
if (newSubmenu != myCurrentSetTypeSubmenu) {
if (myCurrentSetTypeSubmenu != null) {
manager.remove(myCurrentSetTypeSubmenu);
}
if (newSubmenu != null) {
manager.add(newSubmenu);
}
myCurrentSetTypeSubmenu = newSubmenu;
}
List<SyncModelUIAction> actions = mySetTypeActions.get(selectedType);
if (actions != null) {
for (SyncModelUIAction action : actions) {
action.update(myViewer);
}
}
}
}
private static abstract class SyncModelUIAction extends Action {
public abstract void update(CheckboxTreeViewer syncModelUI);
}
private static class SwitchSynchronizationAction extends SyncModelUIAction {
private final CheckboxTreeViewer mySyncModelUI;
private SyncModelNode mySelected;
public SwitchSynchronizationAction(CheckboxTreeViewer syncModelUI) {
mySyncModelUI = syncModelUI;
}
@Override
public void update(CheckboxTreeViewer viewer) {
mySelected = null;
IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
setEnabled(selection.size() == 1);
if (selection.size() != 1) {
return;
}
mySelected = (SyncModelNode) selection.getFirstElement();
if (mySelected.isAutoSynchronized()) {
setText(Messages.SyncModelUI_action_disable_sync);
} else {
setText(Messages.SyncModelUI_action_enable_sync);
}
}
@Override
public void run() {
if (mySelected.isAutoSynchronized()) {
mySelected.setAutoSynchronized(false);
} else {
mySelected.setAutoSynchronized(true);
for (SyncModelNode nextDirectChild : mySelected.getChildren()) {
nextDirectChild.setChecked(true);
mySyncModelUI.setChecked(nextDirectChild, true);
}
}
mySyncModelUI.refresh(mySelected);
}
}
private static class BulkSelectAction extends SyncModelUIAction {
private final boolean mySelectNotUnselect;
private final CheckboxTreeViewer mySyncModelUI;
private SyncModelNode mySelected;
public BulkSelectAction(CheckboxTreeViewer syncModelUI, boolean selectNotUnselect) {
mySyncModelUI = syncModelUI;
mySelectNotUnselect = selectNotUnselect;
}
public void update(CheckboxTreeViewer viewer) {
mySelected = null;
IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
setEnabled(selection.size() == 1);
if (selection.size() != 1) {
return;
}
mySelected = (SyncModelNode) selection.getFirstElement();
setEnabled(!mySelected.isKnownLeaf() && !mySelected.getChildren().isEmpty());
}
@Override
public void run() {
for (SyncModelNode nextDirectChild : mySelected.getChildren()) {
if (!isFiltered(mySelected, nextDirectChild)) {
nextDirectChild.setChecked(mySelectNotUnselect);
mySyncModelUI.setChecked(nextDirectChild, mySelectNotUnselect);
}
}
if (!mySelectNotUnselect) {
mySelected.setAutoSynchronized(false);
}
mySyncModelUI.refresh(mySelected);
}
private boolean isFiltered(SyncModelNode parent, SyncModelNode child) {
for (ViewerFilter nextFilter : mySyncModelUI.getFilters()) {
if (!nextFilter.select(mySyncModelUI, parent, child)) {
return true;
}
}
return false;
}
}
private static class SetTypeAction extends SyncModelUIAction {
private final CheckboxTreeViewer mySyncModelUI;
private SyncModelNode mySelected;
private final String myType;
public SetTypeAction(CheckboxTreeViewer syncModelUI, MenuTypeHint menuTypeHint) {
setText(menuTypeHint.getMessage());
mySyncModelUI = syncModelUI;
myType = menuTypeHint.getType();
}
@Override
public void update(CheckboxTreeViewer viewer) {
mySelected = null;
IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
setEnabled(selection.size() == 1);
if (selection.size() != 1) {
return;
}
mySelected = (SyncModelNode) selection.getFirstElement();
}
@Override
public void run() {
mySelected.setChosenSyncModelViewType(myType);
mySyncModelUI.refresh(mySelected.getParent());
List<SyncModelNode> childNodes = new ArrayList<SyncModelNode>();
childNodes.add(mySelected);
for (int i = 0; i < childNodes.size(); i++) {
SyncModelNode node = childNodes.get(i);
mySyncModelUI.setChecked(node, node.isChecked());
childNodes.addAll(node.getChildren());
}
}
}
private class CheckboxFilteredTree extends FilteredTree {
public CheckboxFilteredTree(Composite parent, int treeStyle, PatternFilter filter) {
super(parent, treeStyle, filter, true);
}
@Override
protected TreeViewer doCreateTreeViewer(Composite parent, int style) {
return new CheckboxTreeViewer(parent, style);
}
public CheckboxTreeViewer getCheckboxTreeViewer() {
return (CheckboxTreeViewer) getViewer();
}
}
}