Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 09dc33841ce8c49ee637a3754d2d9c0c380dc65c (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
/*******************************************************************************
 * Copyright (c) 2000, 2004 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.ccvs.ui.operations;

import java.io.*;
import java.text.DateFormat;
import java.util.*;

import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.revisions.Revision;
import org.eclipse.jface.text.revisions.RevisionInformation;
import org.eclipse.jface.text.source.LineRange;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.client.*;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.client.listeners.AnnotateListener;
import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.core.util.KnownRepositories;
import org.eclipse.team.internal.ccvs.ui.*;
import org.eclipse.team.internal.ccvs.ui.Policy;
import org.eclipse.team.internal.core.TeamPlugin;
import org.eclipse.team.internal.ui.Utils;
import org.eclipse.team.internal.ui.history.GenericHistoryView;
import org.eclipse.team.ui.history.IHistoryPage;
import org.eclipse.ui.*;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;

/**
 * An operation to fetch the annotations for a file from the repository and
 * display them in the annotations view.
 */
public class ShowAnnotationOperation extends CVSOperation {
    
    final private ICVSResource fCVSResource;
    final private String fRevision;
    private final boolean binary;

    public ShowAnnotationOperation(IWorkbenchPart part, ICVSResource cvsResource, String revision, boolean binary) {
        super(part);
        fCVSResource= cvsResource;
        fRevision= revision;
        this.binary = binary;

    }

    /* (non-Javadoc)
     * @see org.eclipse.team.internal.ccvs.ui.operations.CVSOperation#execute(org.eclipse.core.runtime.IProgressMonitor)
     */
    protected void execute(IProgressMonitor monitor) throws CVSException, InterruptedException {

		monitor.beginTask(null, 120);

		// Get the annotations from the repository.
		final AnnotateListener listener= new AnnotateListener();
		fetchAnnotation(listener, fCVSResource, fRevision, Policy.subMonitorFor(monitor, 80));

		// this is not needed if there is a live editor - but we don't know, and the user might have closed the live editor since...
		fetchContents(listener, Policy.subMonitorFor(monitor, 20));

		// this is not needed if there is no live annotate
		final RevisionInformation information= createRevisionInformation(listener, Policy.subMonitorFor(monitor, 20));

		// Open the view and display it from the UI thread.
		final Display display= getPart().getSite().getShell().getDisplay();
		display.asyncExec(new Runnable() {
			public void run() {
				boolean useQuickDiffAnnotate = false;
				//If the file being annotated is not binary, check to see if we can use the quick
				//annotate. Until we are able to show quick diff annotate on read only text editors
				//we can't make use of it for binary files/remote files.
				if (!binary)
					useQuickDiffAnnotate = promptForQuickDiffAnnotate();
				if (useQuickDiffAnnotate){
					//is there an open editor for the given input? If yes, use live annotate
					AbstractDecoratedTextEditor editor= getEditor();
					if (editor != null){
						editor.showRevisionInformation(information, "org.eclipse.quickdiff.providers.CVSReferenceProvider"); //$NON-NLS-1$
						try {
							GenericHistoryView historyView = (GenericHistoryView) getPart().getSite().getPage().showView(GenericHistoryView.VIEW_ID);
							historyView.showHistoryFor(fCVSResource.getIResource());
							IHistoryPage historyPage = historyView.getHistoryPage();
							if (historyPage instanceof CVSHistoryPage){
								CVSHistoryPage cvsHistoryPage = (CVSHistoryPage) historyPage;
								cvsHistoryPage.setMode(CVSHistoryPage.REMOTE_MODE);
							}
						} catch (PartInitException e) {
							CVSException.wrapException(e);
						}
					}
				} else
					showView(listener);	
			}
		});
		monitor.done();
	}

    /* (non-Javadoc)
     * @see org.eclipse.team.internal.ccvs.ui.operations.CVSOperation#getTaskName()
     */
    protected String getTaskName() {
        return CVSUIMessages.ShowAnnotationOperation_taskName; 
    }

	protected boolean hasCharset(ICVSResource cvsResource, InputStream contents) {
		try {
			return TeamPlugin.getCharset(cvsResource.getName(), contents) != null;
		} catch (IOException e) {
			// Assume that the contents do have a charset
			return true;
		}
	}

	/**
	 * Shows the view once the background operation is finished. This must be called
	 * from the UI thread.
	 * 
	 * @param listener The listener with the results.
	 */
    protected void showView(final AnnotateListener listener) {
        final IWorkbench workbench= PlatformUI.getWorkbench();
        final IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
        
		final String defaultPerspectiveID= promptForPerspectiveSwitch();

		if (defaultPerspectiveID != null) {
			try {
				workbench.showPerspective(defaultPerspectiveID, window);
			} catch (WorkbenchException e) {
				Utils.handleError(window.getShell(), e, CVSUIMessages.ShowAnnotationOperation_0, e.getMessage()); 
			}
		}
       
        try {
            final AnnotateView view = AnnotateView.openInActivePerspective();
            view.showAnnotations(fCVSResource, listener.getCvsAnnotateBlocks(), listener.getContents());
        } catch (PartInitException e) {
            CVSUIPlugin.log(e);
        } catch (CVSException e) {
            CVSUIPlugin.log(e);
        }
    }

	private AbstractDecoratedTextEditor getEditor() {
        final IWorkbench workbench= PlatformUI.getWorkbench();
        final IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
        IEditorReference[] references= window.getActivePage().getEditorReferences();
        IResource resource= fCVSResource.getIResource();
		if (resource == null)
			return null;

		for (int i= 0; i < references.length; i++) {
			IEditorReference reference= references[i];
			try {
				if (resource != null && resource.equals(reference.getEditorInput().getAdapter(IFile.class))) {
					IEditorPart editor= reference.getEditor(false);
					if (editor instanceof AbstractDecoratedTextEditor)
						return (AbstractDecoratedTextEditor) editor;
					else {
						//editor opened is not a text editor - reopen file using the defualt text editor
						IEditorPart part = getPart().getSite().getPage().openEditor(new FileEditorInput((IFile) resource), IDEWorkbenchPlugin.DEFAULT_TEXT_EDITOR_ID, true, IWorkbenchPage.MATCH_NONE);
						if (part != null && part instanceof AbstractDecoratedTextEditor)
							return (AbstractDecoratedTextEditor)part;
					}
				}
			} catch (PartInitException e) {
				// ignore
			}
		}
		
		//no existing editor references found, try to open a new editor for the file	
		if (resource instanceof IFile){
			try {
				IEditorDescriptor descrptr = IDE.getEditorDescriptor((IFile) resource);
				//try to open the associated editor only if its an internal editor
				if (descrptr.isInternal()){
					IEditorPart part = IDE.openEditor(getPart().getSite().getPage(), (IFile) resource);
					if (part instanceof AbstractDecoratedTextEditor)
						return (AbstractDecoratedTextEditor)part;
					
					//editor opened is not a text editor - close it
					getPart().getSite().getPage().closeEditor(part, false);
				}
				//open file in default text editor	
				IEditorPart part = IDE.openEditor(getPart().getSite().getPage(), (IFile) resource, IDEWorkbenchPlugin.DEFAULT_TEXT_EDITOR_ID);
				if (part != null && part instanceof AbstractDecoratedTextEditor)
					return (AbstractDecoratedTextEditor)part;
				
			} catch (PartInitException e) {
			}
		}
	
        return null;
	}
	
    private void fetchAnnotation(AnnotateListener listener, ICVSResource cvsResource, String revision, IProgressMonitor monitor) throws CVSException {
    
        monitor = Policy.monitorFor(monitor);
        monitor.beginTask(null, 100);
        
        final ICVSFolder folder = cvsResource.getParent();
        final FolderSyncInfo info = folder.getFolderSyncInfo();
        final ICVSRepositoryLocation location = KnownRepositories.getInstance().getRepository(info.getRoot());
        
        final Session session = new Session(location, folder, true /*output to console*/);
        session.open(Policy.subMonitorFor(monitor, 10), false /* read-only */);
        try {
            final Command.QuietOption quietness = CVSProviderPlugin.getPlugin().getQuietness();
            try {
                CVSProviderPlugin.getPlugin().setQuietness(Command.VERBOSE);
                List localOptions = new ArrayList();
                if (revision != null) {
                    localOptions.add(Annotate.makeRevisionOption(revision));
                }
                if (binary) {
                    localOptions.add(Annotate.FORCE_BINARY_ANNOTATE);
                }
                final IStatus status = Command.ANNOTATE.execute(session, Command.NO_GLOBAL_OPTIONS, (LocalOption[]) localOptions.toArray(new LocalOption[localOptions.size()]), new ICVSResource[]{cvsResource}, listener, Policy.subMonitorFor(monitor, 90));
                if (status.getCode() == CVSStatus.SERVER_ERROR) {
                    throw new CVSServerException(status);
                }
            } finally {
                CVSProviderPlugin.getPlugin().setQuietness(quietness);
                monitor.done();
            }
        } finally {
            session.close();
        }
    }

    private RevisionInformation createRevisionInformation(final AnnotateListener listener, IProgressMonitor monitor) {
	    Map logEntriesByRevision= new HashMap();
		if (fCVSResource instanceof ICVSFile) {
			try {
				ILogEntry[] logEntries= ((ICVSFile) fCVSResource).getLogEntries(Policy.subMonitorFor(monitor, 20));
				for (int i= 0; i < logEntries.length; i++) {
					ILogEntry entry= logEntries[i];
					logEntriesByRevision.put(entry.getRevision(), entry);
				}
			} catch (TeamException e) {
				CVSUIPlugin.log(e);
			}
		}

		final CommitterColors colors= CommitterColors.getDefault();
		RevisionInformation info= new RevisionInformation();
		HashMap sets= new HashMap();
		List annotateBlocks= listener.getCvsAnnotateBlocks();
		for (Iterator blocks= annotateBlocks.iterator(); blocks.hasNext();) {
			final CVSAnnotateBlock block= (CVSAnnotateBlock) blocks.next();
			final String revisionString= block.getRevision();
			Revision revision= (Revision) sets.get(revisionString);
			if (revision == null) {
				final ILogEntry entry= (ILogEntry) logEntriesByRevision.get(revisionString);
				
				revision= new Revision() {
					public Object getHoverInfo() {
						if (entry != null)
							return "<b>" + entry.getAuthor() + " " + entry.getRevision() + " " + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(entry.getDate()) + "</b><p>" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
							entry.getComment() + "</p>"; //$NON-NLS-1$
						return block.toString().substring(0, block.toString().indexOf(" (")); //$NON-NLS-1$
					}
					
					private String getCommitterId() {
						return block.toString().substring(0, block.toString().indexOf(' '));
					}
					
					public String getId() {
						return revisionString;
					}
					
					public Date getDate() {
						return entry.getDate();
					}
					
					public RGB getColor() {
						return colors.getCommitterRGB(getCommitterId());
					}
				};
				sets.put(revisionString, revision);
				info.addRevision(revision);
			}
			revision.addRange(new LineRange(block.getStartLine(), block.getEndLine() - block.getStartLine() + 1));
		}
		return info;
	}
    
	private void fetchContents(final AnnotateListener listener, IProgressMonitor monitor) {
		try {
			if (hasCharset(fCVSResource, listener.getContents())) {
				listener.setContents(getRemoteContents(fCVSResource, monitor));
			}
		} catch (CoreException e) {
			// Log and continue, using the original fetched contents
			CVSUIPlugin.log(e);
		}
	}

	private InputStream getRemoteContents(ICVSResource resource, IProgressMonitor monitor) throws CoreException {
        
    	final ICVSRemoteResource remote = CVSWorkspaceRoot.getRemoteResourceFor(resource);
    	if (remote == null) {
    		return new ByteArrayInputStream(new byte[0]);
    	}
    	final IStorage storage = ((IResourceVariant)remote).getStorage(monitor);
    	if (storage == null) {
    		return new ByteArrayInputStream(new byte[0]);
    	}
    	return storage.getContents();
    }
    
    /**
     * @return The ID of the perspective if the perspective needs to be changed,
     * null otherwise.
     */
	private String promptForPerspectiveSwitch() {
		// check whether we should ask the user.
		final IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
		final String option = store.getString(ICVSUIConstants.PREF_CHANGE_PERSPECTIVE_ON_SHOW_ANNOTATIONS);
		final String desiredID = store.getString(ICVSUIConstants.PREF_DEFAULT_PERSPECTIVE_FOR_SHOW_ANNOTATIONS);
		
		if (option.equals(MessageDialogWithToggle.ALWAYS))
			return desiredID; // no, always switch
		
		if (option.equals(MessageDialogWithToggle.NEVER))
			return null; // no, never switch
		
		// Check whether the desired perspective is already active.
		final IPerspectiveRegistry registry= PlatformUI.getWorkbench().getPerspectiveRegistry();
		final IPerspectiveDescriptor desired = registry.findPerspectiveWithId(desiredID);
		final IWorkbenchPage page = CVSUIPlugin.getActivePage();
		
		if (page != null) {
			final IPerspectiveDescriptor current = page.getPerspective();
			if (current != null && current.getId().equals(desiredID)) {
				return null; // it is active, so no prompt and no switch
			}
		}
		
		if (desired != null) {
		    
			String message;;
			String desc = desired.getDescription();
			if (desc == null) {
				message = NLS.bind(CVSUIMessages.ShowAnnotationOperation_2, new String[] { desired.getLabel() });
			} else {
				message = NLS.bind(CVSUIMessages.ShowAnnotationOperation_3, new String[] { desired.getLabel(), desc });
			}
		    // Ask the user whether to switch
			final MessageDialogWithToggle m = MessageDialogWithToggle.openYesNoQuestion(
			        Utils.getShell(null),
			        CVSUIMessages.ShowAnnotationOperation_1, 
			        message, 
			        CVSUIMessages.ShowAnnotationOperation_4,   
			        false /* toggle state */,
			        store,
			        ICVSUIConstants.PREF_CHANGE_PERSPECTIVE_ON_SHOW_ANNOTATIONS);
			
			final int result = m.getReturnCode();
			switch (result) {
			// yes
			case IDialogConstants.YES_ID:
			case IDialogConstants.OK_ID :
			    return desiredID;
			// no
			case IDialogConstants.NO_ID :
			    return null;
			}
		}
		return null;
	}
	
	/**
	 * Returns true if the user wishes to always use the live annotate view, false otherwise.
	 * @return
	 */
	private boolean promptForQuickDiffAnnotate(){
		//check whether we should ask the user.
		final IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
		final String option = store.getString(ICVSUIConstants.PREF_USE_QUICKDIFFANNOTATE);
		
		if (option.equals(MessageDialogWithToggle.ALWAYS))
			return true; //use live annotate
		
		final MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(Utils.getShell(null), CVSUIMessages.ShowAnnotationOperation_QDAnnotateTitle,
				CVSUIMessages.ShowAnnotationOperation_QDAnnotateMessage,CVSUIMessages.ShowAnnotationOperation_4, false, store, ICVSUIConstants.PREF_USE_QUICKDIFFANNOTATE);
		
		final int result = dialog.getReturnCode();
		switch (result) {
			//yes
			case IDialogConstants.YES_ID:
			case IDialogConstants.OK_ID :
			    return true;
		}
		return false;
	}
}

Back to the top