Index: C:/dev/eclipse/org.tigris.subversion.subclipse.ui/plugin.xml =================================================================== --- C:/dev/eclipse/org.tigris.subversion.subclipse.ui/plugin.xml (revision 2232) +++ C:/dev/eclipse/org.tigris.subversion.subclipse.ui/plugin.xml (working copy) @@ -1006,4 +1006,14 @@ + + + + + + + Index: C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/history/SVNHistoryPage.java =================================================================== --- C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/history/SVNHistoryPage.java (revision 0) +++ C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/history/SVNHistoryPage.java (revision 0) @@ -0,0 +1,879 @@ +package org.tigris.subversion.subclipse.ui.history; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeMap; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.JFaceColors; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.TextViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.team.core.RepositoryProvider; +import org.eclipse.team.core.TeamException; +import org.eclipse.team.ui.history.HistoryPage; +import org.eclipse.ui.PlatformUI; +import org.tigris.subversion.subclipse.core.ISVNLocalResource; +import org.tigris.subversion.subclipse.core.ISVNRemoteResource; +import org.tigris.subversion.subclipse.core.SVNException; +import org.tigris.subversion.subclipse.core.SVNProviderPlugin; +import org.tigris.subversion.subclipse.core.SVNTeamProvider; +import org.tigris.subversion.subclipse.core.history.AliasManager; +import org.tigris.subversion.subclipse.core.history.ILogEntry; +import org.tigris.subversion.subclipse.core.history.LogEntry; +import org.tigris.subversion.subclipse.core.history.LogEntryChangePath; +import org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot; +import org.tigris.subversion.subclipse.ui.ISVNUIConstants; +import org.tigris.subversion.subclipse.ui.Policy; +import org.tigris.subversion.subclipse.ui.SVNUIPlugin; +import org.tigris.subversion.subclipse.ui.internal.Utils; +import org.tigris.subversion.subclipse.ui.settings.ProjectProperties; +import org.tigris.subversion.subclipse.ui.util.LinkList; +import org.tigris.subversion.svnclientadapter.SVNRevision; + +public class SVNHistoryPage extends HistoryPage { + + private SashForm svnHistoryPageControl; + private SashForm innerSashForm; + + HistoryTableProvider historyTableProvider; + TableViewer tableHistoryViewer; + StructuredViewer changePathsViewer; + TextViewer textViewer; + + private boolean showComments; + private boolean showAffectedPaths; + private boolean wrapCommentsText; + boolean shutdown = false; + + private ProjectProperties projectProperties; + private LinkList linkList; + + // cached for efficiency + ILogEntry[] entries; + LogEntryChangePath[] currentLogEntryChangePath; + ILogEntry lastEntry; + SVNRevision revisionStart = SVNRevision.HEAD; + + FetchLogEntriesJob fetchLogEntriesJob = null; + FetchAllLogEntriesJob fetchAllLogEntriesJob = null; + FetchNextLogEntriesJob fetchNextLogEntriesJob = null; + FetchChangePathJob fetchChangePathJob = null; + AliasManager tagManager; + + public IResource resource; + + IAction getNextAction; + IAction toggleStopOnCopyAction; + + + public SVNHistoryPage(Object object) { + // TODO Auto-generated constructor stub + } + + public Control getControl() { + return svnHistoryPageControl; + } + + public void setFocus() { + // TODO Auto-generated method stub + + } + + public String getDescription() { + // TODO Auto-generated method stub + return null; + } + + public String getName() { + // TODO Auto-generated method stub + return null; + } + + public boolean isValidInput(Object object) { + if(object instanceof IResource) { + RepositoryProvider provider = RepositoryProvider.getProvider(((IResource) object).getProject()); + return provider instanceof SVNTeamProvider; + } else if(object instanceof ISVNRemoteResource) { + return true; + // } else if(object instanceof CVSFileRevision) { + // return true; + // } else if(object instanceof CVSLocalFileRevision) { + // return true; + } + + // TODO + + return false; + } + + public void refresh() { + // TODO Auto-generated method stub + + } + + public Object getAdapter(Class adapter) { + // TODO Auto-generated method stub + return null; + } + + public boolean inputSet() { + Object input = getInput(); + if(!(input instanceof IResource)) { + return false; + } + + IResource res = (IResource) input; + RepositoryProvider teamProvider = RepositoryProvider.getProvider(res.getProject(), SVNProviderPlugin.getTypeId()); + if (teamProvider != null) { + try { + ISVNLocalResource localResource = SVNWorkspaceRoot.getSVNResourceFor(res); + if ( localResource != null + && !localResource.getStatus().isAdded() + && localResource.getStatus().isManaged() ) { + projectProperties = ProjectProperties.getProjectProperties(res); + ISVNRemoteResource baseResource = localResource.getBaseResource(); + historyTableProvider.setRemoteResource(baseResource); + historyTableProvider.setResource(res); + this.resource = res; + tableHistoryViewer.setInput(baseResource); + // setContentDescription(Policy.bind("HistoryView.titleWithArgument", baseResource.getName())); //$NON-NLS-1$ + // setTitleToolTip(baseResource.getRepositoryRelativePath()); + return true; + } + } catch (TeamException e) { + SVNUIPlugin.openError(getSite().getShell(), null, null, e); + } + } + // TODO + return false; + } + + public void createControl(Composite parent) { + IPreferenceStore store = SVNUIPlugin.getPlugin().getPreferenceStore(); + // linkingEnabled = store.getBoolean(ISVNUIConstants.PREF_HISTORY_VIEW_EDITOR_LINKING); + + svnHistoryPageControl = new SashForm(parent, SWT.VERTICAL); + svnHistoryPageControl.setLayoutData(new GridData(GridData.FILL_BOTH)); + + createTableHistory(svnHistoryPageControl); + createAffectedPathsViewer(store.getInt(ISVNUIConstants.PREF_AFFECTED_PATHS_LAYOUT)); + + contributeActions(); + + svnHistoryPageControl.setWeights(new int[] { 70, 30}); + + // set F1 help + // PlatformUI.getWorkbench().getHelpSystem().setHelp(svnHistoryPageControl, IHelpContextIds.RESOURCE_HISTORY_VIEW); + // initDragAndDrop(); + + // add listener for editor page activation - this is to support editor linking + // getSite().getPage().addPartListener(partListener); + // getSite().getPage().addPartListener(partListener2); + } + + private void contributeActions() { + SVNUIPlugin plugin = SVNUIPlugin.getPlugin(); + IPreferenceStore store = plugin.getPreferenceStore(); + + // Toggle stop on copy action + toggleStopOnCopyAction = new Action(Policy.bind("HistoryView.stopOnCopy")) { //$NON-NLS-1$ + public void run() { + setStopOnCopy(); + SVNUIPlugin.getPlugin().getPreferenceStore().setValue(ISVNUIConstants.PREF_STOP_ON_COPY, toggleStopOnCopyAction.isChecked()); + } + }; + toggleStopOnCopyAction.setChecked(store.getBoolean(ISVNUIConstants.PREF_STOP_ON_COPY)); + + int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH); + getNextAction = new Action(Policy.bind("HistoryView.getNext"), plugin.getImageDescriptor(ISVNUIConstants.IMG_GET_NEXT)) { //$NON-NLS-1$ + public void run() { + final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource(); + if(fetchNextLogEntriesJob == null) { + fetchNextLogEntriesJob = new FetchNextLogEntriesJob(); + } + if(fetchNextLogEntriesJob.getState() != Job.NONE) { + fetchNextLogEntriesJob.cancel(); + try { + fetchNextLogEntriesJob.join(); + } catch (InterruptedException e) { + SVNUIPlugin.log(new SVNException(Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()), e)); //$NON-NLS-1$ + } + } + fetchNextLogEntriesJob.setRemoteFile(remoteResource); + Utils.schedule(fetchNextLogEntriesJob, getSite()); + } + }; + getNextAction.setToolTipText(Policy.bind("HistoryView.getNext") + " " + entriesToFetch); //$NON-NLS-1$ + if (entriesToFetch <= 0) getNextAction.setEnabled(false); + } + + void setStopOnCopy() { + refresh(); + } + + protected void createTableHistory(Composite parent) { + historyTableProvider = new HistoryTableProvider(); + tableHistoryViewer = historyTableProvider.createTable(parent); + + // set the content provider for the table + tableHistoryViewer.setContentProvider(new IStructuredContentProvider() { + + public Object[] getElements(Object inputElement) { + // Short-circuit to optimize + if(entries != null) + return entries; + + if( !(inputElement instanceof ISVNRemoteResource)) + return null; + final ISVNRemoteResource remoteResource = (ISVNRemoteResource) inputElement; + + if(fetchLogEntriesJob == null) { + fetchLogEntriesJob = new FetchLogEntriesJob(); + } + if(fetchLogEntriesJob.getState() != Job.NONE) { + fetchLogEntriesJob.cancel(); + try { + fetchLogEntriesJob.join(); + } catch(InterruptedException e) { + SVNUIPlugin.log(new SVNException( + Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()), e)); //$NON-NLS-1$ + } + } + fetchLogEntriesJob.setRemoteFile(remoteResource); + Utils.schedule(fetchLogEntriesJob, SVNUIPlugin.getPlugin().getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart().getSite()); + + return new Object[ 0]; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + entries = null; + lastEntry = null; + revisionStart = SVNRevision.HEAD; + } + }); + + // set the selectionchanged listener for the table + // updates the comments and affected paths when selection changes + tableHistoryViewer.addSelectionChangedListener(new ISelectionChangedListener() { + private ILogEntry currentLogEntry; + + public void selectionChanged(SelectionChangedEvent event) { + ISelection selection = event.getSelection(); + ILogEntry logEntry = getLogEntry((IStructuredSelection) selection); + if(logEntry != currentLogEntry) { + this.currentLogEntry = logEntry; + updatePanels(selection); + } + } + }); + + } + + public void createAffectedPathsViewer(int layout) { +// for( int i = 0; i < toggleAffectedPathsLayoutActions.length; i++) { +// ToggleAffectedPathsLayoutAction action = toggleAffectedPathsLayoutActions[i]; +// action.setChecked(layout==action.getLayout()); +// } + + if(innerSashForm!=null) { + innerSashForm.dispose(); + } + if(changePathsViewer!=null) { + changePathsViewer.getControl().dispose(); + } + + innerSashForm = new SashForm(svnHistoryPageControl, SWT.HORIZONTAL); + + switch(layout) { + case ISVNUIConstants.LAYOUT_COMPRESSED: + changePathsViewer = new ChangePathsTreeViewer(innerSashForm); + changePathsViewer.setContentProvider(new ChangePathsTreeContentProvider()); + break; + default: + changePathsViewer = new ChangePathsTableProvider(innerSashForm); + changePathsViewer.setContentProvider(new ChangePathsTableContentProvider()); + break; + } + +// changePathsViewer.addSelectionChangedListener(new ISelectionChangedListener() { +// public void selectionChanged(SelectionChangedEvent event) { +// selection = changePathsViewer.getSelection(); +// } +// }); + +// changePathsViewer.getControl().addListener(SWT.DefaultSelection, new Listener() { +// public void handleEvent(Event e) { +// getOpenChangedPathAction().run(); +// } +// }); + + textViewer = createText(innerSashForm); + setViewerVisibility(); + innerSashForm.layout(); + svnHistoryPageControl.layout(); + + updatePanels(tableHistoryViewer.getSelection()); + } + + /** + * Create the TextViewer for the logEntry comments + */ + protected TextViewer createText(Composite parent) { + TextViewer result = new TextViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.READ_ONLY); +// result.addSelectionChangedListener(new ISelectionChangedListener() { +// public void selectionChanged(SelectionChangedEvent event) { +// copyAction.update(); +// } +// }); + + Font font = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getFontRegistry().get(ISVNUIConstants.SVN_COMMENT_FONT); + if (font != null) result.getTextWidget().setFont(font); +// result.getTextWidget().addMouseListener(new MouseAdapter() { +// public void mouseDown(MouseEvent e) { +// if (e.button != 1) { +// return; +// } +// mouseDown = true; +// } +// public void mouseUp(MouseEvent e) { +// mouseDown = false; +// StyledText text = (StyledText)e.widget; +// int offset = text.getCaretOffset(); +// if (dragEvent) { +// // don't activate a link during a drag/mouse up operation +// dragEvent = false; +// if (linkList != null && linkList.isLinkAt(offset)) { +// text.setCursor(handCursor); +// } +// } else { +// if (linkList != null && linkList.isLinkAt(offset)) { +// text.setCursor(busyCursor); +// openLink(linkList.getLinkAt(offset)); +// text.setCursor(null); +// } +// } +// } +// }); + +// result.getTextWidget().addMouseMoveListener(new MouseMoveListener() { +// public void mouseMove(MouseEvent e) { +// // Do not change cursor on drag events +// if (mouseDown) { +// if (!dragEvent) { +// StyledText text = (StyledText)e.widget; +// text.setCursor(null); +// } +// dragEvent = true; +// return; +// } +// StyledText text = (StyledText)e.widget; +// int offset = -1; +// try { +// offset = text.getOffsetAtLocation(new Point(e.x, e.y)); +// } catch (IllegalArgumentException ex) {} +// if (offset == -1) +// text.setCursor(null); +// else if (linkList != null && linkList.isLinkAt(offset)) +// text.setCursor(handCursor); +// else +// text.setCursor(null); +// } +// }); + + // Create actions for the text editor (copy and select all) +// copyAction = new TextViewerAction(result, ITextOperationTarget.COPY); +// copyAction.setText(Policy.bind("HistoryView.copy")); //$NON-NLS-1$ +// +// selectAllAction = new TextViewerAction(result, ITextOperationTarget.SELECT_ALL); +// selectAllAction.setText(Policy.bind("HistoryView.selectAll")); //$NON-NLS-1$ +// +// getHistoryPageSite().getToolBarManager(). +// +// IActionBars actionBars = getViewSite().getActionBars(); +// actionBars.setGlobalActionHandler(ITextEditorActionConstants.COPY, copyAction); +// actionBars.setGlobalActionHandler(ITextEditorActionConstants.SELECT_ALL, selectAllAction); +// actionBars.updateActionBars(); +// +// // Contribute actions to popup menu for the comments area +// { +// MenuManager menuMgr = new MenuManager(); +// menuMgr.setRemoveAllWhenShown(true); +// menuMgr.addMenuListener(new IMenuListener() { +// public void menuAboutToShow(IMenuManager menuMgr) { +// fillTextMenu(menuMgr); +// } +// }); +// +// StyledText text = result.getTextWidget(); +// Menu menu = menuMgr.createContextMenu(text); +// text.setMenu(menu); +// } + + + return result; + } + + ILogEntry getLogEntry(IStructuredSelection ss) { + if(ss.getFirstElement() instanceof LogEntryChangePath) { + return ((LogEntryChangePath)ss.getFirstElement()).getLogEntry(); + } + return (ILogEntry) ss.getFirstElement(); + } + + void updatePanels(ISelection selection) { + if (selection == null || !(selection instanceof IStructuredSelection)) { + textViewer.setDocument(new Document("")); //$NON-NLS-1$ + changePathsViewer.setInput(null); + return; + } + IStructuredSelection ss = (IStructuredSelection)selection; + if (ss.size() != 1) { + textViewer.setDocument(new Document("")); //$NON-NLS-1$ + changePathsViewer.setInput(null); + return; + } + LogEntry entry = (LogEntry)ss.getFirstElement(); + textViewer.setDocument(new Document(entry.getComment())); + StyledText text = textViewer.getTextWidget(); + if (projectProperties == null) linkList = ProjectProperties.getUrls(entry.getComment()); + else linkList = projectProperties.getLinkList(entry.getComment()); + if (linkList != null) { + int[][] linkRanges = linkList.getLinkRanges(); + // String[] urls = linkList.getUrls(); + for (int i = 0; i < linkRanges.length; i++) { + text.setStyleRange(new StyleRange(linkRanges[i][0], linkRanges[i][1], JFaceColors.getHyperlinkText(Display.getCurrent()), null)); + } + } + changePathsViewer.setInput(entry); + } + + void setViewerVisibility() { + if (showComments && showAffectedPaths) { + svnHistoryPageControl.setMaximizedControl(null); + innerSashForm.setMaximizedControl(null); + } else if (showComments) { + svnHistoryPageControl.setMaximizedControl(null); + innerSashForm.setMaximizedControl(textViewer.getTextWidget()); + } else if (showAffectedPaths) { + svnHistoryPageControl.setMaximizedControl(null); + innerSashForm.setMaximizedControl(changePathsViewer.getControl()); + } else { + svnHistoryPageControl.setMaximizedControl(tableHistoryViewer.getControl()); + } + + changePathsViewer.refresh(); + textViewer.getTextWidget().setWordWrap(wrapCommentsText); + } + + void setCurrentLogEntryChangePath( final LogEntryChangePath[] currentLogEntryChangePath) { + this.currentLogEntryChangePath = currentLogEntryChangePath; + if(!shutdown) { + //Getting the changePaths + /* + final SVNRevision.Number revisionId = remoteResource.getLastChangedRevision(); + */ + getSite().getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + if(currentLogEntryChangePath != null && changePathsViewer != null && !changePathsViewer.getControl().isDisposed()) { + // once we got the changePath, we refresh the table + changePathsViewer.refresh(); + //selectRevision(revisionId); + } + } + }); + } + } + + /** + * Select the revision in the receiver. + */ + public void selectRevision(SVNRevision.Number revision) { + if (entries == null) { + return; + } + + ILogEntry entry = null; + for (int i = 0; i < entries.length; i++) { + if (entries[i].getRevision().equals(revision)) { + entry = entries[i]; + break; + } + } + + if (entry != null) { + IStructuredSelection selection = new StructuredSelection(entry); + tableHistoryViewer.setSelection(selection, true); + } + } + + public void scheduleFetchChangePathJob(ILogEntry logEntry) { + if(fetchChangePathJob == null) { + fetchChangePathJob = new FetchChangePathJob(); + } + if(fetchChangePathJob.getState() != Job.NONE) { + fetchChangePathJob.cancel(); + try { + fetchChangePathJob.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + //SVNUIPlugin.log(new SVNException(Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()), e)); //$NON-NLS-1$ + } + } + fetchChangePathJob.setLogEntry(logEntry); + Utils.schedule(fetchChangePathJob, getSite()); + } + + public boolean isShowChangePaths() { + // return toggleShowAffectedPathsAction.isChecked(); + return true; + } + + + + + private class FetchLogEntriesJob extends Job { + public ISVNRemoteResource remoteResource; + public FetchLogEntriesJob() { + super(Policy.bind("HistoryView.fetchHistoryJob")); //$NON-NLS-1$; + } + public void setRemoteFile(ISVNRemoteResource resource) { + this.remoteResource = resource; + } + public IStatus run(IProgressMonitor monitor) { + try { + if(remoteResource != null && !shutdown) { + if (resource == null) { + if (remoteResource == null || !SVNUIPlugin.getPlugin().getPreferenceStore().getBoolean(ISVNUIConstants.PREF_SHOW_TAGS_IN_REMOTE)) + tagManager = null; + else + tagManager = new AliasManager(remoteResource.getUrl()); + } + else tagManager = new AliasManager(resource); + SVNRevision pegRevision = remoteResource.getRevision(); + SVNRevision revisionEnd = new SVNRevision.Number(0); + boolean stopOnCopy = toggleStopOnCopyAction.isChecked(); + IPreferenceStore store = SVNUIPlugin.getPlugin().getPreferenceStore(); + int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH); + long limit = entriesToFetch; + entries = remoteResource.getLogEntries(monitor, pegRevision, revisionStart, revisionEnd, stopOnCopy, limit + 1, tagManager); + long entriesLength = entries.length; + if (entriesLength > limit) { + ILogEntry[] fetchedEntries = new ILogEntry[entries.length - 1]; + for (int i = 0; i < entries.length - 1; i++) + fetchedEntries[i] = entries[i]; + entries = fetchedEntries; + getNextAction.setEnabled(true); + } else { + getNextAction.setEnabled(false); + } + final SVNRevision.Number revisionId = remoteResource.getLastChangedRevision(); + getSite().getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + if(entries != null && tableHistoryViewer != null && ! tableHistoryViewer.getTable().isDisposed()) { + // once we got the entries, we refresh the table + if (entries.length > 0) { + lastEntry = entries[entries.length - 1]; + long lastEntryNumber = lastEntry.getRevision().getNumber(); + revisionStart = new SVNRevision.Number(lastEntryNumber - 1); + } + tableHistoryViewer.refresh(); + selectRevision(revisionId); + } + } + }); + } + return Status.OK_STATUS; + } catch (TeamException e) { + return e.getStatus(); + } + } + } + + private class FetchNextLogEntriesJob extends Job { + public ISVNRemoteResource remoteResource; + public FetchNextLogEntriesJob() { + super(Policy.bind("HistoryView.fetchHistoryJob")); //$NON-NLS-1$; + } + public void setRemoteFile(ISVNRemoteResource resource) { + this.remoteResource = resource; + } + public IStatus run(IProgressMonitor monitor) { + try { + if(remoteResource != null && !shutdown) { + SVNRevision pegRevision = remoteResource.getRevision(); + SVNRevision revisionEnd = new SVNRevision.Number(0); + boolean stopOnCopy = toggleStopOnCopyAction.isChecked(); + IPreferenceStore store = SVNUIPlugin.getPlugin().getPreferenceStore(); + int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH); + long limit = entriesToFetch; + ILogEntry[] nextEntries = remoteResource.getLogEntries(monitor, pegRevision, revisionStart, revisionEnd, stopOnCopy, limit + 1, tagManager); + long entriesLength = nextEntries.length; + if (entriesLength > limit) { + ILogEntry[] fetchedEntries = new ILogEntry[nextEntries.length - 1]; + for (int i = 0; i < nextEntries.length - 1; i++) + fetchedEntries[i] = nextEntries[i]; + getNextAction.setEnabled(true); + } else { + getNextAction.setEnabled(false); + } + ArrayList entryArray = new ArrayList(); + if (entries == null) entries = new ILogEntry[0]; + for (int i = 0; i < entries.length; i++) entryArray.add(entries[i]); + for (int i = 0; i < nextEntries.length; i++) entryArray.add(nextEntries[i]); + entries = new ILogEntry[entryArray.size()]; + entryArray.toArray(entries); + getSite().getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + if(entries != null && tableHistoryViewer != null && ! tableHistoryViewer.getTable().isDisposed()) { + // once we got the entries, we refresh the table + ISelection selection = tableHistoryViewer.getSelection(); + tableHistoryViewer.refresh(); + tableHistoryViewer.setSelection(selection); + if (entries.length > 0) { + lastEntry = entries[entries.length - 1]; + long lastEntryNumber = lastEntry.getRevision().getNumber(); + revisionStart = new SVNRevision.Number(lastEntryNumber - 1); + } + } + } + }); + } + return Status.OK_STATUS; + } catch (TeamException e) { + return e.getStatus(); + } + } + } + + private class FetchAllLogEntriesJob extends Job { + public ISVNRemoteResource remoteResource; + public FetchAllLogEntriesJob() { + super(Policy.bind("HistoryView.fetchHistoryJob")); //$NON-NLS-1$; + } + public void setRemoteFile(ISVNRemoteResource resource) { + this.remoteResource = resource; + } + public IStatus run(IProgressMonitor monitor) { + try { + if(remoteResource != null && !shutdown) { + if (resource == null) { + if (remoteResource == null || !SVNUIPlugin.getPlugin().getPreferenceStore().getBoolean(ISVNUIConstants.PREF_SHOW_TAGS_IN_REMOTE)) + tagManager = null; + else + tagManager = new AliasManager(remoteResource.getUrl()); + } + else tagManager = new AliasManager(resource); + SVNRevision pegRevision = remoteResource.getRevision(); + revisionStart = SVNRevision.HEAD; + SVNRevision revisionEnd = new SVNRevision.Number(0); + boolean stopOnCopy = toggleStopOnCopyAction.isChecked(); + long limit = 0; + entries = remoteResource.getLogEntries(monitor, pegRevision, revisionStart, revisionEnd, stopOnCopy, limit, tagManager); + final SVNRevision.Number revisionId = remoteResource.getLastChangedRevision(); + getSite().getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + if(entries != null && tableHistoryViewer != null && ! tableHistoryViewer.getTable().isDisposed()) { + // once we got the entries, we refresh the table + if (entries.length > 0) { + lastEntry = entries[entries.length - 1]; + long lastEntryNumber = lastEntry.getRevision().getNumber(); + revisionStart = new SVNRevision.Number(lastEntryNumber - 1); + } + tableHistoryViewer.refresh(); + selectRevision(revisionId); + } + } + }); + } + return Status.OK_STATUS; + } catch (TeamException e) { + return e.getStatus(); + } + } + } + + class FetchChangePathJob extends Job { + public ILogEntry logEntry; + + public FetchChangePathJob() { + super(Policy.bind("HistoryView.fetchChangePathJob")); //$NON-NLS-1$; + } + + public void setLogEntry(ILogEntry logEntry) { + this.logEntry = logEntry; + } + + public IStatus run(IProgressMonitor monitor) { + if(logEntry.getResource()!=null) { + setCurrentLogEntryChangePath(logEntry.getLogEntryChangePaths()); + } + return Status.OK_STATUS; + } + } + + + static final LogEntryChangePath[] EMPTY_CHANGE_PATHS = new LogEntryChangePath[0]; + class ChangePathsTableContentProvider implements IStructuredContentProvider { + + public Object[] getElements(Object inputElement) { + if (!isShowChangePaths() || !(inputElement instanceof ILogEntry)) { + return EMPTY_CHANGE_PATHS; + } + + ILogEntry logEntry = (ILogEntry)inputElement; + if (SVNProviderPlugin.getPlugin().getSVNClientManager().isFetchChangePathOnDemand()) { + if (currentLogEntryChangePath != null) { + return currentLogEntryChangePath; + } + scheduleFetchChangePathJob(logEntry); + return EMPTY_CHANGE_PATHS; + } + + return logEntry.getLogEntryChangePaths(); + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + currentLogEntryChangePath = null; + } + + } + + class ChangePathsTreeContentProvider implements ITreeContentProvider { + + public Object[] getChildren(Object parentElement) { + if(parentElement instanceof HistoryFolder) { + return ((HistoryFolder) parentElement).getChildren(); + } + return null; + } + + public Object getParent(Object element) { + return null; + } + + public boolean hasChildren(Object element) { + if(element instanceof HistoryFolder) { + HistoryFolder folder = (HistoryFolder) element; + return folder.getChildren().length>0; + } + return false; + } + + public Object[] getElements(Object inputElement) { + if (!isShowChangePaths() || !(inputElement instanceof ILogEntry)) { + return EMPTY_CHANGE_PATHS; + } + + if (currentLogEntryChangePath != null) { + + } + + ILogEntry logEntry = (ILogEntry)inputElement; + if (SVNProviderPlugin.getPlugin().getSVNClientManager().isFetchChangePathOnDemand()) { + if (currentLogEntryChangePath != null) { + return getGroups(currentLogEntryChangePath); + } + scheduleFetchChangePathJob(logEntry); + return EMPTY_CHANGE_PATHS; + } + + return getGroups(logEntry.getLogEntryChangePaths()); + } + + private Object[] getGroups(LogEntryChangePath[] changePaths) { + // 1st pass. Collect folder names + Set folderNames = new HashSet(); + for( int i = 0; i < changePaths.length; i++) { + folderNames.add(getFolderName(changePaths[i])); + } + + // 2nd pass. Sorting out explicitly changed folders + TreeMap folders = new TreeMap(); + for( int i = 0; i < changePaths.length; i++) { + LogEntryChangePath changePath = changePaths[i]; + String path = changePath.getPath(); + if(folderNames.contains(path)) { + // changed folder + HistoryFolder folder = (HistoryFolder) folders.get(path); + if(folder==null) { + folder = new HistoryFolder(changePath); + folders.put(path, folder); + } + } else { + // changed resource + path = getFolderName(changePath); + HistoryFolder folder = (HistoryFolder) folders.get(path); + if(folder==null) { + folder = new HistoryFolder(path); + folders.put(path, folder); + } + folder.add(changePath); + } + } + + // 3rd pass. Optimize folders with one or no children + ArrayList groups = new ArrayList(); + for( Iterator it = folders.values().iterator(); it.hasNext();) { + HistoryFolder folder = (HistoryFolder) it.next(); + Object[] children = folder.getChildren(); + if(children.length==1) { + LogEntryChangePath changePath = (LogEntryChangePath)children[0]; + groups.add(new HistoryFolder(changePath)); + } else if(children.length>1) { + groups.add(folder); + } + } + + return groups.toArray(); + } + + private String getFolderName(LogEntryChangePath changePath) { + String path = changePath.getPath(); + int n = path.lastIndexOf('/'); + return n>-1 ? path.substring(0, n) : path; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + currentLogEntryChangePath = null; + } + + } + +} Index: C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/history/SVNHistoryPageSource.java =================================================================== --- C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/history/SVNHistoryPageSource.java (revision 0) +++ C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/history/SVNHistoryPageSource.java (revision 0) @@ -0,0 +1,21 @@ + +package org.tigris.subversion.subclipse.ui.history; + +import org.eclipse.core.resources.IResource; +import org.eclipse.team.ui.history.HistoryPageSource; +import org.eclipse.ui.part.Page; + + +public class SVNHistoryPageSource extends HistoryPageSource { + + public boolean canShowHistoryFor(Object object) { + return object instanceof IResource && ((IResource) object).getType() != IResource.ROOT; + } + + public Page createPage(Object object) { + SVNHistoryPage page = new SVNHistoryPage(object); + return page; + } + +} + Index: C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/repository/model/SVNAdapterFactory.java =================================================================== --- C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/repository/model/SVNAdapterFactory.java (revision 2232) +++ C:/dev/eclipse/org.tigris.subversion.subclipse.ui/src/org/tigris/subversion/subclipse/ui/repository/model/SVNAdapterFactory.java (working copy) @@ -13,6 +13,7 @@ import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.team.ui.history.IHistoryPageSource; import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.progress.IDeferredWorkbenchAdapter; import org.eclipse.ui.views.properties.IPropertySource; @@ -20,6 +21,7 @@ import org.tigris.subversion.subclipse.core.ISVNRemoteFolder; import org.tigris.subversion.subclipse.core.ISVNRemoteResource; import org.tigris.subversion.subclipse.core.ISVNRepositoryLocation; +import org.tigris.subversion.subclipse.ui.history.SVNHistoryPageSource; import org.tigris.subversion.subclipse.ui.repository.properties.SVNRemoteResourcePropertySource; public class SVNAdapterFactory implements IAdapterFactory { @@ -26,6 +28,7 @@ private Object fileAdapter = new RemoteFileElement(); private Object folderAdapter = new RemoteFolderElement(); private Object rootAdapter = new SVNRepositoryRootElement(); + private Object pageHistoryParticipant = new SVNHistoryPageSource(); // Property cache private Object cachedPropertyObject = null; @@ -51,6 +54,11 @@ if (IPropertySource.class == adapterType) { return getPropertySource(adaptableObject); } + + if(IHistoryPageSource.class == adapterType) { + return pageHistoryParticipant; + } + return null; } @@ -68,7 +76,7 @@ * Method declared on IAdapterFactory. */ public Class[] getAdapterList() { - return new Class[] {IWorkbenchAdapter.class, IPropertySource.class, IDeferredWorkbenchAdapter.class}; + return new Class[] {IWorkbenchAdapter.class, IPropertySource.class, IDeferredWorkbenchAdapter.class, IHistoryPageSource.class}; } /** * Returns the property source for the given object. Caches