/******************************************************************************* * Copyright (c) 2006, 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 * Eugene Kuleshov - Bug 153932 [History] Custom hyperlink detectors for comments in History view * Brock Janiczak (brockj@tpg.com.au) - Bug 181899 CVS History wrongly ordered *******************************************************************************/ package org.eclipse.team.internal.ccvs.ui; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.util.*; import org.eclipse.compare.*; import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.compare.structuremergeviewer.ICompareInput; import org.eclipse.core.resources.*; import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory; import org.eclipse.core.resources.mapping.ResourceChangeValidator; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.*; import org.eclipse.jface.dialogs.*; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.*; import org.eclipse.jface.text.revisions.Revision; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.util.OpenStrategy; import org.eclipse.jface.viewers.*; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.*; import org.eclipse.team.core.RepositoryProvider; import org.eclipse.team.core.TeamException; import org.eclipse.team.core.history.*; import org.eclipse.team.core.variants.IResourceVariant; import org.eclipse.team.internal.ccvs.core.*; import org.eclipse.team.internal.ccvs.core.client.Command; import org.eclipse.team.internal.ccvs.core.client.Update; import org.eclipse.team.internal.ccvs.core.filehistory.*; import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot; import org.eclipse.team.internal.ccvs.core.resources.RemoteFile; import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo; import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo; import org.eclipse.team.internal.ccvs.ui.actions.CVSAction; import org.eclipse.team.internal.ccvs.ui.actions.MoveRemoteTagAction; import org.eclipse.team.internal.ccvs.ui.operations.*; import org.eclipse.team.internal.core.history.LocalFileRevision; import org.eclipse.team.internal.ui.TeamUIMessages; import org.eclipse.team.internal.ui.Utils; import org.eclipse.team.internal.ui.actions.*; import org.eclipse.team.internal.ui.history.*; import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement; import org.eclipse.team.ui.history.*; import org.eclipse.team.ui.synchronize.SaveableCompareEditorInput; import org.eclipse.ui.*; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.part.IPageSite; import org.eclipse.ui.progress.IProgressConstants; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import com.ibm.icu.text.DateFormat; import com.ibm.icu.text.SimpleDateFormat; import com.ibm.icu.util.Calendar; public class CVSHistoryPage extends HistoryPage implements IAdaptable, IHistoryCompareAdapter { /* private */ ICVSFile file; /* private */ ICVSFile previousFile; /* private */ IFileRevision currentFileRevision; private ITypedElement fileElement; // cached for efficiency /* private */ CVSFileHistory cvsFileHistory; /* private */IFileRevision[] entries; /* private */CVSHistoryTableProvider historyTableProvider; private Composite tagAndTextComposite; private Composite searchComposite; /* private */TreeViewer treeViewer; /* private */TextViewer textViewer; /* private */TableViewer tagViewer; /* private */CompareRevisionAction compareAction; /* private */OpenRevisionAction openAction; private OpenWithMenu openWithMenu; private CVSHistoryFilterAction cvsHistoryFilter; private IAction toggleTextAction; private IAction toggleTextWrapAction; private IAction toggleListAction; private IAction toggleFilterAction; private IAction toggleSearchAction; private TextViewerAction copyAction; private TextViewerAction selectAllAction; private Action getContentsAction; private Action getRevisionAction; private Action refreshAction; /** * Allows copying the tag name to the clipboard */ private TableViewerAction copyTagAction; private Action tagWithExistingAction; private Action localMode; private Action remoteMode; private Action remoteLocalMode; private Action groupByDateMode; private Action collapseAll; private Action compareModeAction; private SashForm sashForm; private SashForm innerSashForm; private SashForm searchSashForm; private Image branchImage; private Image versionImage; private IDialogSettings settings; protected IFileRevision currentSelection; /* private */RefreshCVSFileHistory refreshCVSFileHistoryJob; /* private */boolean shutdown = false; /* private */boolean localFilteredOut = false; /* private */boolean remoteFilteredOut = false; private HistoryResourceListener resourceListener; //toggle constants for default click action private boolean compareMode = false; //filter constants public final static int REMOTE_LOCAL_MODE = 0; public final static int REMOTE_MODE = 1; public final static int LOCAL_MODE = 2; // page settings keys private final static String SASH_WEIGHTS = "SASH_WEIGHTS"; //$NON-NLS-1$ private final static String INNER_SASH_WEIGHTS = "INNER_SASH_WEIGHTS"; //$NON-NLS-1$ private final static String SASH_WEIGHTS_SEPARATOR = ";"; //$NON-NLS-1$ private final static String SORT_ORDER_KEY = "SORT_ORDER"; //$NON-NLS-1$ // page settings section name private static final String CVS_HISTORY_PAGE_SECTION = CVSHistoryPage.class.getName(); //current filter mode private int currentFilerMode = 0; //text field used for search private Text searchField; //current tag list sort order. private boolean sortTagsAscending; // listener registered on the book this page is contained private DisposeListener disposeListener; //grouping on private boolean groupingOn; private CVSHistoryFilter historyFilter; private CVSHistorySearchFilter searchFilter; private RevisionAnnotationController rulerSelectionListener; private int refreshRequest = 0; private DateFormat dateTimeFormat; private String description; public CVSHistoryPage(Object object) { this.file = getCVSFile(object); IDialogSettings viewsSettings = CVSUIPlugin.getPlugin() .getDialogSettings(); settings = viewsSettings.getSection(CVS_HISTORY_PAGE_SECTION); if (settings == null) { settings = viewsSettings.addNewSection(CVS_HISTORY_PAGE_SECTION); } sortTagsAscending= settings.get(SORT_ORDER_KEY) == null || settings.getBoolean(SORT_ORDER_KEY); } /** * Action to sort the tag list ascending or descending. */ final class SortTagsAction extends Action { private boolean sortAscending; public SortTagsAction(boolean sortAscending) { super( sortAscending ? CVSUIMessages.CVSHistoryPage_SortTagsAscendingAction : CVSUIMessages.CVSHistoryPage_SortTagsDescendingAction, IAction.AS_RADIO_BUTTON); setChecked(sortAscending == sortTagsAscending); this.sortAscending = sortAscending; } @Override public void run() { sortTagsAscending = sortAscending; tagViewer.refresh(); } } @Override public void createControl(Composite parent) { initializeImages(); sashForm = new SashForm(parent, SWT.VERTICAL); sashForm.setLayoutData(new GridData(GridData.FILL_BOTH)); treeViewer = createTree(sashForm); tagAndTextComposite = new Composite(sashForm, SWT.NONE); tagAndTextComposite.setLayout(new FillLayout()); innerSashForm = new SashForm(tagAndTextComposite, SWT.HORIZONTAL); tagViewer = createTagTable(innerSashForm); textViewer = createText(innerSashForm); searchComposite = new Composite(sashForm, SWT.NONE); searchComposite.setLayout(new FillLayout()); searchSashForm = new SashForm(searchComposite, SWT.HORIZONTAL); //Find field searchField = new Text(searchSashForm, SWT.SEARCH); searchField.setMessage(CVSUIMessages.CVSHistoryPage_EnterSearchTerm); final SearchHistoryTable searchHistoryTable = new SearchHistoryTable(); searchField.addModifyListener(e -> Display.getDefault().timerExec(1000, searchHistoryTable)); contributeActions(); setViewerVisibility(); int[] weights = loadSashWeights(SASH_WEIGHTS); sashForm.setWeights(weights.length == 3 ? weights : new int[] { 65, 20, 15 }); int[] innerWeights = loadSashWeights(INNER_SASH_WEIGHTS); innerSashForm.setWeights(innerWeights.length == 2 ? innerWeights : new int[] { 50, 50 }); IHistoryPageSite parentSite = getHistoryPageSite(); if (parentSite != null && parentSite instanceof DialogHistoryPageSite && treeViewer != null) parentSite.setSelectionProvider(treeViewer); resourceListener = new HistoryResourceListener(); ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceListener, IResourceChangeEvent.POST_CHANGE); PlatformUI.getWorkbench().getHelpSystem().setHelp(sashForm, IHelpContextIds.RESOURCE_HISTORY_VIEW); disposeListener = e -> saveState(); parent.addDisposeListener(disposeListener); } private void saveState() { saveSashWeights(SASH_WEIGHTS, sashForm.getWeights()); saveSashWeights(INNER_SASH_WEIGHTS, innerSashForm.getWeights()); historyTableProvider.saveColumnLayout(); settings.put(SORT_ORDER_KEY, sortTagsAscending); } private int[] loadSashWeights(String key) { String value = settings.get(key); if (value == null) { return new int[0]; } String weigths[] = value.split(SASH_WEIGHTS_SEPARATOR); int result[] = new int[weigths.length]; for (int i = 0; i < weigths.length; i++) { try { result[i] = Integer.parseInt(weigths[i]); } catch (NumberFormatException e) { return new int[0]; } } return result; } private void saveSashWeights(String key, int[] weights) { StringBuffer value = new StringBuffer(); for (int i = 0; i < weights.length; i++) { value = value.append(weights[i] + SASH_WEIGHTS_SEPARATOR); } settings.put(key, value.toString()); } private TextViewer createText(SashForm parent) { SourceViewer result = new SourceViewer(parent, null, null, true, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.READ_ONLY); result.getTextWidget().setIndent(2); result.configure(new TextSourceViewerConfiguration(EditorsUI.getPreferenceStore()) { @Override protected Map getHyperlinkDetectorTargets(ISourceViewer sourceViewer) { return Collections.singletonMap("org.eclipse.ui.DefaultTextEditor", //$NON-NLS-1$ new IAdaptable() { @Override public T getAdapter(Class adapter) { if(adapter==IFile.class && getInput() instanceof IFile) { return adapter.cast(getInput()); } else if(adapter==IFileHistory.class && getInput() instanceof IFileHistory) { return adapter.cast(getInput()); } return Platform.getAdapterManager().getAdapter(CVSHistoryPage.this, adapter); } }); } }); result.addSelectionChangedListener(event -> copyAction.update()); result.setTextDoubleClickStrategy( new DefaultTextDoubleClickStrategy(), IDocument.DEFAULT_CONTENT_TYPE); result.activatePlugins(); return result; } private TableViewer createTagTable(SashForm parent) { Table table = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI); TableViewer result = new TableViewer(table); TableLayout layout = new TableLayout(); layout.addColumnData(new ColumnWeightData(100)); table.setLayout(layout); result.setContentProvider(new SimpleContentProvider() { @Override public Object[] getElements(Object inputElement) { if (inputElement == null) return new Object[0]; ITag[] tags = (ITag[]) inputElement; return tags; } }); result.setLabelProvider(new LabelProvider() { @Override public Image getImage(Object element) { if (element == null) return null; ITag tag = (ITag) element; if (!(tag instanceof CVSTag)) return null; switch (((CVSTag)tag).getType()) { case CVSTag.BRANCH: case CVSTag.HEAD: return branchImage; case CVSTag.VERSION: return versionImage; } return null; } @Override public String getText(Object element) { return ((ITag) element).getName(); } }); result.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { if (!(e1 instanceof ITag) || !(e2 instanceof ITag)) return super.compare(viewer, e1, e2); CVSTag tag1 = (CVSTag) e1; CVSTag tag2 = (CVSTag) e2; int type1 = tag1.getType(); int type2 = tag2.getType(); if (type1 != type2) { return type2 - type1; } if (sortTagsAscending) return super.compare(viewer, tag1, tag2); else return super.compare(viewer, tag2, tag1); } }); result.addSelectionChangedListener(event -> { copyTagAction.setEnabled(false); if (event.getSelection() instanceof StructuredSelection) { if (((StructuredSelection) event.getSelection()).getFirstElement() != null) { copyTagAction.setEnabled(true); } } }); return result; } @Override public void setFocus() { sashForm.setFocus(); if (refreshRequest != 0) { refresh(refreshRequest); refreshRequest = 0; } } protected void contributeActions() { CVSUIPlugin plugin = CVSUIPlugin.getPlugin(); //Refresh refreshAction = new Action(CVSUIMessages.HistoryView_refreshLabel, plugin.getImageDescriptor(ICVSUIConstants.IMG_REFRESH_ENABLED)) { @Override public void run() { refresh(); } }; refreshAction.setToolTipText(CVSUIMessages.HistoryView_refresh); refreshAction.setDisabledImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_REFRESH_DISABLED)); refreshAction.setHoverImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_REFRESH)); //Local Mode final IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore(); localMode = new Action(CVSUIMessages.CVSHistoryPage_LocalModeAction, plugin.getImageDescriptor(ICVSUIConstants.IMG_LOCALMODE)) { @Override public void run() { if (isChecked()){ store.setValue(ICVSUIConstants.PREF_REVISION_MODE, LOCAL_MODE); updateFilterMode(LOCAL_MODE); } else setChecked(true); } }; localMode.setToolTipText(CVSUIMessages.CVSHistoryPage_LocalModeTooltip); localMode.setDisabledImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_LOCALMODE_DISABLED)); localMode.setHoverImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_LOCALMODE)); //Remote Mode remoteMode = new Action(CVSUIMessages.CVSHistoryPage_RemoteModeAction, plugin.getImageDescriptor(ICVSUIConstants.IMG_REMOTEMODE)) { @Override public void run() { if (isChecked()){ store.setValue(ICVSUIConstants.PREF_REVISION_MODE, REMOTE_MODE); updateFilterMode(REMOTE_MODE); } else setChecked(true); } }; remoteMode.setToolTipText(CVSUIMessages.CVSHistoryPage_RemoteModeTooltip); remoteMode.setDisabledImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_REMOTEMODE_DISABLED)); remoteMode.setHoverImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_REMOTEMODE)); //Remote + Local Mode remoteLocalMode = new Action(CVSUIMessages.CVSHistoryPage_CombinedModeAction, plugin.getImageDescriptor(ICVSUIConstants.IMG_LOCALREMOTE_MODE)) { @Override public void run() { if (isChecked()){ store.setValue(ICVSUIConstants.PREF_REVISION_MODE, REMOTE_LOCAL_MODE); updateFilterMode(REMOTE_LOCAL_MODE); } else setChecked(true); } }; remoteLocalMode.setToolTipText(CVSUIMessages.CVSHistoryPage_CombinedModeTooltip); remoteLocalMode.setDisabledImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_LOCALREMOTE_MODE_DISABLED)); remoteLocalMode.setHoverImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_LOCALREMOTE_MODE)); //set the inital filter to both remote and local updateFilterMode(store.getInt(ICVSUIConstants.PREF_REVISION_MODE)); //Group by Date groupByDateMode = new Action(CVSUIMessages.CVSHistoryPage_GroupByDate, CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_DATES_CATEGORY)){ @Override public void run() { groupingOn = !groupingOn; store.setValue(ICVSUIConstants.PREF_GROUPBYDATE_MODE, groupingOn); refreshHistory(false, false, CVSFileHistory.REFRESH_LOCAL | CVSFileHistory.REFRESH_REMOTE); } }; groupingOn = store.getBoolean(ICVSUIConstants.PREF_GROUPBYDATE_MODE); groupByDateMode.setChecked(groupingOn); groupByDateMode.setToolTipText(CVSUIMessages.CVSHistoryPage_GroupByDate); groupByDateMode.setDisabledImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_DATES_CATEGORY)); groupByDateMode.setHoverImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_DATES_CATEGORY)); //Collapse All collapseAll = new Action(CVSUIMessages.CVSHistoryPage_CollapseAllAction, plugin.getImageDescriptor(ICVSUIConstants.IMG_COLLAPSE_ALL)) { @Override public void run() { treeViewer.collapseAll(); } }; collapseAll.setToolTipText(CVSUIMessages.CVSHistoryPage_CollapseAllTooltip); collapseAll.setDisabledImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_COLLAPSE_ALL)); collapseAll.setHoverImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_COLLAPSE_ALL)); //Compare Mode Action compareModeAction = new Action(CVSUIMessages.CVSHistoryPage_CompareModeToggleAction,plugin.getImageDescriptor(ICVSUIConstants.IMG_COMPARE_VIEW)) { @Override public void run() { compareMode = !compareMode; compareModeAction.setChecked(compareMode); } }; compareModeAction.setToolTipText(CVSUIMessages.CVSHistoryPage_CompareModeTooltip); compareModeAction.setDisabledImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_COMPARE_VIEW)); compareModeAction.setHoverImageDescriptor(plugin.getImageDescriptor(ICVSUIConstants.IMG_COMPARE_VIEW)); compareModeAction.setChecked(false); // Click Compare action compareAction = new CompareRevisionAction(CVSUIMessages.CVSHistoryPage_CompareRevisionAction, this); compareAction.setEnabled(!treeViewer.getSelection().isEmpty()); treeViewer.getTree().addSelectionListener(new SelectionAdapter(){ @Override public void widgetSelected(SelectionEvent e) { //update the current compareAction.setCurrentFileRevision(getCurrentFileRevision()); compareAction.selectionChanged(treeViewer.getStructuredSelection()); } }); openAction = new OpenRevisionAction(CVSUIMessages.CVSHistoryPage_OpenAction, this); openAction.setEnabled(!treeViewer.getSelection().isEmpty()); treeViewer.getTree().addSelectionListener(new SelectionAdapter(){ @Override public void widgetSelected(SelectionEvent e) { openAction.selectionChanged(treeViewer.getStructuredSelection()); } }); // Add 'Open With...' sub-menu openWithMenu = new OpenWithMenu(this); treeViewer.getTree().addSelectionListener(new SelectionAdapter(){ @Override public void widgetSelected(SelectionEvent e) { openWithMenu.selectionChanged(treeViewer.getStructuredSelection()); } }); new OpenAndLinkWithEditorHelper(treeViewer) { @Override protected void open(ISelection selection, boolean activate) { if (getSite() != null && selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection= (IStructuredSelection)selection; if (compareMode){ StructuredSelection sel = new StructuredSelection(new Object[] {getCurrentFileRevision(), structuredSelection.getFirstElement()}); compareAction.selectionChanged(sel); compareAction.run(); } else { //Pass in the entire structured selection to allow for multiple editor openings openAction.selectionChanged(structuredSelection); openAction.run(); } } } @Override protected void activate(ISelection selection) { int currentMode= OpenStrategy.getOpenMethod(); try { OpenStrategy.setOpenMethod(OpenStrategy.DOUBLE_CLICK); open(selection, true); } finally { OpenStrategy.setOpenMethod(currentMode); } } @Override protected void linkToEditor(ISelection selection) { // XXX: Not yet implemented, see http://bugs.eclipse.org/324185 } }; getContentsAction = getContextMenuAction(CVSUIMessages.HistoryView_getContentsAction, true /* needs progress */, monitor -> { monitor.beginTask(null, 100); try { if(confirmOverwrite() && validateChange()) { IStorage currentStorage = currentSelection.getStorage(SubMonitor.convert(monitor, 50)); InputStream in = currentStorage.getContents(); ((IFile)file.getIResource()).setContents(in, false, true, SubMonitor.convert(monitor, 50)); } } catch (TeamException e) { throw new CoreException(e.getStatus()); } finally { monitor.done(); } }); PlatformUI.getWorkbench().getHelpSystem().setHelp(getContentsAction, IHelpContextIds.GET_FILE_CONTENTS_ACTION); getRevisionAction = getContextMenuAction(CVSUIMessages.HistoryView_getRevisionAction, true /* needs progress */, monitor -> { ICVSRemoteFile remoteFile = (ICVSRemoteFile) CVSWorkspaceRoot.getRemoteResourceFor(((CVSFileRevision) currentSelection).getCVSRemoteFile()); try { if(confirmOverwrite() && validateChange()) { CVSTag revisionTag = new CVSTag(remoteFile.getRevision(), CVSTag.VERSION); if(CVSAction.checkForMixingTags(getHistoryPageSite().getShell(), new IResource[] {file.getIResource()}, revisionTag)) { new UpdateOperation( null, new IResource[] {file.getIResource()}, new Command.LocalOption[] {Update.IGNORE_LOCAL_CHANGES}, revisionTag) .run(monitor); Display.getDefault().asyncExec(() -> refresh()); } } } catch (InvocationTargetException e1) { throw CVSException.wrapException(e1); } catch (InterruptedException e2) { // Cancelled by user } }); PlatformUI.getWorkbench().getHelpSystem().setHelp(getRevisionAction, IHelpContextIds.GET_FILE_REVISION_ACTION); // Override MoveRemoteTagAction to work for log entries final IActionDelegate tagActionDelegate = new MoveRemoteTagAction() { @Override protected ICVSResource[] getSelectedCVSResources() { ICVSResource[] resources = super.getSelectedCVSResources(); if (resources == null || resources.length == 0) { ArrayList logEntrieFiles = null; IStructuredSelection selection = getSelection(); if (!selection.isEmpty()) { logEntrieFiles = new ArrayList(); Iterator elements = selection.iterator(); while (elements.hasNext()) { Object next = elements.next(); if (next instanceof CVSFileRevision) { logEntrieFiles.add(((CVSFileRevision)next).getCVSRemoteFile()); continue; } if (next instanceof IAdaptable) { IAdaptable a = (IAdaptable) next; Object adapter = a.getAdapter(ICVSResource.class); if (adapter instanceof ICVSResource) { logEntrieFiles.add(((ILogEntry)adapter).getRemoteFile()); continue; } } } } if (logEntrieFiles != null && !logEntrieFiles.isEmpty()) { return (ICVSResource[])logEntrieFiles.toArray(new ICVSResource[logEntrieFiles.size()]); } } return resources; } /* * Override the creation of the tag operation in order to support * the refresh of the view after the tag operation completes */ @Override protected ITagOperation createTagOperation() { return new TagInRepositoryOperation(getTargetPart(), getSelectedRemoteResources()) { @Override public void execute(IProgressMonitor monitor) throws CVSException, InterruptedException { super.execute(monitor); Display.getDefault().asyncExec(() -> { if( ! wasCancelled()) { refresh(); } }); }; }; } }; tagWithExistingAction = getContextMenuAction(CVSUIMessages.HistoryView_tagWithExistingAction, false /* no progress */, monitor -> { tagActionDelegate.selectionChanged(tagWithExistingAction, treeViewer.getSelection()); tagActionDelegate.run(tagWithExistingAction); }); PlatformUI.getWorkbench().getHelpSystem().setHelp(getRevisionAction, IHelpContextIds.TAG_WITH_EXISTING_ACTION); // Toggle text visible action toggleTextAction = new Action(TeamUIMessages.GenericHistoryView_ShowCommentViewer) { @Override public void run() { setViewerVisibility(); store.setValue(ICVSUIConstants.PREF_SHOW_COMMENTS, toggleTextAction.isChecked()); } }; toggleTextAction.setChecked(store.getBoolean(ICVSUIConstants.PREF_SHOW_COMMENTS)); //PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleTextAction, IHelpContextIds.SHOW_COMMENT_IN_HISTORY_ACTION); // Toggle wrap comments action toggleTextWrapAction = new Action(TeamUIMessages.GenericHistoryView_WrapComments) { @Override public void run() { setViewerVisibility(); store.setValue(ICVSUIConstants.PREF_WRAP_COMMENTS, toggleTextWrapAction.isChecked()); } }; toggleTextWrapAction.setChecked(store.getBoolean(ICVSUIConstants.PREF_WRAP_COMMENTS)); //PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleTextWrapAction, IHelpContextIds.SHOW_TAGS_IN_HISTORY_ACTION); // Toggle list visible action toggleListAction = new Action(TeamUIMessages.GenericHistoryView_ShowTagViewer) { @Override public void run() { setViewerVisibility(); store.setValue(ICVSUIConstants.PREF_SHOW_TAGS, toggleListAction.isChecked()); } }; toggleListAction.setChecked(store.getBoolean(ICVSUIConstants.PREF_SHOW_TAGS)); //PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleListAction, IHelpContextIds.SHOW_TAGS_IN_HISTORY_ACTION); //Toggle search field toggleSearchAction= new Action(CVSUIMessages.CVSHistoryPage_ShowSearchField) { @Override public void run() { setViewerVisibility(); store.setValue(ICVSUIConstants.PREF_SHOW_SEARCH, toggleSearchAction.isChecked()); if (!toggleSearchAction.isChecked()){ if (searchFilter != null) treeViewer.removeFilter(searchFilter); } else { searchField.setMessage(CVSUIMessages.CVSHistoryPage_EnterSearchTerm); searchField.selectAll(); searchField.setFocus(); } } }; toggleSearchAction.setChecked(store.getBoolean(ICVSUIConstants.PREF_SHOW_SEARCH)); //PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleListAction, IHelpContextIds.SHOW_TAGS_IN_HISTORY_ACTION); toggleFilterAction = new Action(CVSUIMessages.CVSHistoryPage_NoFilter){ @Override public void run(){ if (historyFilter != null) treeViewer.removeFilter(historyFilter); historyFilter = null; String old = CVSHistoryPage.this.description; CVSHistoryPage.this.description = null; CVSHistoryPage.this.firePropertyChange(CVSHistoryPage.this, P_NAME, old, getName()); toggleFilterAction.setEnabled(false); } }; toggleFilterAction.setEnabled(historyFilter != null); //Create the filter action cvsHistoryFilter = new CVSHistoryFilterAction(this); cvsHistoryFilter.setText(CVSUIMessages.CVSHistoryPage_FilterOn); cvsHistoryFilter.init(treeViewer); cvsHistoryFilter.setToolTipText(CVSUIMessages.CVSHistoryPage_FilterHistoryTooltip); cvsHistoryFilter.setImageDescriptor(CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_FILTER_HISTORY)); //Contribute actions to popup menu MenuManager menuMgr = new MenuManager(); Menu menu = menuMgr.createContextMenu(treeViewer.getTree()); menuMgr.addMenuListener(menuMgr1 -> fillTableMenu(menuMgr1)); menuMgr.setRemoveAllWhenShown(true); treeViewer.getTree().setMenu(menu); //Don't add the object contribution menu items if this page is hosted in a dialog IHistoryPageSite parentSite = getHistoryPageSite(); if (!parentSite.isModal()) { IWorkbenchPart part = parentSite.getPart(); if (part != null) { IWorkbenchPartSite workbenchPartSite = part.getSite(); workbenchPartSite.registerContextMenu(menuMgr, treeViewer); } IPageSite pageSite = parentSite.getWorkbenchPageSite(); if (pageSite != null) { IActionBars actionBars = pageSite.getActionBars(); // Contribute toggle text visible to the toolbar drop-down IMenuManager actionBarsMenu = actionBars.getMenuManager(); if (actionBarsMenu != null){ actionBarsMenu.add(toggleTextWrapAction); actionBarsMenu.add(new Separator()); actionBarsMenu.add(toggleTextAction); actionBarsMenu.add(toggleListAction); actionBarsMenu.add(new Separator()); actionBarsMenu.add(toggleSearchAction); actionBarsMenu.add(new Separator()); actionBarsMenu.add(cvsHistoryFilter); actionBarsMenu.add(toggleFilterAction); } // Create actions for the text editor copyAction = new TextViewerAction(textViewer, ITextOperationTarget.COPY); copyAction.setText(CVSUIMessages.HistoryView_copy); actionBars.setGlobalActionHandler(ITextEditorActionConstants.COPY, copyAction); selectAllAction = new TextViewerAction(textViewer, ITextOperationTarget.SELECT_ALL); selectAllAction.setText(CVSUIMessages.HistoryView_selectAll); actionBars.setGlobalActionHandler(ITextEditorActionConstants.SELECT_ALL, selectAllAction); copyTagAction = new TableViewerAction(tagViewer); copyTagAction.setText(CVSUIMessages.HistoryView_copy); copyTagAction.setEnabled(false); actionBars.updateActionBars(); } } //Create the local tool bar IToolBarManager tbm = parentSite.getToolBarManager(); if (tbm != null) { String fileNameQualifier = getFileNameQualifier(); //Add groups tbm.add(new Separator(fileNameQualifier + "grouping")); //$NON-NLS-1$ tbm.appendToGroup(fileNameQualifier+"grouping", groupByDateMode); //$NON-NLS-1$ tbm.add(new Separator(fileNameQualifier+"modes")); //$NON-NLS-1$ tbm.appendToGroup(fileNameQualifier+"modes", remoteLocalMode); //$NON-NLS-1$ tbm.appendToGroup(fileNameQualifier+"modes", localMode); //$NON-NLS-1$ tbm.appendToGroup(fileNameQualifier+"modes", remoteMode); //$NON-NLS-1$ tbm.add(new Separator(fileNameQualifier+"collapse")); //$NON-NLS-1$ tbm.appendToGroup(fileNameQualifier+"collapse", collapseAll); //$NON-NLS-1$ if (!parentSite.isModal()) { //don't bother adding the compare mode toolbar button if in //a dialog; you can only compare from dialogs tbm.appendToGroup(fileNameQualifier+"collapse", compareModeAction); //$NON-NLS-1$ } tbm.update(false); } menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(menuMgr1 -> fillTextMenu(menuMgr1)); StyledText text = textViewer.getTextWidget(); menu = menuMgr.createContextMenu(text); text.setMenu(menu); menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(menuMgr1 -> fillTagMenu(menuMgr1)); menu = menuMgr.createContextMenu(tagViewer.getControl()); tagViewer.getControl().setMenu(menu); } private String getFileNameQualifier() { //Just append the current system time to generate a unique group name return Long.toString(System.currentTimeMillis()); } private boolean isLocalHistoryFilteredOut() { return localFilteredOut; } private boolean isRemoteHistoryFilteredOut(){ return remoteFilteredOut; } /* private */ void fillTableMenu(IMenuManager manager) { // file actions go first (view file) IHistoryPageSite parentSite = getHistoryPageSite(); manager.add(new Separator(IWorkbenchActionConstants.GROUP_FILE)); if (file != null && !parentSite.isModal()){ manager.add(openAction); MenuManager openWithSubmenu = new MenuManager( CVSUIMessages.CVSHistoryPage_OpenWithMenu); openWithSubmenu.add(openWithMenu); manager.add(openWithSubmenu); manager.add(compareAction); manager.add(new Separator("openCompare")); //$NON-NLS-1$ } if (file != null && !(file instanceof RemoteFile)) { // Add the "Add to Workspace" action if 1 revision is selected. ISelection sel = treeViewer.getSelection(); if (!sel.isEmpty()) { if (sel instanceof IStructuredSelection) { IStructuredSelection tempSelection = (IStructuredSelection) sel; if (tempSelection.size() == 1) { manager.add(getContentsAction); if (!(tempSelection.getFirstElement() instanceof LocalFileRevision)) { manager.add(getRevisionAction); manager.add(new Separator()); if (!parentSite.isModal()) manager.add(tagWithExistingAction); } } } } } if (!parentSite.isModal()){ manager.add(new Separator("additions")); //$NON-NLS-1$ manager.add(refreshAction); manager.add(new Separator("additions-end")); //$NON-NLS-1$ } } private void fillTextMenu(IMenuManager manager) { manager.add(copyAction); manager.add(selectAllAction); } private void fillTagMenu(IMenuManager manager) { manager.add(copyTagAction); manager.add(new Separator()); manager.add(new SortTagsAction(true)); manager.add(new SortTagsAction(false)); } /** * Creates the group that displays lists of the available repositories and * team streams. * * @param the * parent composite to contain the group * @return the group control */ protected TreeViewer createTree(Composite parent) { historyTableProvider = new CVSHistoryTableProvider(); TreeViewer viewer = historyTableProvider.createTree(parent); viewer.setContentProvider(new ITreeContentProvider() { @Override public Object[] getElements(Object inputElement) { // The entries of already been fetch so return them if (entries != null) return entries; if (!(inputElement instanceof IFileHistory) && !(inputElement instanceof AbstractHistoryCategory[])) return new Object[0]; if (inputElement instanceof AbstractHistoryCategory[]){ return (AbstractHistoryCategory[]) inputElement; } final IFileHistory fileHistory = (IFileHistory) inputElement; entries = fileHistory.getFileRevisions(); return entries; } @Override public void dispose() { } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { entries = null; } @Override public Object[] getChildren(Object parentElement) { if (parentElement instanceof AbstractHistoryCategory){ return ((AbstractHistoryCategory) parentElement).getRevisions(); } return null; } @Override public Object getParent(Object element) { return null; } @Override public boolean hasChildren(Object element) { if (element instanceof AbstractHistoryCategory){ IFileRevision[] revs = ((AbstractHistoryCategory) element).getRevisions(); if (revs != null) return revs.length > 0; } return false; } }); viewer.addSelectionChangedListener(event -> { ISelection selection = event.getSelection(); if (selection == null || !(selection instanceof IStructuredSelection)) { textViewer.setDocument(new Document("")); //$NON-NLS-1$ tagViewer.setInput(null); setStatusLineMessage(null); return; } IStructuredSelection ss = (IStructuredSelection)selection; if (ss.size() != 1) { textViewer.setDocument(new Document("")); //$NON-NLS-1$ tagViewer.setInput(null); setStatusLineMessage(null); return; } Object o = ss.getFirstElement(); if (o instanceof AbstractHistoryCategory){ textViewer.setDocument(new Document("")); //$NON-NLS-1$ tagViewer.setInput(null); setStatusLineMessage(null); return; } IFileRevision entry = (IFileRevision)o; textViewer.setDocument(new Document(entry.getComment())); tagViewer.setInput(entry.getTags()); setStatusLineMessage(CVSHistoryTableProvider.getCommentAsSingleLine(entry)); }); return viewer; } private Action getContextMenuAction(String title, final boolean needsProgressDialog, final IWorkspaceRunnable action) { return new Action(title) { @Override public void run() { try { if (file == null) return; ISelection selection = treeViewer.getSelection(); if (!(selection instanceof IStructuredSelection)) return; IStructuredSelection ss = (IStructuredSelection)selection; Object o = ss.getFirstElement(); if (o instanceof AbstractHistoryCategory) return; currentSelection = (IFileRevision)o; if(needsProgressDialog) { PlatformUI.getWorkbench().getProgressService().run(true, true, monitor -> { try { action.run(monitor); } catch (CoreException e) { throw new InvocationTargetException(e); } }); } else { try { action.run(null); } catch (CoreException e) { throw new InvocationTargetException(e); } } } catch (InvocationTargetException e) { IHistoryPageSite parentSite = getHistoryPageSite(); CVSUIPlugin.openError(parentSite.getShell(), null, null, e, CVSUIPlugin.LOG_NONTEAM_EXCEPTIONS); } catch (InterruptedException e) { // Do nothing } } @Override public boolean isEnabled() { ISelection selection = treeViewer.getSelection(); if (!(selection instanceof IStructuredSelection)) return false; IStructuredSelection ss = (IStructuredSelection)selection; if(ss.size() != 1) return false; return true; } }; } private boolean confirmOverwrite() { if (file!=null && file.getIResource().exists()) { try { if(file.isModified(null)) { String title = CVSUIMessages.HistoryView_overwriteTitle; String msg = CVSUIMessages.HistoryView_overwriteMsg; IHistoryPageSite parentSite = getHistoryPageSite(); final MessageDialog dialog = new MessageDialog(parentSite.getShell(), title, null, msg, MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.CANCEL_LABEL }, 0); final int[] result = new int[1]; parentSite.getShell().getDisplay().syncExec(() -> result[0] = dialog.open()); if (result[0] != 0) { // cancel return false; } } } catch(CVSException e) { CVSUIPlugin.log(e); } } return true; } private boolean validateChange(){ if (file!=null && file.getIResource().exists()) { IResourceChangeDescriptionFactory factory = ResourceChangeValidator.getValidator().createDeltaFactory(); factory.change((IFile) file.getIResource()); return IDE.promptToConfirm(getHistoryPageSite().getShell(), CVSUIMessages.CVSHistoryPage_ValidateChangeTitle, NLS.bind(CVSUIMessages.CVSHistoryPage_ValidateChangeMessage, new String[]{file.getName()}), factory.getDelta(), new String[0], true /* syncExec */); } return false; } /* * Refresh the view by refetching the log entries for the remote file */ @Override public void refresh() { refresh(CVSFileHistory.REFRESH_LOCAL | CVSFileHistory.REFRESH_REMOTE); } public void refresh(int refreshFlags) { printDebugInfo("CVSHistoryPage#refresh", (IFile)(previousFile != null ? previousFile.getIResource() : null), cvsFileHistory, new Throwable()); //$NON-NLS-1$ //refetch revisions, not a select only job // TODO refreshHistory(true, false, refreshFlags); } private void refreshHistory(boolean refetch, boolean selectOnly, int refreshFlags) { if (refreshCVSFileHistoryJob.getState() != Job.NONE){ RefreshCVSFileHistory oldJob = refreshCVSFileHistoryJob; oldJob.cancel(); refreshCVSFileHistoryJob = new RefreshCVSFileHistory(this); refreshCVSFileHistoryJob.setLocalFileRevision(oldJob.localFileRevision); refreshCVSFileHistoryJob.setSelectLocal(oldJob.useLocalSelect); refetch = true; selectOnly = false; refreshFlags = CVSFileHistory.REFRESH_ALL; printDebugInfo("CVSHistoryPage#refreshHistory, cancel old job", (IFile)(previousFile != null ? previousFile.getIResource() : null), cvsFileHistory, null); //$NON-NLS-1$ } refreshCVSFileHistoryJob.setFileHistory(cvsFileHistory); IResource resource = previousFile.getIResource(); if (resource != null){ IResource workspaceFile = ResourcesPlugin.getWorkspace().getRoot().findMember(resource.getFullPath()); refreshCVSFileHistoryJob.setWorkspaceFile((IFile) workspaceFile); } //if we need to refetch it's not a select only job and vice versa refreshCVSFileHistoryJob.setSelectOnly(selectOnly); refreshCVSFileHistoryJob.setRefetchHistory(refetch); refreshCVSFileHistoryJob.setIncludeLocals(!isLocalHistoryFilteredOut()); refreshCVSFileHistoryJob.setIncludeRemote(!isRemoteHistoryFilteredOut()); refreshCVSFileHistoryJob.setGrouping(groupingOn); refreshCVSFileHistoryJob.setRefreshFlags(refreshFlags); IHistoryPageSite parentSite = getHistoryPageSite(); printDebugInfo("CVSHistoryPage#refreshHistory, about to schedule RefreshCVSFileHistoryJob", (IFile)resource, cvsFileHistory, null); //$NON-NLS-1$ Utils.schedule(refreshCVSFileHistoryJob, getWorkbenchSite(parentSite)); } private IWorkbenchPartSite getWorkbenchSite(IHistoryPageSite parentSite) { IWorkbenchPart part = parentSite.getPart(); if (part != null) return part.getSite(); return null; } /** * Select the revision in the receiver. */ public void selectRevision(String revision) { IFileRevision entry = null; entry = getFileRevision(revision); if (entry != null) { IStructuredSelection selection = new StructuredSelection(entry); treeViewer.getTree().setRedraw(false); treeViewer.setSelection(selection, true); treeViewer.getTree().setRedraw(true); } else { //nothing to select so clear selection treeViewer.getTree().deselectAll(); } } private IFileRevision getFileRevision(String revision) { if (entries != null) { for (int i = 0; i < entries.length; i++) { if (entries[i].getContentIdentifier().equals(revision)) { return entries[i]; } } } else if (cvsFileHistory != null) { return cvsFileHistory.getFileRevision(revision); } return null; } /** * Select the local revision in the receiver. Local revisions are differentiated by their * timestamps. */ public void selectLocalRevision(long timeStamp){ IFileRevision entry = null; if (entries != null) { for (int i = 0; i < entries.length; i++) { if (entries[i].getTimestamp() == timeStamp) { entry = entries[i]; break; } } }else if (cvsFileHistory != null) { IFileRevision[] tempEntries = cvsFileHistory.getFileRevisions(); for (int i = 0; i < tempEntries.length; i++) { if (tempEntries[i].getTimestamp() == timeStamp) { entry = tempEntries[i]; break; } } } if (entry != null) { IStructuredSelection selection = new StructuredSelection(entry); treeViewer.getTree().setRedraw(false); treeViewer.setSelection(selection, true); treeViewer.getTree().setRedraw(true); } else { //nothing to select so clear selection treeViewer.getTree().deselectAll(); } } protected static ICVSFile getCVSFile(Object object) { // First, adapt to IResource and ensure mapped to CVS IResource resource = Adapters.adapt(object, IResource.class); if (resource instanceof IFile) { RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject()); if (provider instanceof CVSTeamProvider) return CVSWorkspaceRoot.getCVSFileFor((IFile) resource); return null; } // Second, try ICVSFile ICVSFile remoteFile = Adapters.adapt(object, ICVSFile.class); if (remoteFile != null) { return remoteFile; } // Next, try ICVSResource ICVSResource remote = Adapters.adapt(object, ICVSResource.class); if (remote instanceof RemoteFile) { return (ICVSFile)remote; } // Next, try IResourceVariant IResourceVariant variant = Adapters.adapt(object, IResourceVariant.class); if (variant instanceof RemoteFile) { return (ICVSFile)remote; } // Finally, try IFileRevision IFileRevision revision = Adapters.adapt(object, IFileRevision.class); if (revision instanceof CVSFileRevision) { return ((CVSFileRevision)revision).getCVSRemoteFile(); } return null; } /* private */void setViewerVisibility() { boolean showText = toggleTextAction.isChecked(); boolean showList = toggleListAction.isChecked(); boolean showSearch = toggleSearchAction.isChecked(); //check to see if this page is being shown in a dialog, in which case //don't show the text and list panes IHistoryPageSite parentSite = getHistoryPageSite(); if (parentSite.isModal()){ showText = false; showList = false; } if (showText && showList && showSearch) { //tree + tag + text + search tagAndTextComposite.setVisible(true); searchComposite.setVisible(true); sashForm.setWeights(new int[] {60, 25, 15}); sashForm.setMaximizedControl(null); innerSashForm.setMaximizedControl(null); searchSashForm.setMaximizedControl(null); } else if (showText && showSearch) { //tree + text + search tagAndTextComposite.setVisible(true); searchComposite.setVisible(true); sashForm.setWeights(new int[] {60, 25, 15}); sashForm.setMaximizedControl(null); innerSashForm.setMaximizedControl(textViewer.getTextWidget()); searchSashForm.setMaximizedControl(searchField); } else if (showList && showSearch) { //tree + tag + search tagAndTextComposite.setVisible(true); searchComposite.setVisible(true); sashForm.setWeights(new int[] {60, 25, 15}); sashForm.setMaximizedControl(null); innerSashForm.setMaximizedControl(tagViewer.getTable()); searchSashForm.setMaximizedControl(searchField); } else if (showSearch){ //tree + search tagAndTextComposite.setVisible(false); searchComposite.setVisible(true); sashForm.setWeights(new int[] {85, 0, 15}); sashForm.setMaximizedControl(null); innerSashForm.setMaximizedControl(null); searchSashForm.setMaximizedControl(searchField); } else if (showText && showList) { //tree + tag + text tagAndTextComposite.setVisible(true); searchComposite.setVisible(false); sashForm.setWeights(new int[] {70, 30, 0}); sashForm.setMaximizedControl(null); innerSashForm.setMaximizedControl(null); searchSashForm.setMaximizedControl(searchField); } else if (showText) { //tree + text tagAndTextComposite.setVisible(true); searchComposite.setVisible(false); sashForm.setWeights(new int[] {70, 30, 0}); sashForm.setMaximizedControl(null); innerSashForm.setMaximizedControl(textViewer.getTextWidget()); } else if (showList) { //tree + tag tagAndTextComposite.setVisible(true); searchComposite.setVisible(false); sashForm.setWeights(new int[] {70, 30, 0}); sashForm.setMaximizedControl(null); innerSashForm.setMaximizedControl(tagViewer.getTable()); } else { //tree tagAndTextComposite.setVisible(false); searchComposite.setVisible(false); sashForm.setMaximizedControl(treeViewer.getControl()); } boolean wrapText = toggleTextWrapAction.isChecked(); textViewer.getTextWidget().setWordWrap(wrapText); } private void initializeImages() { CVSUIPlugin plugin = CVSUIPlugin.getPlugin(); versionImage = plugin.getImageDescriptor(ICVSUIConstants.IMG_PROJECT_VERSION).createImage(); branchImage = plugin.getImageDescriptor(ICVSUIConstants.IMG_TAG).createImage(); } @Override public void dispose() { shutdown = true; if (!sashForm.isDisposed() && !innerSashForm.isDisposed()) { saveState(); // called when switching pages if (disposeListener != null){ sashForm.getParent().removeDisposeListener(disposeListener); disposeListener = null; } } if (resourceListener != null){ ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceListener); resourceListener = null; } if (branchImage != null) { branchImage.dispose(); branchImage = null; } if (versionImage != null) { versionImage.dispose(); versionImage = null; } if (rulerSelectionListener != null) { rulerSelectionListener.dispose(); rulerSelectionListener= null; } //Cancel any incoming if (refreshCVSFileHistoryJob != null) { if (refreshCVSFileHistoryJob.getState() != Job.NONE) { refreshCVSFileHistoryJob.cancel(); } } super.dispose(); } public IFileRevision getCurrentFileRevision() { if (currentFileRevision != null) return currentFileRevision; if (file != null) { try { //Case 1 : file is remote if (file instanceof RemoteFile) { RemoteFile remote = (RemoteFile) file; currentFileRevision = cvsFileHistory.getFileRevision(remote.getContentIdentifier()); //remote.getContents(monitor); //currentFileRevision = new CVSFileRevision(remote.getLogEntry(monitor)); return currentFileRevision; } //Case 2 : file is local //if (file.isModified(monitor)) { //file has been modified locally IFile localFile = (IFile) file.getIResource(); if (localFile != null) { //make sure that there's actually a resource associated with the file currentFileRevision = new LocalFileRevision(localFile); } else { //no local version exists if (file.getSyncInfo() != null) { currentFileRevision = cvsFileHistory.getFileRevision(file.getSyncInfo().getRevision()); } } return currentFileRevision; } catch (CVSException e) { } } return null; } private final class CVSRevisionAnnotationController extends RevisionAnnotationController { public CVSRevisionAnnotationController(IWorkbenchPage page, IFile file) { super(page, file, treeViewer); } public CVSRevisionAnnotationController(IWorkbenchPage page, IStorageEditorInput editorInput) { super(page, editorInput, treeViewer); } @Override protected Object getHistoryEntry(Revision selected) { return CVSHistoryPage.this.getFileRevision(selected.getId()); } } private final class SearchHistoryTable implements Runnable { @Override public void run() { String searchString = searchField.getText(); if (searchString.equals("") || //$NON-NLS-1$ searchString.equals(CVSUIMessages.CVSHistoryPage_EnterSearchTerm)) { if (searchFilter != null) treeViewer.removeFilter(searchFilter); return; } if (searchFilter != null) treeViewer.removeFilter(searchFilter); searchFilter = new CVSHistorySearchFilter(searchString); if (historyFilter != null) treeViewer.removeFilter(historyFilter); treeViewer.addFilter(searchFilter); } } private class RefreshCVSFileHistory extends Job { private final static int NUMBER_OF_CATEGORIES = 4; private CVSFileHistory fileHistory; private AbstractHistoryCategory[] categories; private boolean grouping; private Object[] elementsToExpand; private boolean revisionsFound; private IFile workspaceFile; private CVSHistoryPage page; private boolean selectOnly; private boolean useLocalSelect; private CVSLocalFileRevision localFileRevision; private int refreshFlags = CVSFileHistory.REFRESH_LOCAL | CVSFileHistory.REFRESH_REMOTE; public RefreshCVSFileHistory(CVSHistoryPage page) { super(CVSUIMessages.HistoryView_fetchHistoryJob); this.page = page; } public void setWorkspaceFile(IFile workspaceFile) { this.workspaceFile = workspaceFile; } public void setIncludeLocals(boolean flag) { if (fileHistory != null) fileHistory.includeLocalRevisions(flag); } public void setIncludeRemote(boolean flag){ if (fileHistory != null) fileHistory.includeRemoteRevisions(flag); } public void setRefetchHistory(boolean refetch) { if (fileHistory != null) fileHistory.setRefetchRevisions(refetch); } public void setFileHistory(CVSFileHistory fileHistory) { this.fileHistory = fileHistory; } public void setGrouping (boolean value){ this.grouping = value; } public void setSelectOnly(boolean select) { this.selectOnly = select; } public void setSelectLocal(boolean localSelect) { this.useLocalSelect = localSelect; } public void setLocalFileRevision(CVSLocalFileRevision localRev){ this.localFileRevision = localRev; } @Override public IStatus run(IProgressMonitor monitor) { final int cachedRefreshFlags = refreshFlags; final boolean cachedSelectOnly= selectOnly; IStatus status = Status.OK_STATUS; printDebugInfo("RefreshCVSFileHistory#run started for", workspaceFile, fileHistory, null); //$NON-NLS-1$ if (fileHistory != null && !shutdown) { printDebugInfo("RefreshCVSFileHistory#run checkpoint 1", workspaceFile, fileHistory, null); //$NON-NLS-1$ //If fileHistory terminates in a bad way, try to fetch the local //revisions only boolean localFetched = false; boolean needsUpdate = true; if (!fileHistory.isInitialized() && fileHistory.isIncludeLocal() && (cachedRefreshFlags & CVSFileHistory.REFRESH_REMOTE) > 0) { // If this is the first refresh, show the local history before hitting the server try { printDebugInfo("RefreshCVSFileHistory#run checkpoint 2", workspaceFile, fileHistory, null); //$NON-NLS-1$ fileHistory.refresh(CVSFileHistory.REFRESH_LOCAL, monitor); updateTable(cachedSelectOnly); localFetched = true; needsUpdate = false; } catch (TeamException e) { // Ignore and try the full refresh printDebugInfo("RefreshCVSFileHistory#run encountered an exception(1)", workspaceFile, fileHistory, e); //$NON-NLS-1$ } } try { printDebugInfo("RefreshCVSFileHistory#run checkpoint 3", workspaceFile, fileHistory, null); //$NON-NLS-1$ fileHistory.refresh(cachedRefreshFlags, monitor); needsUpdate = true; } catch (OperationCanceledException ex) { printDebugInfo("RefreshCVSFileHistory#run OperationCanceledException", workspaceFile, fileHistory, ex); //$NON-NLS-1$ throw ex; } catch (TeamException ex) { printDebugInfo("RefreshCVSFileHistory#run encountered an exception(2)", workspaceFile, fileHistory, ex); //$NON-NLS-1$ if (!localFetched) { try { fileHistory.refresh(CVSFileHistory.REFRESH_LOCAL, monitor); needsUpdate = true; } catch (TeamException e) { // Ignore and allow the original exception to go through printDebugInfo("RefreshCVSFileHistory#run encountered an exception(3)", workspaceFile, fileHistory, e); //$NON-NLS-1$ } } status = new CVSStatus(ex.getStatus().getSeverity(), ex.getStatus().getCode(), ex.getMessage(), ex); } if (needsUpdate) { printDebugInfo("RefreshCVSFileHistory#run checkpoint 4", workspaceFile, fileHistory, null); //$NON-NLS-1$ updateTable(cachedSelectOnly); } } if (status != Status.OK_STATUS ) { this.setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE); this.setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE); } printDebugInfo("RefreshCVSFileHistory#run finished, status: " + status, workspaceFile, fileHistory, null); //$NON-NLS-1$ return status; } private void updateTable(final boolean selectOnly) { if (grouping) revisionsFound = sortRevisions(); Utils.asyncExec((Runnable) () -> { printDebugInfo("RefreshCVSFileHistory#updateTable, in asyncExec", workspaceFile, cvsFileHistory, null); //$NON-NLS-1$ treeViewer.refresh(); historyTableProvider.setFile(fileHistory, workspaceFile); //historyTableProvider.setWorkspaceFile(workspaceFile); if (!selectOnly){ if (grouping) { mapExpandedElements(treeViewer.getExpandedElements()); treeViewer.getTree().setLinesVisible(revisionsFound); treeViewer.getTree().setRedraw(false); printDebugInfo("RefreshCVSFileHistory#updateTable, setInput:grouping", workspaceFile, cvsFileHistory, null); //$NON-NLS-1$ treeViewer.setInput(categories); //if user is switching modes and already has expanded elements //selected try to expand those, else expand all if (elementsToExpand.length > 0) treeViewer.setExpandedElements(elementsToExpand); else { treeViewer.expandAll(); Object[] el = treeViewer.getExpandedElements(); if (el != null && el.length > 0) { treeViewer.setSelection(new StructuredSelection(el[0])); treeViewer.getTree().deselectAll(); } } treeViewer.getTree().setRedraw(true); } else { if (fileHistory.getFileRevisions().length > 0) { treeViewer.getTree().setLinesVisible(true); printDebugInfo("RefreshCVSFileHistory#updateTable, setInput:no grouping", workspaceFile, cvsFileHistory, null); //$NON-NLS-1$ treeViewer.setInput(fileHistory); } else { categories = new AbstractHistoryCategory[] {getErrorMessage()}; treeViewer.getTree().setLinesVisible(false); treeViewer.setInput(categories); } } } //Update the history (if it exists) to reflect the new //counts if (historyFilter != null){ CVSHistoryFilter tempFilter = new CVSHistoryFilter(historyFilter.branchName, historyFilter.author, historyFilter.comment, historyFilter.fromDate, historyFilter.toDate, historyFilter.isOr); showFilter(tempFilter); } //Select the current file if we didn't have to refetch the history if (file != null){ try { if (useLocalSelect){ page.selectLocalRevision(localFileRevision.getTimestamp()); } else { byte[] syncBytes = file.getSyncBytes(); if (syncBytes != null) { String workspaceRevision = ResourceSyncInfo.getRevision(syncBytes); page.selectRevision(workspaceRevision); } } } catch (CVSException e){ } } }, treeViewer); } private void mapExpandedElements(Object[] expandedElements) { //store the names of the currently expanded categories in a map HashMap elementMap = new HashMap(); for (int i=0; i { if (treeViewer.getControl().isDisposed()) return; if (treeViewer.getControl().isVisible()) { if (hasRevision) refresh(CVSFileHistory.REFRESH_LOCAL); else refresh(); } else { refreshRequest = hasRevision ? CVSFileHistory.REFRESH_LOCAL : CVSFileHistory.REFRESH_LOCAL | CVSFileHistory.REFRESH_REMOTE; } }); } } private String getRevision() { try { byte[] syncBytes = file.getSyncBytes(); if (syncBytes != null && !ResourceSyncInfo.isAddition(syncBytes)) { return ResourceSyncInfo.getRevision(syncBytes); } } catch (CVSException e) { // Ignore the errors } return null; } } @Override public Control getControl() { return sashForm; } @Override public boolean isValidInput(Object object) { return getCVSFile(object) != null; } @Override public String getName() { if (description != null) return description; if (file != null) return file.getName(); return ""; //$NON-NLS-1$ } /** * Returns the path of the file currently being shown in the CVS History Page. * @return an IPath or null if the file is null */ public IPath getFilePath() { if (file != null) return file.getIResource().getFullPath(); return null; } @Override public T getAdapter(Class adapter) { if(adapter == IHistoryCompareAdapter.class) { return adapter.cast(this); } return null; } @Override public ICompareInput getCompareInput(Object object) { if (object instanceof IFileRevision){ IFileRevision selectedFileRevision = (IFileRevision)object; if (fileElement == null) fileElement = SaveableCompareEditorInput.createFileElement((IFile) file.getIResource()); FileRevisionTypedElement right = new FileRevisionTypedElement(selectedFileRevision, getLocalEncoding()); DiffNode node = new DiffNode(fileElement, right); return node; } return null; } private String getLocalEncoding() { IResource resource = file.getIResource(); if (resource instanceof IFile) { IFile file = (IFile) resource; try { return file.getCharset(); } catch (CoreException e) { CVSUIPlugin.log(e); } } return null; } public void setClickAction(boolean compare) { //toggleCompareAction is going to switch the mode //so make sure that we're in the appropriate mode before compareMode = !compare; compareModeAction.run(); } @Override public void prepareInput(ICompareInput input, CompareConfiguration configuration, IProgressMonitor monitor) { initLabels(input, configuration); // TODO: pre-fetch contents } private void initLabels(ICompareInput input, CompareConfiguration cc) { String leftLabel = getFileRevisionLabel(input.getLeft(), cc); cc.setLeftLabel(leftLabel); String rightLabel = getFileRevisionLabel(input.getRight(), cc); cc.setRightLabel(rightLabel); } private String getFileRevisionLabel(ITypedElement element, CompareConfiguration cc) { String label = null; if (element instanceof IEditableContent) { //current revision if (element instanceof IModificationDate) { IModificationDate md = (IModificationDate) element; Date dateFromLong = new Date(md.getModificationDate()); label = NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_workspace, new Object[]{ element.getName(), getDateTimeFormat().format(dateFromLong)}); } else { label = element.getName(); } return label; } else if (element instanceof FileRevisionTypedElement) { Object fileObject = ((FileRevisionTypedElement) element).getFileRevision(); if (fileObject instanceof LocalFileRevision) { try { IStorage storage = ((LocalFileRevision) fileObject).getStorage(new NullProgressMonitor()); if (Adapters.adapt(storage, IFileState.class) != null) { //local revision label = NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_localRevision, new Object[]{element.getName(), ((FileRevisionTypedElement) element).getTimestamp()}); } } catch (CoreException e) { } } else { label = NLS.bind( TeamUIMessages.CompareFileRevisionEditorInput_repository, new Object[]{ element.getName(), ((FileRevisionTypedElement) element).getContentIdentifier(), ((FileRevisionTypedElement) element).getAuthor() }); } } return label; } private synchronized DateFormat getDateTimeFormat() { if (dateTimeFormat == null) dateTimeFormat = DateFormat.getDateTimeInstance(); return dateTimeFormat; } @Override public String getDescription() { try { if (file != null) return file.getRepositoryRelativePath(); } catch (CVSException e) { // Ignore } return null; } @Override public boolean inputSet() { //reset currentFileRevision currentFileRevision = null; Object inputObj = getInput(); ICVSFile cvsFile = getCVSFile(inputObj); if (cvsFile == null) return false; this.file = cvsFile; fileElement = null; // if this input is the same as the last, we don't need to cancel // the current job boolean needRefresh = checkPreviousInput(); printDebugInfo("CVSHistoryPage#inputSet, needRefresh = " + needRefresh, (IFile)cvsFile.getIResource(), cvsFileHistory, null); //$NON-NLS-1$ if (refreshCVSFileHistoryJob != null) { if (!needRefresh && refreshCVSFileHistoryJob.getState() != Job.NONE) { // let the old job finish printDebugInfo("CVSHistoryPage#inputSet, the old job is still running", (IFile)cvsFile.getIResource(), cvsFileHistory, null); //$NON-NLS-1$ return true; } else { // cancel the old job and continue printDebugInfo("CVSHistoryPage#inputSet, cancel the old job", (IFile)cvsFile.getIResource(), cvsFileHistory, null); //$NON-NLS-1$ refreshCVSFileHistoryJob.cancel(); } } refreshCVSFileHistoryJob = new RefreshCVSFileHistory(this); //if the input is a local file revision, pass it to the refresh job to //allow the refresh job to use it to match the time stamp of the local //files displayed in the history page if (inputObj instanceof CVSLocalFileRevision){ refreshCVSFileHistoryJob.setLocalFileRevision((CVSLocalFileRevision) inputObj); } else if (inputObj instanceof IFile) { refreshCVSFileHistoryJob.setLocalFileRevision(new CVSLocalFileRevision((IFile) inputObj)); } //let the refresh job know which flavour of select to use (ie. select CVSFileRevisions //or CVSLocalFileRevision) refreshCVSFileHistoryJob.setSelectLocal(inputObj instanceof CVSLocalFileRevision || inputObj instanceof IFile); //If the file history doesn't need to be refreshed, we can just //use the previous input file history if (needRefresh){ cvsFileHistory = new CVSFileHistory(cvsFile); //fetch both local and remote revisions the first time around cvsFileHistory.includeLocalRevisions(true); //blank current input only after we're sure that we have a file //to fetch history for this.treeViewer.setInput(null); linkWithEditor(); } //always refresh the history if the input gets set - in which //case set the selectOnly to false refreshHistory(needRefresh, !needRefresh, CVSFileHistory.REFRESH_LOCAL | CVSFileHistory.REFRESH_REMOTE); if (toggleSearchAction!= null && toggleSearchAction.isChecked()){ searchField.selectAll(); } return true; } /** * @param page the workbench page that view and editor are contained in * @param editor the editor to link to the history view * @param historyView the history view to link to the editor */ public void linkWithEditor() { if (rulerSelectionListener != null) { rulerSelectionListener.dispose(); rulerSelectionListener= null; } if (!getHistoryPageSite().isModal()) { IResource resource = file.getIResource(); if (resource instanceof IFile) { IFile file = (IFile) resource; rulerSelectionListener= new CVSRevisionAnnotationController(getHistoryPageSite().getWorkbenchPageSite().getPage(), file); } else { Object input = getInput(); if (input instanceof IStorageEditorInput) { IStorageEditorInput editorInput = (IStorageEditorInput) input; rulerSelectionListener= new CVSRevisionAnnotationController(getHistoryPageSite().getWorkbenchPageSite().getPage(), editorInput); } } } } /* * Check to see if we need to refresh the input; if the previous file * that was being shown */ private boolean checkPreviousInput() { if (previousFile != null){ try { if (isSameRemote(file, previousFile) && (isSameLocalFile(file, previousFile) || (!isLocal(file) && isLocal(previousFile)))) { return false; } } catch (CVSException e) { } } //set previous file to current file previousFile = file; return true; } private boolean isLocal(ICVSFile file) { return file.getIResource() != null; } private boolean isSameLocalFile(ICVSFile file, ICVSFile previousFile) { IResource r1 = file.getIResource(); IResource r2 = previousFile.getIResource(); return r1 != null && r2 != null && r1.equals(r2); } private boolean isSameRemote(ICVSFile file, ICVSFile previousFile) throws CVSException { String path = file.getRepositoryRelativePath(); String previousPath = previousFile.getRepositoryRelativePath(); //Could be comparing two local files with no remotes if (path == null && previousPath == null) return true; return (path != null && previousPath != null && path.equals(previousPath) && isSameRepository(file.getParent(), previousFile.getParent())); } private boolean isSameRepository(ICVSFolder parent1, ICVSFolder parent2) { try { FolderSyncInfo info1 = parent1.getFolderSyncInfo(); FolderSyncInfo info2 = parent2.getFolderSyncInfo(); return (info1 != null && info2 != null && info1.getRemoteLocation().equals(info2.getRemoteLocation())); } catch (CVSException e) { // Ignore } return false; } private void updateFilterMode(int mode) { currentFilerMode=mode; switch(mode){ case LOCAL_MODE: localFilteredOut = false; remoteFilteredOut = true; localMode.setChecked(true); remoteMode.setChecked(false); remoteLocalMode.setChecked(false); break; case REMOTE_MODE: localFilteredOut = true; remoteFilteredOut = false; localMode.setChecked(false); remoteMode.setChecked(true); remoteLocalMode.setChecked(false); break; case REMOTE_LOCAL_MODE: localFilteredOut = false; remoteFilteredOut = false; localMode.setChecked(false); remoteMode.setChecked(false); remoteLocalMode.setChecked(true); break; } //the refresh job gets created once the input is set //don't bother trying to refresh any history until the input has been set if (refreshCVSFileHistoryJob != null){ //don't refetch, but not a select only job (ie. have to get the //existing revisions corresponding to the mode change) refreshHistory(false, false, CVSFileHistory.REFRESH_LOCAL | CVSFileHistory.REFRESH_REMOTE); } } public TreeViewer getTreeViewer() { return treeViewer; } public void showFilter(CVSHistoryFilter filter) { if (historyFilter != null) treeViewer.removeFilter(historyFilter); historyFilter = filter; int before = cvsFileHistory.getFileRevisions().length; treeViewer.addFilter(historyFilter); IHistoryPageSite historyPageSite =getHistoryPageSite(); if (historyPageSite instanceof WorkbenchHistoryPageSite){ IWorkbenchPart part = ((WorkbenchHistoryPageSite) historyPageSite).getPart(); if (part instanceof GenericHistoryView){ String revisions = NLS.bind(CVSUIMessages.CVSHistoryPage_FilterOnMessage, new Object[]{Integer.valueOf(historyFilter.getMatchCount()),Integer.valueOf(before)}); String old = getName(); description = NLS.bind(CVSUIMessages.CVSHistoryPage_FilterDescription, new Object[]{file.getName(), revisions}); CVSHistoryPage.this.firePropertyChange(CVSHistoryPage.this, P_NAME, old, getName()); } } toggleFilterAction.setEnabled(true); } /* * Sets the filter mode for the page. * param flag LOCAL_MODE, REMOTE_MODE, REMOTE_LOCAL_MODE */ public void setMode(int flag){ switch(flag){ case LOCAL_MODE: localMode.setChecked(true); localMode.run(); break; case REMOTE_MODE: remoteMode.setChecked(true); remoteMode.run(); break; case REMOTE_LOCAL_MODE: remoteLocalMode.setChecked(true); remoteLocalMode.run(); break; } //refetch revisions, not a select only job refreshHistory(true, false, CVSFileHistory.REFRESH_LOCAL | CVSFileHistory.REFRESH_REMOTE); } /** * Save any changes that are buffered in the pages typed element. * @param monitor a progress monitor. * @throws CoreException */ public void saveChanges(IProgressMonitor monitor) throws CoreException { if (fileElement instanceof LocalResourceTypedElement) { LocalResourceTypedElement element = (LocalResourceTypedElement) fileElement; element.commit(monitor); } } private void setStatusLineMessage(String message) { IPageSite workbenchPageSite = getHistoryPageSite().getWorkbenchPageSite(); if (workbenchPageSite != null) { workbenchPageSite.getActionBars().getStatusLineManager().setMessage(message); } } private static void printDebugInfo(String message, IFile file, CVSFileHistory history, Throwable t) { if (!Policy.DEBUG_HISTORY) return; String time= new SimpleDateFormat("m:ss.SSS").format(new Date(System.currentTimeMillis())); //$NON-NLS-1$ String fileName= file != null ? file.getName() : ""; //$NON-NLS-1$ String fileHistoryID= history != null ? history.toString() : ""; //$NON-NLS-1$ int i= fileHistoryID.indexOf('@'); if (i != -1) fileHistoryID= fileHistoryID.substring(i); System.out.println(time + ": " + fileName + ", " + fileHistoryID + ": " + message); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if (t != null) t.printStackTrace(); } }