blob: cd105654b25be658c91e1d527df616eef2823c9a [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.Iterator;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.NullProgressMonitor;
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.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.subscribers.SyncInfo;
import org.eclipse.team.internal.ui.*;
import org.eclipse.team.internal.ui.jobs.JobBusyCursor;
import org.eclipse.team.internal.ui.synchronize.actions.*;
import org.eclipse.team.internal.ui.synchronize.sets.SubscriberInput;
import org.eclipse.team.internal.ui.synchronize.views.*;
import org.eclipse.team.ui.synchronize.actions.SubscriberAction;
import org.eclipse.team.ui.synchronize.actions.SyncInfoFilter;
import org.eclipse.ui.*;
import org.eclipse.ui.part.*;
import org.eclipse.ui.views.navigator.ResourceSorter;
/**
* A synchronize view page that works with participants that are subclasses of
* {@link TeamSubscriberParticipant}. It shows changes in the tree or table view
* and supports navigation, opening, and filtering changes.
* <p>
* Clients can subclass to extend the label decoration or add action bar
* contributions. For more extensive modifications, clients should create
* their own custom control.
* </p>
* @since 3.0
*/
public class TeamSubscriberParticipantPage implements IPageBookViewPage, IPropertyChangeListener {
// The viewer that is shown in the view. Currently this can be either a table or tree viewer.
private StructuredViewer viewer;
// Parent composite of this view. It is remembered so that we can dispose of its children when
// the viewer type is switched.
private Composite composite = null;
private boolean settingWorkingSet = false;
// Viewer type constants
private int layout;
// Remembering the current input and the previous.
private SubscriberInput input = null;
// A set of common actions. They are hooked to the active SubscriberInput and must
// be reset when the input changes.
// private SyncViewerActions actions;
private JobBusyCursor busyCursor;
private ISynchronizeView view;
private TeamSubscriberParticipant participant;
private IPageSite site;
public final static int[] INCOMING_MODE_FILTER = new int[] {SyncInfo.CONFLICTING, SyncInfo.INCOMING};
public final static int[] OUTGOING_MODE_FILTER = new int[] {SyncInfo.CONFLICTING, SyncInfo.OUTGOING};
public final static int[] BOTH_MODE_FILTER = new int[] {SyncInfo.CONFLICTING, SyncInfo.INCOMING, SyncInfo.OUTGOING};
public final static int[] CONFLICTING_MODE_FILTER = new int[] {SyncInfo.CONFLICTING};
// Actions
private OpenWithActionGroup openWithActions;
private NavigateAction gotoNext;
private NavigateAction gotoPrevious;
private Action toggleLayoutTree;
private Action toggleLayoutTable;
private RefactorActionGroup refactorActions;
private SyncViewerShowPreferencesAction showPreferences;
private RefreshAction refreshAction;
private ComparisonCriteriaActionGroup comparisonCriteria;
private Action collapseAll;
private Action expandAll;
private WorkingSetFilterActionGroup workingSetGroup;
private StatusLineContributionGroup statusLine;
/**
* Constructs a new SynchronizeView.
*/
public TeamSubscriberParticipantPage(TeamSubscriberParticipant page, ISynchronizeView view, SubscriberInput input) {
this.participant = page;
this.view = view;
this.input = input;
layout = getStore().getInt(IPreferenceIds.SYNCVIEW_VIEW_TYPE);
if (layout != TeamSubscriberParticipant.TREE_LAYOUT) {
layout = TeamSubscriberParticipant.TABLE_LAYOUT;
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#createControl(org.eclipse.swt.widgets.Composite)
*/
public void createControl(Composite parent) {
composite = new Composite(parent, SWT.NONE);
GridLayout gridLayout= new GridLayout();
gridLayout.makeColumnsEqualWidth= false;
gridLayout.marginWidth= 0;
gridLayout.marginHeight = 0;
gridLayout.verticalSpacing = 0;
composite.setLayout(gridLayout);
// Create the busy cursor with no control to start with (createViewer will set it)
busyCursor = new JobBusyCursor(null /* control */, SubscriberAction.SUBSCRIBER_JOB_TYPE);
createViewer(composite);
// create actions
openWithActions = new OpenWithActionGroup(this);
refactorActions = new RefactorActionGroup(view);
gotoNext = new NavigateAction(view, this, INavigableControl.NEXT);
gotoPrevious = new NavigateAction(view, this, INavigableControl.PREVIOUS);
comparisonCriteria = new ComparisonCriteriaActionGroup(input);
toggleLayoutTable = new ToggleViewLayoutAction(participant, TeamSubscriberParticipant.TABLE_LAYOUT);
toggleLayoutTree = new ToggleViewLayoutAction(participant, TeamSubscriberParticipant.TREE_LAYOUT);
workingSetGroup = new WorkingSetFilterActionGroup(getSite().getShell(), this, view, participant);
showPreferences = new SyncViewerShowPreferencesAction(view.getSite().getShell());
refreshAction = new RefreshAction(getSite().getPage(), input, true /* refresh all */);
statusLine = new StatusLineContributionGroup(this.input);
collapseAll = new Action() {
public void run() {
collapseAll();
}
};
Utils.initAction(collapseAll, "action.collapseAll."); //$NON-NLS-1$
expandAll = new Action() {
public void run() {
Viewer viewer = getViewer();
ISelection selection = viewer.getSelection();
if(viewer instanceof AbstractTreeViewer && ! selection.isEmpty()) {
Iterator elements = ((IStructuredSelection)selection).iterator();
while (elements.hasNext()) {
Object next = elements.next();
((AbstractTreeViewer) viewer).expandToLevel(next, AbstractTreeViewer.ALL_LEVELS);
}
}
}
};
Utils.initAction(expandAll, "action.expandAll."); //$NON-NLS-1$
participant.addPropertyChangeListener(this);
TeamUIPlugin.getPlugin().getPreferenceStore().addPropertyChangeListener(this);
updateMode(participant.getMode());
}
/* (non-Javadoc)
* @see org.eclipse.ui.IViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
*/
public void init(IPageSite site) throws PartInitException {
this.site = site;
}
private void hookContextMenu() {
if(getViewer() != null) {
MenuManager menuMgr = new MenuManager(participant.getId()); //$NON-NLS-1$
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
setContextMenu(manager);
}
});
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
getSite().registerContextMenu(participant.getId(), menuMgr, viewer);
}
}
private void setContextMenu(IMenuManager manager) {
openWithActions.fillContextMenu(manager);
refactorActions.fillContextMenu(manager);
manager.add(new Separator());
manager.add(expandAll);
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
/**
* Toggles between label/tree/table viewers.
*/
public void switchViewerType(int viewerType) {
if(viewer == null || viewerType != layout) {
if (composite == null || composite.isDisposed()) return;
IStructuredSelection oldSelection = null;
if(viewer != null) {
oldSelection = (IStructuredSelection)viewer.getSelection();
}
layout = viewerType;
getStore().setValue(IPreferenceIds.SYNCVIEW_VIEW_TYPE, layout);
disposeChildren(composite);
createViewer(composite);
composite.layout();
if(oldSelection == null || oldSelection.size() == 0) {
//gotoDifference(INavigableControl.NEXT);
} else {
viewer.setSelection(oldSelection, true);
}
}
}
/**
* Adds the listeners to the viewer.
*/
private void initializeListeners() {
viewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
updateStatusLine((IStructuredSelection)event.getSelection());
}
});
viewer.addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
handleDoubleClick(event);
}
});
viewer.addOpenListener(new IOpenListener() {
public void open(OpenEvent event) {
handleOpen(event);
}
});
}
private void createViewer(Composite parent) {
//tbMgr.createControl(parent);
switch(layout) {
case TeamSubscriberParticipant.TREE_LAYOUT:
createTreeViewerPartControl(parent);
break;
case TeamSubscriberParticipant.TABLE_LAYOUT:
createTableViewerPartControl(parent);
break;
}
viewer.setInput(input);
viewer.getControl().setFocus();
initializeListeners();
hookContextMenu();
getSite().setSelectionProvider(getViewer());
busyCursor.setControl(viewer.getControl());
}
protected ILabelProvider getLabelProvider() {
return new TeamSubscriberParticipantLabelProvider();
}
private void createTreeViewerPartControl(Composite parent) {
GridData data = new GridData(GridData.FILL_BOTH);
viewer = new SyncTreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setLabelProvider(getLabelProvider());
viewer.setSorter(new SyncViewerSorter(ResourceSorter.NAME));
((TreeViewer)viewer).getTree().setLayoutData(data);
}
private void createTableViewerPartControl(Composite parent) {
// Create the table
Table table = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
table.setLinesVisible(true);
GridData data = new GridData(GridData.FILL_BOTH);
table.setLayoutData(data);
// Set the table layout
TableLayout layout = new TableLayout();
table.setLayout(layout);
// Create the viewer
TableViewer tableViewer = new SyncTableViewer(table);
// Create the table columns
createColumns(table, layout, tableViewer);
// Set the table contents
viewer = tableViewer;
viewer.setContentProvider(new SyncSetTableContentProvider());
viewer.setLabelProvider(getLabelProvider());
viewer.setSorter(new SyncViewerTableSorter());
}
/**
* Creates the columns for the sync viewer table.
*/
private void createColumns(Table table, TableLayout layout, TableViewer viewer) {
SelectionListener headerListener = SyncViewerTableSorter.getColumnListener(viewer);
// revision
TableColumn col = new TableColumn(table, SWT.NONE);
col.setResizable(true);
col.setText(Policy.bind("TeamSubscriberParticipantPage.7")); //$NON-NLS-1$
col.addSelectionListener(headerListener);
layout.addColumnData(new ColumnWeightData(30, true));
// tags
col = new TableColumn(table, SWT.NONE);
col.setResizable(true);
col.setText(Policy.bind("TeamSubscriberParticipantPage.8")); //$NON-NLS-1$
col.addSelectionListener(headerListener);
layout.addColumnData(new ColumnWeightData(50, true));
}
private void disposeChildren(Composite parent) {
// Null out the control of the busy cursor while we are switching viewers
busyCursor.setControl(null);
Control[] children = parent.getChildren();
for (int i = 0; i < children.length; i++) {
Control control = children[i];
control.dispose();
}
}
private void handleOpen(OpenEvent event) {
openWithActions.openInCompareEditor();
}
/**
* Handles a double-click event from the viewer.
* Expands or collapses a folder when double-clicked.
*
* @param event the double-click event
* @since 2.0
*/
private void handleDoubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
Object element = selection.getFirstElement();
// Double-clicking should expand/collapse containers
if (viewer instanceof TreeViewer) {
TreeViewer tree = (TreeViewer)viewer;
if (tree.isExpandable(element)) {
tree.setExpandedState(element, !tree.getExpandedState(element));
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#setFocus()
*/
public void setFocus() {
if (viewer == null) return;
viewer.getControl().setFocus();
}
public StructuredViewer getViewer() {
return viewer;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose() {
busyCursor.dispose();
statusLine.dispose();
}
/*
* Return the current input for the view.
*/
public SubscriberInput getInput() {
return input;
}
public void collapseAll() {
if (viewer == null || !(viewer instanceof AbstractTreeViewer)) return;
viewer.getControl().setRedraw(false);
((AbstractTreeViewer)viewer).collapseToLevel(viewer.getInput(), TreeViewer.ALL_LEVELS);
viewer.getControl().setRedraw(true);
}
/**
* This method enables "Show In" support for this view
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class key) {
if (key == IShowInSource.class) {
return new IShowInSource() {
public ShowInContext getShowInContext() {
StructuredViewer v = getViewer();
if (v == null) return null;
return new ShowInContext(null, v.getSelection());
}
};
}
return null;
}
/**
* Updates the message shown in the status line.
*
* @param selection the current selection
*/
private void updateStatusLine(IStructuredSelection selection) {
String msg = getStatusLineMessage(selection);
getSite().getActionBars().getStatusLineManager().setMessage(msg);
}
/**
* Returns the message to show in the status line.
*
* @param selection the current selection
* @return the status line message
* @since 2.0
*/
private String getStatusLineMessage(IStructuredSelection selection) {
if (selection.size() == 1) {
IResource resource = getResource(selection.getFirstElement());
if (resource == null) {
return Policy.bind("SynchronizeView.12"); //$NON-NLS-1$
} else {
return resource.getFullPath().makeRelative().toString();
}
}
if (selection.size() > 1) {
return selection.size() + Policy.bind("SynchronizeView.13"); //$NON-NLS-1$
}
return ""; //$NON-NLS-1$
}
private IResource getResource(Object object) {
return SyncSetContentProvider.getResource(object);
}
public void selectAll() {
Viewer viewer = getViewer();
if (viewer instanceof TableViewer) {
TableViewer table = (TableViewer)viewer;
table.getTable().selectAll();
} else {
// Select All in a tree doesn't really work well
}
}
private IPreferenceStore getStore() {
return TeamUIPlugin.getPlugin().getPreferenceStore();
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#getControl()
*/
public Control getControl() {
return composite;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#setActionBars(org.eclipse.ui.IActionBars)
*/
public void setActionBars(IActionBars actionBars) {
if(actionBars != null) {
IToolBarManager manager = actionBars.getToolBarManager();
// toolbar
manager.add(refreshAction);
manager.add(comparisonCriteria);
manager.add(new Separator());
manager.add(gotoNext);
manager.add(gotoPrevious);
manager.add(collapseAll);
manager.add(new Separator());
// view menu
updateViewMenu(actionBars);
// status line
statusLine.fillActionBars(actionBars);
}
}
private void updateViewMenu(IActionBars actionBars) {
IMenuManager menu = actionBars.getMenuManager();
menu.removeAll();
MenuManager layoutMenu = new MenuManager(Policy.bind("action.layout.label")); //$NON-NLS-1$
layoutMenu.add(toggleLayoutTable);
layoutMenu.add(toggleLayoutTree);
workingSetGroup.fillActionBars(actionBars);
menu.add(layoutMenu);
menu.add(new Separator());
menu.add(showPreferences);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPageBookViewPage#getSite()
*/
public IPageSite getSite() {
return this.site;
}
/* (non-Javadoc)
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
// Layout change
if(event.getProperty().equals(TeamSubscriberParticipant.P_SYNCVIEWPAGE_LAYOUT)) {
switchViewerType(((Integer)event.getNewValue()).intValue());
// Direction mode change
} else if(event.getProperty().equals(TeamSubscriberParticipant.P_SYNCVIEWPAGE_MODE)) {
updateMode(((Integer)event.getNewValue()).intValue());
// Working set changed via menu selection - notify participant and
// do all the real work when we get the next workset changed event
} else if(event.getProperty().equals(WorkingSetFilterActionGroup.CHANGE_WORKING_SET)) {
if(settingWorkingSet) return;
participant.setWorkingSet((IWorkingSet)event.getNewValue());
// Working set changed programatically
} else if(event.getProperty().equals(TeamSubscriberParticipant.P_SYNCVIEWPAGE_WORKINGSET)) {
settingWorkingSet = true;
Object newValue = event.getNewValue();
if (newValue instanceof IWorkingSet) {
workingSetGroup.setWorkingSet((IWorkingSet)newValue);
} else if (newValue == null) {
workingSetGroup.setWorkingSet(null);
}
settingWorkingSet = false;
} else if(event.getProperty().equals(IPreferenceIds.SYNCVIEW_VIEW_SYNCINFO_IN_LABEL)) {
getViewer().refresh(true /* update labels */);
}
}
private void updateMode(int mode) {
int[] modeFilter = BOTH_MODE_FILTER;
switch(mode) {
case TeamSubscriberParticipant.INCOMING_MODE:
modeFilter = INCOMING_MODE_FILTER; break;
case TeamSubscriberParticipant.OUTGOING_MODE:
modeFilter = OUTGOING_MODE_FILTER; break;
case TeamSubscriberParticipant.BOTH_MODE:
modeFilter = BOTH_MODE_FILTER; break;
case TeamSubscriberParticipant.CONFLICTING_MODE:
modeFilter = CONFLICTING_MODE_FILTER; break;
}
try {
input.setFilter(
new SyncInfoFilter.AndSyncInfoFilter(
new SyncInfoFilter[] {
new SyncInfoFilter.SyncInfoDirectionFilter(modeFilter),
new SyncInfoFilter.SyncInfoChangeTypeFilter(new int[] {SyncInfo.ADDITION, SyncInfo.DELETION, SyncInfo.CHANGE}),
new SyncInfoFilter.PseudoConflictFilter()
}), new NullProgressMonitor());
} catch (TeamException e) {
Utils.handleError(getSite().getShell(), e, Policy.bind("SynchronizeView.16"), e.getMessage()); //$NON-NLS-1$
}
}
/**
* @return Returns the participant.
*/
public TeamSubscriberParticipant getParticipant() {
return participant;
}
/**
* @return Returns the view.
*/
public ISynchronizeView getSynchronizeView() {
return view;
}
}