1 /*******************************************************************************
2  * Copyright (c) 2006, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  * IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.team.internal.ui.mapping;
15 
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Set;
21 
22 import org.eclipse.core.resources.mapping.IModelProviderDescriptor;
23 import org.eclipse.core.resources.mapping.ModelProvider;
24 import org.eclipse.core.resources.mapping.ResourceTraversal;
25 import org.eclipse.core.runtime.Adapters;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IPath;
28 import org.eclipse.core.runtime.IProgressMonitor;
29 import org.eclipse.core.runtime.IStatus;
30 import org.eclipse.core.runtime.MultiStatus;
31 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
32 import org.eclipse.core.runtime.jobs.Job;
33 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
34 import org.eclipse.jface.dialogs.ErrorDialog;
35 import org.eclipse.jface.preference.PreferenceStore;
36 import org.eclipse.jface.util.IPropertyChangeListener;
37 import org.eclipse.jface.util.PropertyChangeEvent;
38 import org.eclipse.jface.viewers.TreeViewer;
39 import org.eclipse.jface.viewers.Viewer;
40 import org.eclipse.osgi.util.NLS;
41 import org.eclipse.swt.SWT;
42 import org.eclipse.swt.events.SelectionEvent;
43 import org.eclipse.swt.events.SelectionListener;
44 import org.eclipse.swt.layout.GridData;
45 import org.eclipse.swt.layout.GridLayout;
46 import org.eclipse.swt.widgets.Button;
47 import org.eclipse.swt.widgets.Composite;
48 import org.eclipse.swt.widgets.Label;
49 import org.eclipse.team.core.diff.FastDiffFilter;
50 import org.eclipse.team.core.diff.IDiffChangeEvent;
51 import org.eclipse.team.core.diff.IDiffChangeListener;
52 import org.eclipse.team.core.diff.IDiffTree;
53 import org.eclipse.team.core.diff.IThreeWayDiff;
54 import org.eclipse.team.core.mapping.ISynchronizationContext;
55 import org.eclipse.team.core.mapping.ISynchronizationScope;
56 import org.eclipse.team.internal.core.subscribers.SubscriberDiffTreeEventHandler;
57 import org.eclipse.team.internal.ui.TeamUIMessages;
58 import org.eclipse.team.internal.ui.TeamUIPlugin;
59 import org.eclipse.team.internal.ui.Utils;
60 import org.eclipse.team.internal.ui.synchronize.AbstractSynchronizePage;
61 import org.eclipse.team.internal.ui.synchronize.ForwardingChangesSection;
62 import org.eclipse.team.internal.ui.synchronize.StartupPreferencePage;
63 import org.eclipse.team.internal.ui.synchronize.SynchronizePageConfiguration;
64 import org.eclipse.team.internal.ui.synchronize.SynchronizeView;
65 import org.eclipse.team.ui.ISharedImages;
66 import org.eclipse.team.ui.TeamUI;
67 import org.eclipse.team.ui.mapping.ISynchronizationCompareAdapter;
68 import org.eclipse.team.ui.mapping.ITeamContentProviderDescriptor;
69 import org.eclipse.team.ui.mapping.ITeamContentProviderManager;
70 import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
71 import org.eclipse.team.ui.synchronize.ModelOperation;
72 import org.eclipse.team.ui.synchronize.ModelSynchronizeParticipant;
73 import org.eclipse.ui.forms.events.HyperlinkAdapter;
74 import org.eclipse.ui.forms.events.HyperlinkEvent;
75 import org.eclipse.ui.forms.widgets.Hyperlink;
76 
77 public class DiffTreeChangesSection extends ForwardingChangesSection implements IDiffChangeListener, IPropertyChangeListener, IEmptyTreeListener {
78 
79 	private ISynchronizationContext context;
80 	private IStatus[] errors;
81 	private boolean showingError;
82 	private JobChangeAdapter jobChangeListener;
83 
84 	public interface ITraversalFactory {
getTraversals(ISynchronizationScope scope)85 		ResourceTraversal[] getTraversals(ISynchronizationScope scope);
86 	}
87 
DiffTreeChangesSection(Composite parent, AbstractSynchronizePage page, ISynchronizePageConfiguration configuration)88 	public DiffTreeChangesSection(Composite parent, AbstractSynchronizePage page, ISynchronizePageConfiguration configuration) {
89 		super(parent, page, configuration);
90 		context = (ISynchronizationContext)configuration.getProperty(ITeamContentProviderManager.P_SYNCHRONIZATION_CONTEXT);
91 		context.getDiffTree().addDiffChangeListener(this);
92 		getConfiguration().addPropertyChangeListener(this);
93 		jobChangeListener = new JobChangeAdapter() {
94 			@Override
95 			public void running(IJobChangeEvent event) {
96 				if (isJobOfInterest(event.getJob())) {
97 					if (context.getDiffTree().isEmpty())
98 						calculateDescription();
99 				}
100 			}
101 			private boolean isJobOfInterest(Job job) {
102 				if (job.belongsTo(getConfiguration().getParticipant()))
103 					return true;
104 				SubscriberDiffTreeEventHandler handler = getHandler();
105 				if (handler != null && handler.getEventHandlerJob() == job)
106 					return true;
107 				return false;
108 			}
109 			@Override
110 			public void done(IJobChangeEvent event) {
111 				if (isJobOfInterest(event.getJob())) {
112 					if (context.getDiffTree().isEmpty())
113 						calculateDescription();
114 				}
115 			}
116 		};
117 		Job.getJobManager().addJobChangeListener(jobChangeListener);
118 	}
119 
120 	@Override
dispose()121 	public void dispose() {
122 		context.getDiffTree().removeDiffChangeListener(this);
123 		getConfiguration().removePropertyChangeListener(this);
124 		Job.getJobManager().removeJobChangeListener(jobChangeListener);
125 		super.dispose();
126 	}
127 
128 	@Override
getChangesCount()129 	protected int getChangesCount() {
130 		return context.getDiffTree().size();
131 	}
132 
133 	@Override
getChangesInMode(int candidateMode)134 	protected long getChangesInMode(int candidateMode) {
135 		long numChanges;
136 		long numConflicts = context.getDiffTree().countFor(IThreeWayDiff.CONFLICTING, IThreeWayDiff.DIRECTION_MASK);
137 		switch (candidateMode) {
138 		case ISynchronizePageConfiguration.CONFLICTING_MODE:
139 			numChanges = numConflicts;
140 			break;
141 		case ISynchronizePageConfiguration.OUTGOING_MODE:
142 			numChanges = numConflicts + context.getDiffTree().countFor(IThreeWayDiff.OUTGOING, IThreeWayDiff.DIRECTION_MASK);
143 			break;
144 		case ISynchronizePageConfiguration.INCOMING_MODE:
145 			numChanges = numConflicts + context.getDiffTree().countFor(IThreeWayDiff.INCOMING, IThreeWayDiff.DIRECTION_MASK);
146 			break;
147 		case ISynchronizePageConfiguration.BOTH_MODE:
148 			numChanges = numConflicts + context.getDiffTree().countFor(IThreeWayDiff.INCOMING, IThreeWayDiff.DIRECTION_MASK)
149 				+ context.getDiffTree().countFor(IThreeWayDiff.OUTGOING, IThreeWayDiff.DIRECTION_MASK);
150 			break;
151 		default:
152 			numChanges = 0;
153 			break;
154 		}
155 		return numChanges;
156 	}
157 
hasChangesInMode(String id, ISynchronizationCompareAdapter adapter, int candidateMode)158 	protected boolean hasChangesInMode(String id, ISynchronizationCompareAdapter adapter, int candidateMode) {
159 		switch (candidateMode) {
160 		case ISynchronizePageConfiguration.CONFLICTING_MODE:
161 			return hasChangesFor(id, adapter, context, new int[] { IThreeWayDiff.CONFLICTING }, IThreeWayDiff.DIRECTION_MASK);
162 		case ISynchronizePageConfiguration.OUTGOING_MODE:
163 			return hasChangesFor(id, adapter, context, new int[] { IThreeWayDiff.CONFLICTING, IThreeWayDiff.OUTGOING }, IThreeWayDiff.DIRECTION_MASK);
164 		case ISynchronizePageConfiguration.INCOMING_MODE:
165 			return hasChangesFor(id, adapter, context, new int[] { IThreeWayDiff.CONFLICTING, IThreeWayDiff.INCOMING }, IThreeWayDiff.DIRECTION_MASK);
166 		case ISynchronizePageConfiguration.BOTH_MODE:
167 			return hasChangesFor(id, adapter, context, new int[] { IThreeWayDiff.CONFLICTING, IThreeWayDiff.INCOMING, IThreeWayDiff.OUTGOING }, IThreeWayDiff.DIRECTION_MASK);
168 		}
169 		return false;
170 	}
171 
hasChangesFor(String id, ISynchronizationCompareAdapter adapter, ISynchronizationContext context, int[] states, int mask)172 	private boolean hasChangesFor(String id, ISynchronizationCompareAdapter adapter, ISynchronizationContext context, int[] states, int mask) {
173 		ITraversalFactory factory = Adapters.adapt(adapter, ITraversalFactory.class);
174 		ResourceTraversal[] traversals;
175 		if (factory == null) {
176 			traversals = context.getScope().getTraversals(id);
177 		} else {
178 			traversals = factory.getTraversals(context.getScope());
179 		}
180 		return (context.getDiffTree().hasMatchingDiffs(traversals, FastDiffFilter.getStateFilter(states, mask)));
181 	}
182 
183 	@Override
getVisibleChangesCount()184 	protected long getVisibleChangesCount() {
185 		ISynchronizePageConfiguration configuration = getConfiguration();
186 		if (configuration.getComparisonType() == ISynchronizePageConfiguration.TWO_WAY) {
187 			return context.getDiffTree().size();
188 		}
189 		int currentMode =  configuration.getMode();
190 		String id = (String)configuration.getProperty(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER);
191 		if (id != null && !id.equals(ModelSynchronizeParticipant.ALL_MODEL_PROVIDERS_VISIBLE)) {
192 			try {
193 				IModelProviderDescriptor desc = ModelProvider.getModelProviderDescriptor(id);
194 				ISynchronizationCompareAdapter adapter = Utils.getCompareAdapter(desc.getModelProvider());
195 				if (adapter != null) {
196 					return hasChangesInMode(desc.getId(), adapter, getConfiguration().getMode()) ? -1 : 0;
197 				}
198 			} catch (CoreException e) {
199 				TeamUIPlugin.log(e);
200 			}
201 			// Use the view state to indicate whether there are visible changes
202 			return isViewerEmpty() ? 0 : -1;
203 		}
204 		return getChangesInMode(currentMode);
205 	}
206 
207 	@Override
getCandidateMode()208 	protected int getCandidateMode() {
209 		SynchronizePageConfiguration configuration = (SynchronizePageConfiguration)getConfiguration();
210 		long outgoingChanges = context.getDiffTree().countFor(IThreeWayDiff.OUTGOING, IThreeWayDiff.DIRECTION_MASK);
211 		if (outgoingChanges > 0) {
212 			if (configuration.isModeSupported(ISynchronizePageConfiguration.OUTGOING_MODE)) {
213 				return ISynchronizePageConfiguration.OUTGOING_MODE;
214 			}
215 			if (configuration.isModeSupported(ISynchronizePageConfiguration.BOTH_MODE)) {
216 				return ISynchronizePageConfiguration.BOTH_MODE;
217 			}
218 		}
219 		long incomingChanges = context.getDiffTree().countFor(IThreeWayDiff.INCOMING, IThreeWayDiff.DIRECTION_MASK);
220 		if (incomingChanges > 0) {
221 			if (configuration.isModeSupported(ISynchronizePageConfiguration.INCOMING_MODE)) {
222 				return ISynchronizePageConfiguration.INCOMING_MODE;
223 			}
224 			if (configuration.isModeSupported(ISynchronizePageConfiguration.BOTH_MODE)) {
225 				return ISynchronizePageConfiguration.BOTH_MODE;
226 			}
227 		}
228 		return configuration.getMode();
229 	}
230 
231 	@Override
diffsChanged(IDiffChangeEvent event, IProgressMonitor monitor)232 	public void diffsChanged(IDiffChangeEvent event, IProgressMonitor monitor) {
233 		IStatus[] errors = event.getErrors();
234 		if (errors.length > 0) {
235 			this.errors = errors;
236 		}
237 		calculateDescription();
238 	}
239 
240 	@Override
propertyChanged(IDiffTree tree, int property, IPath[] paths)241 	public void propertyChanged(IDiffTree tree, int property, IPath[] paths) {
242 		// Do nothing
243 	}
244 
245 	@Override
propertyChange(PropertyChangeEvent event)246 	public void propertyChange(PropertyChangeEvent event) {
247 		if (event.getProperty().equals(ISynchronizePageConfiguration.P_MODE)
248 				|| event.getProperty().equals(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER)) {
249 			calculateDescription();
250 		}
251 	}
252 
253 	@Override
getEmptyChangesComposite(Composite parent)254 	protected Composite getEmptyChangesComposite(Composite parent) {
255 		if (context.getDiffTree().isEmpty()) {
256 			SubscriberDiffTreeEventHandler handler = getHandler();
257 			if (handler != null && handler.getState() == SubscriberDiffTreeEventHandler.STATE_STARTED) {
258 				// The context has not been initialized yet
259 				return getInitializationPane(parent);
260 			}
261 			if (isRefreshRunning() || (handler != null && handler.getEventHandlerJob().getState() != Job.NONE)) {
262 				return getInitializingMessagePane(parent);
263 			}
264 		} else {
265 			ISynchronizePageConfiguration configuration = getConfiguration();
266 			String id = (String)configuration.getProperty(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER);
267 			if (id == null)
268 				id = ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER;
269 			if (id.equals(ModelSynchronizeParticipant.ALL_MODEL_PROVIDERS_VISIBLE)) {
270 				if (getChangesInMode(getConfiguration().getMode()) > 0 && isAtLeastOneProviderDisabled()) {
271 					// There are changes in this mode but they are not visible so enable
272 					// all providers
273 					return createEnableParticipantModelProvidersPane(parent);
274 				}
275 			} else {
276 				// A particular model is active so we need to look for a model that has changes in this
277 				// same mode before offering to change modes.
278 				ModelProvider[] providers =findModelsWithChangesInMode(getConfiguration().getMode());
279 				ModelProvider currentProvider = null;
280 				for (ModelProvider provider : providers) {
281 					if (isEnabled(provider)) {
282 						if (provider.getDescriptor().getId().equals(id)) {
283 							currentProvider = provider;
284 						} else {
285 							return getPointerToModel(parent, provider, id);
286 						}
287 					}
288 				}
289 				if (currentProvider != null || providers.length > 0) {
290 					// The current provider has changes but the view is empty
291 					// or there is a disabled provider with changes
292 					// This is an error so offer to enable and show all models
293 					return createEnableParticipantModelProvidersPane(parent);
294 				}
295 			}
296 		}
297 		return super.getEmptyChangesComposite(parent);
298 	}
299 
isAtLeastOneProviderDisabled()300 	private boolean isAtLeastOneProviderDisabled() {
301 		ModelProvider[] providers =findModelsWithChangesInMode(getConfiguration().getMode());
302 		for (ModelProvider provider : providers) {
303 			if (!isEnabled(provider)) {
304 				return true;
305 			}
306 		}
307 		return false;
308 	}
309 
findModelsWithChangesInMode(int mode)310 	private ModelProvider[] findModelsWithChangesInMode(int mode) {
311 		ModelProvider[] providers =context.getScope().getModelProviders();
312 		providers = ModelOperation.sortByExtension(providers);
313 		List<ModelProvider> result = new ArrayList<>();
314 		for (ModelProvider provider : providers) {
315 			ISynchronizationCompareAdapter adapter = Utils.getCompareAdapter(provider);
316 			if (adapter != null) {
317 				boolean hasChanges = hasChangesInMode(provider.getId(), adapter, getConfiguration().getMode());
318 				if (hasChanges) {
319 					result.add(provider);
320 				}
321 			}
322 		}
323 		return result.toArray(new ModelProvider[result.size()]);
324 	}
325 
isEnabled(ModelProvider provider)326 	private boolean isEnabled(ModelProvider provider) {
327 		ITeamContentProviderDescriptor desc = TeamUI.getTeamContentProviderManager().getDescriptor(provider.getId());
328 		return (desc != null && desc.isEnabled());
329 	}
330 
createEnableParticipantModelProvidersPane(Composite parent)331 	private Composite createEnableParticipantModelProvidersPane(Composite parent) {
332 		Composite composite = new Composite(parent, SWT.NONE);
333 		composite.setBackground(getListBackgroundColor());
334 		GridLayout layout = new GridLayout();
335 		layout.numColumns = 2;
336 		composite.setLayout(layout);
337 		GridData data = new GridData(GridData.FILL_BOTH);
338 		data.grabExcessVerticalSpace = true;
339 		composite.setLayoutData(data);
340 
341 		String message;
342 		int changesCount = getChangesCount();
343 		if (changesCount == 1) {
344 			message = TeamUIMessages.DiffTreeChangesSection_8;
345 		} else {
346 			message = NLS.bind(TeamUIMessages.DiffTreeChangesSection_9, Integer.valueOf(changesCount));
347 		}
348 		final ITeamContentProviderDescriptor[] descriptors = getEnabledContentDescriptors();
349 		if (descriptors.length == 0)
350 			message = NLS.bind(TeamUIMessages.DiffTreeChangesSection_10, message);
351 		else
352 			message = NLS.bind(TeamUIMessages.DiffTreeChangesSection_11, message);
353 
354 		createDescriptionLabel(composite, message);
355 
356 		Label warning = new Label(composite, SWT.NONE);
357 		warning.setImage(TeamUIPlugin.getPlugin().getImage(ISharedImages.IMG_WARNING_OVR));
358 
359 		Hyperlink link = getForms().createHyperlink(composite, TeamUIMessages.DiffTreeChangesSection_12, SWT.WRAP);
360 		link.addHyperlinkListener(new HyperlinkAdapter() {
361 			@Override
362 			public void linkActivated(HyperlinkEvent e) {
363 				ModelSynchronizeParticipant participant = (ModelSynchronizeParticipant)getConfiguration().getParticipant();
364 				ModelProvider[] providers = participant.getEnabledModelProviders();
365 				Set<ITeamContentProviderDescriptor> toEnable = new HashSet<>();
366 				toEnable.addAll(Arrays.asList(descriptors));
367 				for (ModelProvider provider : providers) {
368 					ITeamContentProviderDescriptor desc = TeamUI.getTeamContentProviderManager().getDescriptor(provider.getId());
369 					if (desc != null && !desc.isEnabled()) {
370 						toEnable.add(desc);
371 					}
372 				}
373 				TeamUI.getTeamContentProviderManager()
374 						.setEnabledDescriptors(toEnable.toArray(new ITeamContentProviderDescriptor[toEnable.size()]));
375 				getConfiguration().setProperty(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER, ModelSynchronizeParticipant.ALL_MODEL_PROVIDERS_VISIBLE );
376 
377 			}
378 		});
379 		getForms().getHyperlinkGroup().add(link);
380 
381 		return composite;
382 	}
383 
getEnabledContentDescriptors()384 	private ITeamContentProviderDescriptor[] getEnabledContentDescriptors() {
385 		ModelSynchronizeParticipant participant = (ModelSynchronizeParticipant)getConfiguration().getParticipant();
386 		ModelProvider[] providers = participant.getEnabledModelProviders();
387 		Set<ITeamContentProviderDescriptor> result = new HashSet<>();
388 		for (ModelProvider provider : providers) {
389 			ITeamContentProviderDescriptor desc = TeamUI.getTeamContentProviderManager().getDescriptor(provider.getId());
390 			if (desc != null && desc.isEnabled())
391 				result.add(desc);
392 		}
393 		return result.toArray(new ITeamContentProviderDescriptor[result.size()]);
394 	}
395 
getInitializationPane(Composite parent)396 	private Composite getInitializationPane(Composite parent) {
397 		Composite composite = new Composite(parent, SWT.NONE);
398 		composite.setBackground(getListBackgroundColor());
399 		GridLayout layout = new GridLayout();
400 		layout.numColumns = 2;
401 		composite.setLayout(layout);
402 		GridData data = new GridData(GridData.FILL_BOTH);
403 		data.grabExcessVerticalSpace = true;
404 		composite.setLayoutData(data);
405 
406 		createDescriptionLabel(composite, NLS.bind(TeamUIMessages.DiffTreeChangesSection_3, new String[] { Utils.shortenText(SynchronizeView.MAX_NAME_LENGTH, getConfiguration().getParticipant().getName()) }));
407 
408 		final boolean[] remember = new boolean[] { false };
409 		final PreferenceStore store = (PreferenceStore) getConfiguration()
410 			.getProperty(StartupPreferencePage.STARTUP_PREFERENCES);
411 		Hyperlink link = getForms().createHyperlink(composite, TeamUIMessages.DiffTreeChangesSection_4, SWT.WRAP);
412 		link.addHyperlinkListener(new HyperlinkAdapter() {
413 			@Override
414 			public void linkActivated(HyperlinkEvent e) {
415 				if (remember[0] && store != null) {
416 					store.putValue(StartupPreferencePage.PROP_STARTUP_ACTION, StartupPreferencePage.STARTUP_ACTION_POPULATE);
417 				}
418 				getHandler().initializeIfNeeded();
419 			}
420 		});
421 		getForms().getHyperlinkGroup().add(link);
422 
423 		link = getForms().createHyperlink(composite, TeamUIMessages.DiffTreeChangesSection_5, SWT.WRAP);
424 		link.addHyperlinkListener(new HyperlinkAdapter() {
425 			@Override
426 			public void linkActivated(HyperlinkEvent e) {
427 				if (remember[0] && store != null) {
428 					store.putValue(StartupPreferencePage.PROP_STARTUP_ACTION, StartupPreferencePage.STARTUP_ACTION_SYNCHRONIZE);
429 				}
430 				getConfiguration().getParticipant().run(getConfiguration().getSite().getPart());
431 			}
432 		});
433 		getForms().getHyperlinkGroup().add(link);
434 
435 		if (store != null) {
436 			final Button rememberButton = getForms().createButton(composite, TeamUIMessages.DiffTreeChangesSection_14, SWT.CHECK);
437 			rememberButton.setToolTipText(TeamUIMessages.DiffTreeChangesSection_14);
438 			data = new GridData(GridData.FILL_HORIZONTAL);
439 			data.horizontalSpan = 2;
440 			data.horizontalIndent=5;
441 			data.verticalIndent=5;
442 			data.widthHint = 100;
443 			rememberButton.setLayoutData(data);
444 			rememberButton.addSelectionListener(new SelectionListener() {
445 				@Override
446 				public void widgetSelected(SelectionEvent e) {
447 					remember[0] = rememberButton.getSelection();
448 				}
449 				@Override
450 				public void widgetDefaultSelected(SelectionEvent e) {
451 					// Do nothing
452 				}
453 			});
454 		}
455 
456 		return composite;
457 	}
458 
getInitializingMessagePane(Composite parent)459 	private Composite getInitializingMessagePane(Composite parent) {
460 		Composite composite = new Composite(parent, SWT.NONE);
461 		composite.setBackground(getListBackgroundColor());
462 		GridLayout layout = new GridLayout();
463 		layout.numColumns = 2;
464 		composite.setLayout(layout);
465 		GridData data = new GridData(GridData.FILL_BOTH);
466 		data.grabExcessVerticalSpace = true;
467 		composite.setLayoutData(data);
468 		if (isRefreshRunning()) {
469 			createDescriptionLabel(composite,NLS.bind(TeamUIMessages.DiffTreeChangesSection_6, new String[] { Utils.shortenText(SynchronizeView.MAX_NAME_LENGTH, getConfiguration().getParticipant().getName()) }));
470 		} else {
471 			createDescriptionLabel(composite,NLS.bind(TeamUIMessages.DiffTreeChangesSection_7, new String[] { Utils.shortenText(SynchronizeView.MAX_NAME_LENGTH, getConfiguration().getParticipant().getName()) }));
472 		}
473 		return composite;
474 	}
475 
isRefreshRunning()476 	private boolean isRefreshRunning() {
477 		return Job.getJobManager().find(getConfiguration().getParticipant()).length > 0;
478 	}
479 
getHandler()480 	private SubscriberDiffTreeEventHandler getHandler() {
481 		return Adapters.adapt(context, SubscriberDiffTreeEventHandler.class);
482 	}
483 
getPointerToModel(Composite parent, final ModelProvider provider, String oldId)484 	private Composite getPointerToModel(Composite parent, final ModelProvider provider, String oldId) {
485 		Composite composite = new Composite(parent, SWT.NONE);
486 		composite.setBackground(getListBackgroundColor());
487 		GridLayout layout = new GridLayout();
488 		layout.numColumns = 2;
489 		composite.setLayout(layout);
490 		GridData data = new GridData(GridData.FILL_BOTH);
491 		data.grabExcessVerticalSpace = true;
492 		composite.setLayoutData(data);
493 
494 		IModelProviderDescriptor oldDesc = ModelProvider.getModelProviderDescriptor(oldId);
495 		String message;
496 		String modeToString = Utils.modeToString(getConfiguration().getMode());
497 		message = NLS.bind(TeamUIMessages.DiffTreeChangesSection_0, new String[] {
498 					provider.getDescriptor().getLabel(),
499 					modeToString });
500 		message = NLS.bind(TeamUIMessages.DiffTreeChangesSection_1, new String[] { modeToString, oldDesc.getLabel(), message });
501 
502 		createDescriptionLabel(composite, message);
503 
504 		Label warning = new Label(composite, SWT.NONE);
505 		warning.setImage(TeamUIPlugin.getPlugin().getImage(ISharedImages.IMG_WARNING_OVR));
506 
507 		Hyperlink link = getForms().createHyperlink(composite, NLS.bind(TeamUIMessages.DiffTreeChangesSection_2, new String[] { provider.getDescriptor().getLabel() }), SWT.WRAP);
508 		link.addHyperlinkListener(new HyperlinkAdapter() {
509 			@Override
510 			public void linkActivated(HyperlinkEvent e) {
511 				getConfiguration().setProperty(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER, provider.getDescriptor().getId() );
512 			}
513 		});
514 		getForms().getHyperlinkGroup().add(link);
515 
516 		new Label(composite, SWT.NONE);
517 		Hyperlink link2 = getForms().createHyperlink(composite, TeamUIMessages.DiffTreeChangesSection_13, SWT.WRAP);
518 		link2.addHyperlinkListener(new HyperlinkAdapter() {
519 			@Override
520 			public void linkActivated(HyperlinkEvent e) {
521 				getConfiguration().setProperty(ModelSynchronizeParticipant.P_VISIBLE_MODEL_PROVIDER, ModelSynchronizeParticipant.ALL_MODEL_PROVIDERS_VISIBLE );
522 			}
523 		});
524 		getForms().getHyperlinkGroup().add(link2);
525 
526 		return composite;
527 	}
528 
529 	@Override
treeEmpty(TreeViewer viewer)530 	public void treeEmpty(TreeViewer viewer) {
531 		handleEmptyViewer();
532 	}
533 
handleEmptyViewer()534 	private void handleEmptyViewer() {
535 		// Override stand behavior to do our best to show something
536 		TeamUIPlugin.getStandardDisplay().asyncExec(() -> {
537 			if (!getContainer().isDisposed())
538 				updatePage(getEmptyChangesComposite(getContainer()));
539 		});
540 	}
541 
542 	@Override
calculateDescription()543 	protected void calculateDescription() {
544 		if (errors != null && errors.length > 0) {
545 			if (!showingError) {
546 				TeamUIPlugin.getStandardDisplay().asyncExec(() -> {
547 					updatePage(getErrorComposite(getContainer()));
548 					showingError = true;
549 				});
550 			}
551 			return;
552 		}
553 		showingError = false;
554 		if (isViewerEmpty()) {
555 			handleEmptyViewer();
556 		} else {
557 			super.calculateDescription();
558 		}
559 	}
560 
isViewerEmpty()561 	private boolean isViewerEmpty() {
562 		Viewer v = getPage().getViewer();
563 		if (v instanceof CommonViewerAdvisor.NavigableCommonViewer) {
564 			CommonViewerAdvisor.NavigableCommonViewer cv = (CommonViewerAdvisor.NavigableCommonViewer) v;
565 			return cv.isEmpty();
566 		}
567 		return false;
568 	}
569 
570 	@Override
notEmpty(TreeViewer viewer)571 	public void notEmpty(TreeViewer viewer) {
572 		calculateDescription();
573 	}
574 
getErrorComposite(Composite parent)575 	private Composite getErrorComposite(Composite parent) {
576 		Composite composite = new Composite(parent, SWT.NONE);
577 		composite.setBackground(getListBackgroundColor());
578 		GridLayout layout = new GridLayout();
579 		layout.numColumns = 2;
580 		composite.setLayout(layout);
581 		GridData data = new GridData(GridData.FILL_BOTH);
582 		data.grabExcessVerticalSpace = true;
583 		composite.setLayoutData(data);
584 
585 		createDescriptionLabel(composite, NLS.bind(TeamUIMessages.ChangesSection_10, new String[] { Utils.shortenText(SynchronizeView.MAX_NAME_LENGTH, getConfiguration().getParticipant().getName()) }));
586 
587 		Hyperlink link = getForms().createHyperlink(composite, TeamUIMessages.ChangesSection_8, SWT.WRAP);
588 		link.addHyperlinkListener(new HyperlinkAdapter() {
589 			@Override
590 			public void linkActivated(HyperlinkEvent e) {
591 				showErrors();
592 			}
593 		});
594 		getForms().getHyperlinkGroup().add(link);
595 
596 		link = getForms().createHyperlink(composite, TeamUIMessages.ChangesSection_9, SWT.WRAP);
597 		link.addHyperlinkListener(new HyperlinkAdapter() {
598 			@Override
599 			public void linkActivated(HyperlinkEvent e) {
600 				errors = null;
601 				calculateDescription();
602 				SubscriberDiffTreeEventHandler handler = getHandler();
603 				if (handler != null)
604 					handler.initializeIfNeeded();
605 				else
606 					getConfiguration().getParticipant().run(getConfiguration().getSite().getPart());
607 			}
608 		});
609 		getForms().getHyperlinkGroup().add(link);
610 
611 		return composite;
612 	}
613 
showErrors()614 	/* private */ void showErrors() {
615 		if (errors != null) {
616 			IStatus[] status = errors;
617 			String title = TeamUIMessages.ChangesSection_11;
618 			if (status.length == 1) {
619 				ErrorDialog.openError(getShell(), title, status[0].getMessage(), status[0]);
620 			} else {
621 				MultiStatus multi = new MultiStatus(TeamUIPlugin.ID, 0, status, TeamUIMessages.ChangesSection_12, null);
622 				ErrorDialog.openError(getShell(), title, null, multi);
623 			}
624 		}
625 	}
626 }
627