Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e7fb11159c64989395d37781cf86e2aac4b777cc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
/*******************************************************************************
 * Copyright (c) 2000, 2018 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.internal.ui.synchronize;

import org.eclipse.compare.*;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.internal.ui.synchronize.actions.OpenInCompareAction;
import org.eclipse.team.ui.synchronize.*;
import org.eclipse.ui.*;

/**
 * Abstract superclass for tree viewer advisors
 */
public abstract class AbstractTreeViewerAdvisor extends StructuredViewerAdvisor implements IAdaptable {

	private ICompareNavigator nav;
	private INavigatable navigatable;

	/**
	 * Interface used to implement navigation for tree viewers. This interface is used by
	 * {@link TreeViewerAdvisor#navigate(TreeViewer, boolean, boolean, boolean) to open}
	 * selections and navigate.
	 */
	public interface ITreeViewerAccessor {
		public void createChildren(TreeItem item);
		public void openSelection();
	}

	private class TreeCompareNavigator extends CompareNavigator {

		@Override
		protected INavigatable[] getNavigatables() {
			INavigatable navigatable = getNavigatable();
			return new INavigatable[] { navigatable };
		}

		@Override
		public boolean selectChange(boolean next) {
			if (getSubNavigator() != null) {
				if (getSubNavigator().hasChange(next)) {
					getSubNavigator().selectChange(next);
					return false;
				}
			}
			boolean noNextChange = super.selectChange(next);
			if (!noNextChange) {
				// Check to see if the selected element can be opened.
				// If it can't, try the next one
				Object selectedObject = AbstractTreeViewerAdvisor.this.getFirstElement(getViewer().getStructuredSelection());
				if (!hasCompareInput(selectedObject)) {
					return selectChange(next);
				}
			}
			return noNextChange;
		}

		private boolean hasCompareInput(Object selectedObject) {
			SyncInfo syncInfo = getSyncInfo(selectedObject);
			if(syncInfo != null) {
				return syncInfo.getLocal().getType() == IResource.FILE;
			}
			ISynchronizeParticipant p = getConfiguration().getParticipant();
			if (p instanceof ModelSynchronizeParticipant) {
				ModelSynchronizeParticipant msp = (ModelSynchronizeParticipant) p;
				return msp.hasCompareInputFor(selectedObject);
			}
			return true;
		}

		private SyncInfo getSyncInfo(Object obj) {
			if (obj instanceof SyncInfoModelElement) {
				return ((SyncInfoModelElement) obj).getSyncInfo();
			} else {
				return null;
			}
		}

		@Override
		public boolean hasChange(boolean next) {
			if (getSubNavigator() != null) {
				if (getSubNavigator().hasChange(next)) {
					return true;
				}
			}
			return super.hasChange(next);
		}

		private CompareNavigator getSubNavigator() {
			IWorkbenchSite ws = AbstractTreeViewerAdvisor.this.getConfiguration().getSite().getWorkbenchSite();
			if (ws instanceof IWorkbenchPartSite) {
				Object selectedObject = AbstractTreeViewerAdvisor.this.getFirstElement(getViewer().getStructuredSelection());
				IEditorPart editor = OpenInCompareAction.findOpenCompareEditor((IWorkbenchPartSite)ws, selectedObject, getConfiguration().getParticipant());
				if(editor != null) {
					// if an existing editor is open on the current selection, use it
					CompareEditorInput input = (CompareEditorInput)editor.getEditorInput();
					ICompareNavigator navigator = input.getNavigator();
					if (navigator instanceof TreeCompareNavigator) {
						// The input knows to use the global navigator.
						// Assume it set the input navigator property
						navigator = (ICompareNavigator)AbstractTreeViewerAdvisor.this.getConfiguration().getProperty(SynchronizePageConfiguration.P_INPUT_NAVIGATOR);
					}
					if (navigator instanceof CompareNavigator) {
						return (CompareNavigator) navigator;

					}
				}
			}
			return null;
		}

	}

	private static boolean hasNextPrev(TreeViewer viewer, TreeItem item, boolean next) {
		if (item == null || !(viewer instanceof ITreeViewerAccessor))
			return false;
		TreeItem children[] = null;
		if (next) {
			if (viewer.isExpandable(item.getData()))
				return true;
			while(item != null) {
				TreeItem parent = item.getParentItem();
				if (parent != null)
					children = parent.getItems();
				else
					children = item.getParent().getItems();
				if (children != null && children.length > 0) {
					if (children[children.length - 1] != item) {
						// The item is not the last so there must be a next
						return true;
					} else {
						// Set the parent as the item and go up one more level
						item = parent;
					}
				}
			}
		} else {
			while(item != null) {
				TreeItem parent = item.getParentItem();
				if (parent != null)
					children = parent.getItems();
				else
					children = item.getParent().getItems();
				if (children != null && children.length > 0) {
					if (children[0] != item) {
						// The item is not the first so there must be a previous
						return true;
					} else {
						// Set the parent as the item and go up one more level
						item = parent;
					}
				}
			}
		}
		return false;
	}

	private static TreeItem findNextPrev(TreeViewer viewer, TreeItem item, boolean next) {
		if (item == null || !(viewer instanceof ITreeViewerAccessor))
			return null;
		TreeItem children[] = null;
		ITreeViewerAccessor treeAccessor = (ITreeViewerAccessor) viewer;
		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) {
						treeAccessor.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);
			treeAccessor.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 static void setSelection(TreeViewer viewer, TreeItem ti, boolean fireOpen, boolean expandOnly) {
		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);
				if (expandOnly) {
					viewer.expandToLevel(data, 0);
				} else {
					viewer.setSelection(selection, true);
					ISelection currentSelection = viewer.getSelection();
					if (fireOpen && currentSelection != null && selection.equals(currentSelection)) {
						if (viewer instanceof ITreeViewerAccessor) {
							((ITreeViewerAccessor) viewer).openSelection();
						}
					}
				}
			}
		}
	}

	/**
	 * 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 not override.
	 * @param viewer
	 *
	 * @param next if <code>true</code> the next node is selected, otherwise the previous node
	 * @param fireOpen
	 * @param expandOnly
	 * @return <code>true</code> if at end (or beginning)
	 */
	public static boolean navigate(TreeViewer viewer, boolean next, boolean fireOpen, boolean expandOnly) {
		Tree tree = viewer.getTree();
		if (tree == null)
			return false;
		TreeItem item = getNextItem(viewer, next);
		if (item != null)
			setSelection(viewer, item, fireOpen, expandOnly);
		return item == null;
	}

	private static TreeItem getNextItem(TreeViewer viewer, boolean next) {
		TreeItem item = getCurrentItem(viewer);
		if (item != null) {
			while (true) {
				item = findNextPrev(viewer, item, next);
				if (item == null)
					break;
				if (item.getItemCount() <= 0)
					break;
			}
		}
		return item;
	}

	private static TreeItem getCurrentItem(TreeViewer viewer) {
		Tree tree = viewer.getTree();
		if (tree == null)
			return null;
		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];
			}
		}
		return item;
	}

	private static boolean hasChange(TreeViewer viewer, boolean next) {
		TreeItem item = getCurrentItem(viewer);
		if (item != null) {
			return hasNextPrev(viewer, item, next);
		}
		return false;
	}

	public AbstractTreeViewerAdvisor(ISynchronizePageConfiguration configuration) {
		super(configuration);
		ICompareNavigator nav = (ICompareNavigator)configuration.getProperty(SynchronizePageConfiguration.P_NAVIGATOR);
		if (nav == null) {
			configuration.setProperty(SynchronizePageConfiguration.P_NAVIGATOR, getAdapter(ICompareNavigator.class));
		}
		configuration.addActionContribution(new NavigationActionGroup());
	}

	/**
	 * Allow navigation in tree viewers.
	 *
	 * @param next if <code>true</code> then navigate forwards, otherwise navigate
	 * backwards.
	 * @return <code>true</code> if the end is reached, and <code>false</code> otherwise.
	 */
	public boolean navigate(boolean next) {
		return navigate((TreeViewer)getViewer(), next, false, false);
	}

	protected boolean hasChange(boolean next) {
		return hasChange((TreeViewer)getViewer(), next);
	}

	/* (non-Javadoc)
	 * Allow adding an advisor to the PartNavigator and support coordinated
 	 * navigation between several objects.
	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public <T> T getAdapter(Class<T> adapter) {
		if(adapter == ICompareNavigator.class) {
			if(nav == null) {
				nav = new TreeCompareNavigator();
			}
			return (T) nav;
		}
		if(adapter == INavigatable.class) {
			return (T) getNavigatable();
		}
		return null;
	}

	private synchronized INavigatable getNavigatable() {
		if(navigatable == null) {
			navigatable = new INavigatable() {
				@Override
				public boolean selectChange(int flag) {
					if (flag == INavigatable.FIRST_CHANGE) {
						getViewer().setSelection(StructuredSelection.EMPTY);
						flag = INavigatable.NEXT_CHANGE;
					} else if (flag == INavigatable.LAST_CHANGE) {
						getViewer().setSelection(StructuredSelection.EMPTY);
						flag = INavigatable.PREVIOUS_CHANGE;
					}
					return navigate((TreeViewer)getViewer(), flag == INavigatable.NEXT_CHANGE, true, false);
				}

				@Override
				public boolean openSelectedChange() {
					Viewer v = getViewer();
					if (v instanceof ITreeViewerAccessor && !v.getControl().isDisposed()) {
						ITreeViewerAccessor tva = (ITreeViewerAccessor) v;
						tva.openSelection();
						return true;
					}
					return false;
				}
				@Override
				public boolean hasChange(int changeFlag) {
					return AbstractTreeViewerAdvisor.this.hasChange(changeFlag == INavigatable.NEXT_CHANGE);
				}
				@Override
				public Object getInput() {
					return getViewer().getInput();
				}

			};
		}
		return navigatable;
	}

	/**
	 * Handles a double-click event from the viewer. Expands or collapses a folder when double-clicked.
	 *
	 * @param viewer the viewer
	 * @param event the double-click event
	 */
	@Override
	protected boolean handleDoubleClick(StructuredViewer viewer, DoubleClickEvent event) {
		if (super.handleDoubleClick(viewer, event)) return true;
		IStructuredSelection selection = (IStructuredSelection) event.getSelection();
		Object element = getFirstElementOrPath(selection);
		AbstractTreeViewer treeViewer = (AbstractTreeViewer) getViewer();
		if(element != null) {
			if (treeViewer.getExpandedState(element)) {
				treeViewer.collapseToLevel(element, AbstractTreeViewer.ALL_LEVELS);
			} else {
				expandToNextDiff(element);
			}
		}
		return true;
	}

	private Object getFirstElementOrPath(IStructuredSelection selection) {
		if (selection instanceof TreeSelection) {
			TreeSelection ts = (TreeSelection) selection;
			TreePath[] paths = ts.getPaths();
			if (paths.length > 0)
				return paths[0];
		}
		Object element = selection.getFirstElement();
		return element;
	}

	private Object getFirstElement(IStructuredSelection selection) {
		Object element = getFirstElementOrPath(selection);
		if (element instanceof TreePath) {
			TreePath path = (TreePath) element;
			element = path.getLastSegment();
		}
		return element;
	}

	protected void expandToNextDiff(Object elementOrPath) {
		AbstractTreeViewerAdvisor.navigate((TreeViewer)getViewer(), true /* next */, false /* no-open */, true /* only-expand */);
	}
}

Back to the top