blob: b4321d1dbc382249d6f5460f7b3851212348e059 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.ui.synchronize;
import java.util.ArrayList;
import java.util.Iterator;
import org.eclipse.compare.*;
import org.eclipse.compare.internal.INavigatable;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.action.*;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.*;
import org.eclipse.team.core.subscribers.*;
import org.eclipse.team.internal.ui.*;
import org.eclipse.team.internal.ui.synchronize.views.*;
import org.eclipse.team.ui.synchronize.actions.INavigableControl;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.internal.PluginAction;
import org.eclipse.ui.views.navigator.ResourceSorter;
public class SyncInfoDiffTreeViewer extends TreeViewer implements INavigableControl {
private SyncInfoSet set;
// Actions
private Action expandAll;
private NavigationAction nextAction;
private NavigationAction previousAction;
private boolean acceptParticipantMenuContributions = false;
private MenuManager menuMgr = null;
private String menuId;
/**
* Change the tree layout between using compressed folders and regular folders
* when the user setting is changed.
*/
private IPropertyChangeListener propertyListener = new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(IPreferenceIds.SYNCVIEW_COMPRESS_FOLDERS)) {
setTreeViewerContentProvider();
}
}
};
public SyncInfoDiffTreeViewer(Composite parent, String menuId, SyncInfoSet set) {
super(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
this.menuId = menuId;
this.set = set;
GridData data = new GridData(GridData.FILL_BOTH);
setSorter(new SyncViewerSorter(ResourceSorter.NAME));
setLabelProvider(new TeamSubscriberParticipantLabelProvider());
getTree().setLayoutData(data);
getStore().addPropertyChangeListener(propertyListener);
setTreeViewerContentProvider();
addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
handleDoubleClick(event);
}
});
getTree().setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());
INavigatable nav= new INavigatable() {
public boolean gotoDifference(boolean next) {
// Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
return internalNavigate(next, true);
}
};
getTree().setData(INavigatable.NAVIGATOR_PROPERTY, nav);
createActions();
hookContextMenu();
createToolBarActions(parent);
setInput(new SyncInfoDiffNode(getSyncSet(), ResourcesPlugin.getWorkspace().getRoot()));
}
protected Object getTitle() {
return "Synchronization Changes";
}
public void setAcceptParticipantMenuContributions(boolean accept) {
this.acceptParticipantMenuContributions = accept;
if(acceptParticipantMenuContributions) {
IWorkbenchPartSite site = Utils.findSite(getControl());
if(site == null) {
site = Utils.findSite();
}
if(site != null) {
site.registerContextMenu(menuId, menuMgr, this);
}
}
}
private void createToolBarActions(Composite parent) {
ToolBarManager tbm= CompareViewerPane.getToolBarManager(parent);
if (tbm != null) {
tbm.removeAll();
tbm.add(new Separator("navigation")); //$NON-NLS-1$
createToolItems(tbm);
tbm.update(true);
}
}
protected void createToolItems(IToolBarManager tbm) {
nextAction= new NavigationAction(true);
tbm.appendToGroup("navigation", nextAction); //$NON-NLS-1$
previousAction= new NavigationAction(false);
tbm.appendToGroup("navigation", previousAction); //$NON-NLS-1$
}
protected SyncInfoSet getSyncSet() {
return set;
}
private void createActions() {
expandAll = new Action() {
public void run() {
expandAllFromSelection();
}
};
Utils.initAction(expandAll, "action.expandAll."); //$NON-NLS-1$
}
protected void expandAllFromSelection() {
ISelection selection = getSelection();
if(! selection.isEmpty()) {
Iterator elements = ((IStructuredSelection)selection).iterator();
while (elements.hasNext()) {
Object next = elements.next();
expandToLevel(next, AbstractTreeViewer.ALL_LEVELS);
}
}
}
protected void fillContextMenu(IMenuManager manager) {
manager.add(expandAll);
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void setTreeViewerContentProvider() {
if (getStore().getBoolean(IPreferenceIds.SYNCVIEW_COMPRESS_FOLDERS)) {
setContentProvider(new CompressedFolderContentProvider());
} else {
setContentProvider(new SyncSetTreeContentProvider());
}
}
/**
* Return the preference store for this plugin.
* @return IPreferenceStore for this plugin
*/
private IPreferenceStore getStore() {
return TeamUIPlugin.getPlugin().getPreferenceStore();
}
protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {
Object[] changed= event.getElements();
if (changed != null && getInput() != null) {
ArrayList others= new ArrayList();
for (int i= 0; i < changed.length; i++) {
Object curr = changed[i];
if (curr instanceof IResource) {
IContentProvider provider = getContentProvider();
if (provider != null && provider instanceof SyncSetContentProvider) {
curr = ((SyncSetContentProvider)provider).getModelObject((IResource)curr);
}
}
others.add(curr);
}
if (others.isEmpty()) {
return;
}
event= new LabelProviderChangedEvent((IBaseLabelProvider) event.getSource(), others.toArray());
}
super.handleLabelProviderChanged(event);
}
/**
* Cleanup listeners and call super for content provider and label provider disposal.
*/
protected void handleDispose(DisposeEvent event) {
super.handleDispose(event);
getStore().removePropertyChangeListener(propertyListener);
}
/**
* Handles a double-click event from the viewer.
* Expands or collapses a folder when double-clicked.
*
* @param event the double-click event
*/
protected void handleDoubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
Object element = selection.getFirstElement();
// Double-clicking should expand/collapse containers
if(isExpandable(element)) {
setExpandedState(element, ! getExpandedState(element));
}
}
protected void hookContextMenu() {
menuMgr = new MenuManager(menuId); //$NON-NLS-1$
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
fillContextMenu(manager);
}
});
Menu menu = menuMgr.createContextMenu(getControl());
menu.addMenuListener(new MenuListener() {
public void menuHidden(MenuEvent e) {
}
// Hack to allow action contributions to update their
// state before the menu is shown. This is required when
// the state of the selection changes and the contributions
// need to update enablement based on this.
public void menuShown(MenuEvent e) {
IContributionItem[] items = menuMgr.getItems();
for (int i = 0; i < items.length; i++) {
IContributionItem item = items[i];
if(item instanceof ActionContributionItem) {
IAction actionItem = ((ActionContributionItem)item).getAction();
if(actionItem instanceof PluginAction) {
((PluginAction)actionItem).selectionChanged(getSelection());
}
}
}
}
});
getControl().setMenu(menu);
if(acceptParticipantMenuContributions) {
IWorkbenchPartSite site = Utils.findSite(getControl());
if(site == null) {
site = Utils.findSite();
}
if(site != null) {
site.registerContextMenu(menuId, menuMgr, this);
}
}
}
protected void inputChanged(Object in, Object oldInput) {
super.inputChanged(in, oldInput);
if (in != oldInput) {
initialSelection();
}
}
/**
* This hook method is called from within <code>inputChanged</code>
* after a new input has been set but before any controls are updated.
* This default implementation calls <code>navigate(true)</code>
* to select and expand the first leaf node.
* Clients can override this method and are free to decide whether
* they want to call the inherited method.
*
* @since 2.0
*/
protected void initialSelection() {
internalNavigate(false, true);
}
/**
* Selects the next (or previous) node of the current selection.
* If there is no current selection the first (last) node in the tree is selected.
* Wraps around at end or beginning.
* Clients may override.
*
* @param next if <code>true</code> the next node is selected, otherwise the previous node
*/
public boolean gotoDifference(int direction) {
boolean next = direction == INavigableControl.NEXT ? true : false;
return internalNavigate(next, false);
}
public void updateCompareEditorInput(CompareEditorInput input) {
nextAction.setCompareEditorInput(input);
previousAction.setCompareEditorInput(input);
}
/**
* Selects the next (or previous) node of the current selection.
* If there is no current selection the first (last) node in the tree is selected.
* Wraps around at end or beginning.
* Clients may override.
*
* @param next if <code>true</code> the next node is selected, otherwise the previous node
* @return <code>true</code> if at end (or beginning)
*/
private boolean internalNavigate(boolean next, boolean fireOpen) {
Control c= getControl();
if (!(c instanceof Tree))
return false;
Tree tree= (Tree) c;
TreeItem item= null;
TreeItem children[]= tree.getSelection();
if (children != null && children.length > 0)
item= children[0];
if (item == null) {
children= tree.getItems();
if (children != null && children.length > 0) {
item= children[0];
if (item != null && item.getItemCount() <= 0) {
internalSetSelection(item, fireOpen); // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
return false;
}
}
}
while (true) {
item= findNextPrev(item, next);
if (item == null)
break;
if (item.getItemCount() <= 0)
break;
}
if (item != null) {
internalSetSelection(item, fireOpen); // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
return false;
}
return true;
}
private TreeItem findNextPrev(TreeItem item, boolean next) {
if (item == null)
return null;
TreeItem children[]= null;
if (!next) {
TreeItem parent= item.getParentItem();
if (parent != null)
children= parent.getItems();
else
children= item.getParent().getItems();
if (children != null && children.length > 0) {
// goto previous child
int index= 0;
for (; index < children.length; index++)
if (children[index] == item)
break;
if (index > 0) {
item= children[index-1];
while (true) {
createChildren(item);
int n= item.getItemCount();
if (n <= 0)
break;
item.setExpanded(true);
item= item.getItems()[n-1];
}
// previous
return item;
}
}
// go up
return parent;
} else {
item.setExpanded(true);
createChildren(item);
if (item.getItemCount() > 0) {
// has children: go down
children= item.getItems();
return children[0];
}
while (item != null) {
children= null;
TreeItem parent= item.getParentItem();
if (parent != null)
children= parent.getItems();
else
children= item.getParent().getItems();
if (children != null && children.length > 0) {
// goto next child
int index= 0;
for (; index < children.length; index++)
if (children[index] == item)
break;
if (index < children.length-1) {
// next
return children[index+1];
}
}
// go up
item= parent;
}
}
return item;
}
private void internalSetSelection(TreeItem ti, boolean fireOpen) {
if (ti != null) {
Object data= ti.getData();
if (data != null) {
// Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106
ISelection selection= new StructuredSelection(data);
setSelection(selection, true);
ISelection currentSelection= getSelection();
if (fireOpen && currentSelection != null && selection.equals(currentSelection)) {
fireOpen(new OpenEvent(this, selection));
}
}
}
}
}