2 * Copyright (C) 2006 Robin Rosenberg
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License, version 2.1, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org
.spearce
.egit
.ui
;
19 import java
.io
.IOException
;
20 import java
.util
.Date
;
21 import java
.util
.HashMap
;
24 import org
.eclipse
.compare
.CompareConfiguration
;
25 import org
.eclipse
.compare
.structuremergeviewer
.ICompareInput
;
26 import org
.eclipse
.core
.resources
.IProject
;
27 import org
.eclipse
.core
.resources
.IResource
;
28 import org
.eclipse
.core
.resources
.IResourceChangeEvent
;
29 import org
.eclipse
.core
.resources
.IResourceChangeListener
;
30 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
31 import org
.eclipse
.core
.runtime
.IAdaptable
;
32 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
33 import org
.eclipse
.core
.runtime
.IStatus
;
34 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
35 import org
.eclipse
.core
.runtime
.Status
;
36 import org
.eclipse
.core
.runtime
.jobs
.Job
;
37 import org
.eclipse
.jface
.action
.IMenuListener
;
38 import org
.eclipse
.jface
.action
.IMenuManager
;
39 import org
.eclipse
.jface
.action
.MenuManager
;
40 import org
.eclipse
.jface
.viewers
.ColumnWeightData
;
41 import org
.eclipse
.jface
.viewers
.ILazyTreeContentProvider
;
42 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
43 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
44 import org
.eclipse
.jface
.viewers
.LabelProvider
;
45 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
46 import org
.eclipse
.jface
.viewers
.TableLayout
;
47 import org
.eclipse
.jface
.viewers
.TreeViewer
;
48 import org
.eclipse
.jface
.viewers
.Viewer
;
49 import org
.eclipse
.swt
.SWT
;
50 import org
.eclipse
.swt
.events
.MouseEvent
;
51 import org
.eclipse
.swt
.events
.MouseListener
;
52 import org
.eclipse
.swt
.events
.MouseMoveListener
;
53 import org
.eclipse
.swt
.events
.SelectionAdapter
;
54 import org
.eclipse
.swt
.events
.SelectionEvent
;
55 import org
.eclipse
.swt
.graphics
.Image
;
56 import org
.eclipse
.swt
.graphics
.Point
;
57 import org
.eclipse
.swt
.layout
.GridData
;
58 import org
.eclipse
.swt
.layout
.GridLayout
;
59 import org
.eclipse
.swt
.widgets
.Composite
;
60 import org
.eclipse
.swt
.widgets
.Control
;
61 import org
.eclipse
.swt
.widgets
.Event
;
62 import org
.eclipse
.swt
.widgets
.Listener
;
63 import org
.eclipse
.swt
.widgets
.Menu
;
64 import org
.eclipse
.swt
.widgets
.Tree
;
65 import org
.eclipse
.swt
.widgets
.TreeColumn
;
66 import org
.eclipse
.swt
.widgets
.TreeItem
;
67 import org
.eclipse
.team
.core
.RepositoryProvider
;
68 import org
.eclipse
.team
.core
.history
.IFileHistory
;
69 import org
.eclipse
.team
.core
.history
.IFileHistoryProvider
;
70 import org
.eclipse
.team
.core
.history
.IFileRevision
;
71 import org
.eclipse
.team
.internal
.ui
.TeamUIMessages
;
72 import org
.eclipse
.team
.internal
.ui
.history
.DialogHistoryPageSite
;
73 import org
.eclipse
.team
.ui
.history
.HistoryPage
;
74 import org
.eclipse
.team
.ui
.history
.IHistoryCompareAdapter
;
75 import org
.eclipse
.team
.ui
.history
.IHistoryPageSite
;
76 import org
.spearce
.egit
.core
.GitProvider
;
77 import org
.spearce
.egit
.core
.internal
.mapping
.GitFileRevision
;
78 import org
.spearce
.egit
.core
.project
.RepositoryMapping
;
79 import org
.spearce
.egit
.ui
.internal
.actions
.GitCompareRevisionAction
;
80 import org
.spearce
.jgit
.lib
.Commit
;
81 import org
.spearce
.jgit
.lib
.ObjectId
;
82 import org
.spearce
.jgit
.lib
.Tag
;
83 import org
.spearce
.jgit
.lib
.Repository
.StGitPatch
;
85 public class GitHistoryPage
extends HistoryPage
implements IAdaptable
,
86 IHistoryCompareAdapter
{
88 private Composite localComposite
;
90 private TreeViewer viewer
;
94 private IFileRevision
[] fileRevisions
;
96 protected boolean hintShowDiffNow
;
98 public GitHistoryPage(Object object
) {
102 public boolean inputSet() {
104 viewer
.setInput(getInput());
105 // TODO Auto-generated method stub
109 public void createControl(Composite parent
) {
110 localComposite
= new Composite(parent
, SWT
.NONE
);
111 GridLayout layout
= new GridLayout();
112 layout
.marginHeight
= 0;
113 layout
.marginWidth
= 0;
114 localComposite
.setLayout(layout
);
115 GridData data
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
116 data
.grabExcessVerticalSpace
= true;
117 localComposite
.setLayoutData(data
);
119 createTree(localComposite
);
121 IHistoryPageSite parentSite
= getHistoryPageSite();
122 if (parentSite
!= null && parentSite
instanceof DialogHistoryPageSite
)
123 parentSite
.setSelectionProvider(viewer
);
125 final GitCompareRevisionAction compareAction
= new GitCompareRevisionAction(
127 final GitCompareRevisionAction compareActionPrev
= new GitCompareRevisionAction(
129 tree
.addMouseListener(new MouseListener() {
131 public void mouseUp(MouseEvent e
) {
134 public void mouseDown(MouseEvent e
) {
135 hintShowDiffNow
= e
.button
==1;
138 public void mouseDoubleClick(MouseEvent e
) {
143 tree
.addMouseMoveListener(new MouseMoveListener() {
145 public void mouseMove(MouseEvent e
) {
146 TreeItem item
= tree
.getItem(new Point(e
.x
,e
.y
));
147 if (item
!= null && item
!=lastItem
) {
148 IFileRevision rev
= (IFileRevision
) item
.getData();
149 String commitStr
=null;
150 if (rev
!=null && appliedPatches
!=null) {
151 String id
= rev
.getContentIdentifier();
152 if (!id
.equals("Workspace")) {
153 StGitPatch patch
= (StGitPatch
) appliedPatches
.get(new ObjectId(id
));
155 commitStr
= "Patch: "+patch
.getName();
157 commitStr
= "Workspace:";
160 if (commitStr
== null)
161 commitStr
= "Commit: "+rev
.getContentIdentifier();
162 tree
.setToolTipText(commitStr
+"\nAuthor:\t"+rev
.getAuthor()+"\nDate:\t"+new Date(rev
.getTimestamp())+"\n\n"+rev
.getComment());
168 tree
.addSelectionListener(new SelectionAdapter() {
169 public void widgetSelected(SelectionEvent e
) {
170 // update the current
171 TreeItem
[] selection
= tree
.getSelection();
172 IFileRevision
[] selection2
= new IFileRevision
[selection
.length
];
173 for (int i
= 0; i
< selection
.length
; ++i
) {
174 selection2
[i
] = (IFileRevision
) selection
[i
].getData();
177 compareAction
.setCurrentFileRevision(fileRevisions
[0]);
178 compareAction
.selectionChanged(new StructuredSelection(
180 IProject project
= ((IResource
) getInput()).getProject();
181 GitProvider provider
= (GitProvider
)RepositoryProvider
182 .getProvider(project
);
183 RepositoryMapping repositoryMapping
= provider
.getData().getRepositoryMapping(project
);
185 if (selection2
.length
== 1 && hintShowDiffNow
) {
186 ObjectId
[] parentIds
= ((GitFileRevision
)selection2
[0]).getCommit().getParentIds();
187 if (parentIds
.length
> 0) {
188 ObjectId parentId
= parentIds
[0];
189 Commit parent
= repositoryMapping
.getRepository().mapCommit(parentId
);
190 IFileRevision previous
= new GitFileRevision(parent
,
191 ((GitFileRevision
)selection2
[0]).getResource(),
192 ((GitFileRevision
)selection2
[0]).getCount()+1);
193 compareActionPrev
.setCurrentFileRevision(null);
194 compareActionPrev
.selectionChanged(new StructuredSelection(new IFileRevision
[] {selection2
[0], previous
}));
195 System
.out
.println("detail="+e
.detail
);
196 tree
.getDisplay().asyncExec(new Runnable() {
198 if (GitCompareRevisionAction
.findReusableCompareEditor(GitHistoryPage
.this.getSite().getPage()) != null)
199 compareActionPrev
.run();
204 compareActionPrev
.setCurrentFileRevision(null);
205 compareActionPrev
.selectionChanged(new StructuredSelection(new IFileRevision
[0]));
207 } catch (IOException e1
) {
208 // TODO Auto-generated catch block
209 e1
.printStackTrace();
211 hintShowDiffNow
= false;
214 compareAction
.setPage(this);
215 compareActionPrev
.setPage(this);
216 MenuManager menuMgr
= new MenuManager();
217 Menu menu
= menuMgr
.createContextMenu(tree
);
218 menuMgr
.addMenuListener(new IMenuListener() {
219 public void menuAboutToShow(IMenuManager menuMgr
) {
220 menuMgr
.add(compareAction
);
221 menuMgr
.add(compareActionPrev
);
224 menuMgr
.setRemoveAllWhenShown(true);
227 GitHistoryResourceListener resourceListener
= new GitHistoryResourceListener();
228 ResourcesPlugin
.getWorkspace().addResourceChangeListener(
229 resourceListener
, IResourceChangeEvent
.POST_CHANGE
);
233 class GitHistoryResourceListener
implements IResourceChangeListener
{
235 public void resourceChanged(IResourceChangeEvent event
) {
236 System
.out
.println("resourceChanged(" + event
+ ")");
241 class GitHistoryLabelProvider
extends LabelProvider
implements
242 ITableLabelProvider
{
244 public Image
getColumnImage(Object element
, int columnIndex
) {
248 public String
getColumnText(Object element
, int columnIndex
) {
249 if (columnIndex
== 0) {
250 int count
= ((GitFileRevision
) element
).getCount();
257 return "HEAD~"+count
;
260 if (columnIndex
== 1) {
261 String rss
= ((IFileRevision
) element
).getURI().toString();
262 String rs
= rss
.substring(rss
.length()-10);
263 String id
= ((IFileRevision
) element
).getContentIdentifier();
264 if (appliedPatches
!=null) {
265 if (!id
.equals("Workspace")) {
266 StGitPatch patch
= (StGitPatch
) appliedPatches
.get(new ObjectId(id
));
268 return patch
.getName();
272 if (id
.length() > 9) // make sure "Workspace" is spelled out
273 return id
.substring(0, 7) + "..";
277 return id
+ "@.." + rs
;
280 if (columnIndex
== 2) {
281 String id
= ((IFileRevision
)element
).getContentIdentifier();
282 ObjectId oid
= new ObjectId(id
);
283 StringBuilder b
=new StringBuilder();
285 Tag
[] matching
= tags
.get(oid
);
286 if (matching
!= null) {
287 for (Tag t
: matching
) {
290 b
.append(t
.getTag());
294 if (branches
!= null) {
297 String
[] matching
= branches
.get(oid
);
298 if (matching
!= null) {
299 for (String t
: matching
) {
309 if (columnIndex
== 3) {
310 Date d
= new Date(((IFileRevision
) element
).getTimestamp());
311 if (d
.getTime() == -1)
315 if (columnIndex
== 4)
316 return ((IFileRevision
) element
).getAuthor();
318 if (columnIndex
== 5) {
319 String comment
= ((IFileRevision
) element
).getComment();
322 int p
= comment
.indexOf('\n');
324 return comment
.substring(0, p
);
328 return Integer
.toString(columnIndex
);
332 private void createTree(Composite composite
) {
333 tree
= new Tree(composite
, SWT
.H_SCROLL
| SWT
.V_SCROLL
| SWT
.MULTI
334 | SWT
.FULL_SELECTION
| SWT
.VIRTUAL
);
335 tree
.setHeaderVisible(true);
336 tree
.setLinesVisible(true);
337 GridData data
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
338 tree
.setLayoutData(data
);
339 tree
.setData("HEAD");
340 tree
.addListener(SWT
.SetData
, new Listener() {
341 public void handleEvent(Event event
) {
343 TreeItem item
= (TreeItem
) event
.item
;
344 Tree parent
= item
.getParent();
345 if (parent
== null) {
346 item
.setText(new String
[] { "hej", "san" });
349 ITableLabelProvider p
= (ITableLabelProvider
) viewer
351 for (int i
= 0; i
< 6; ++i
) {
352 String text
= p
.getColumnText(
353 fileRevisions
[event
.index
], i
);
355 item
.setText(i
, text
);
359 item
.setData(fileRevisions
[event
.index
]);
361 } catch (Throwable b
) {
366 TableLayout layout
= new TableLayout();
367 tree
.setLayout(layout
);
369 viewer
= new TreeViewer(tree
, SWT
.VIRTUAL
| SWT
.FULL_SELECTION
);
371 viewer
.setUseHashlookup(true);
375 viewer
.setLabelProvider(new GitHistoryLabelProvider());
377 viewer
.setContentProvider(new GitHistoryContentProvider());
379 viewer
.setInput(getInput());
382 private Map appliedPatches
;
383 private Map
<ObjectId
,Tag
[]> tags
;
384 private Map
<ObjectId
, String
[]> branches
;
386 class HistoryRefreshJob
extends Job
{
388 public HistoryRefreshJob(String name
) {
392 protected IStatus
run(IProgressMonitor monitor
) {
393 monitor
= new NullProgressMonitor();
394 monitor
.beginTask("UpdateHistory", IProgressMonitor
.UNKNOWN
);
395 IProject project
= ((IResource
) getInput()).getProject();
396 RepositoryProvider provider
= RepositoryProvider
397 .getProvider(project
);
398 RepositoryMapping repositoryMapping
= ((GitProvider
)provider
).getData().getRepositoryMapping(project
);
399 Map newappliedPatches
= null;
401 newappliedPatches
= repositoryMapping
.getRepository().getAppliedPatches();
402 } catch (IOException e
) {
403 // TODO Auto-generated catch block
406 Map
<ObjectId
,Tag
[]> newtags
= new HashMap
<ObjectId
,Tag
[]>();
408 for (String name
: repositoryMapping
.getRepository().getTags()) {
409 Tag t
= repositoryMapping
.getRepository().mapTag(name
);
410 Tag
[] samecommit
= newtags
.get(t
.getObjId());
411 if (samecommit
==null) {
412 samecommit
= new Tag
[] { t
};
414 Tag
[] n
=new Tag
[samecommit
.length
+1];
415 for (int j
=0; j
<samecommit
.length
; ++j
)
416 n
[j
] = samecommit
[j
];
420 newtags
.put(t
.getObjId(), samecommit
);
422 } catch (IOException e
) {
423 // TODO Auto-generated catch block
426 Map
<ObjectId
, String
[]> newBranches
= new HashMap
<ObjectId
, String
[]>();
428 for (String branch
: repositoryMapping
.getRepository().getBranches()) {
429 ObjectId id
= repositoryMapping
.getRepository().resolve("refs/heads/"+branch
);
430 String
[] samecommit
= newBranches
.get(id
);
431 if (samecommit
== null) {
432 samecommit
= new String
[] { branch
};
434 String
[] n
=new String
[samecommit
.length
+ 1];
435 for (int j
=0; j
<samecommit
.length
; ++j
)
436 n
[j
] = samecommit
[j
];
437 n
[n
.length
-1] = branch
;
440 newBranches
.put(id
, samecommit
);
442 branches
= newBranches
;
443 } catch (IOException e
) {
444 // TODO Auto-generated catch block
448 IFileHistoryProvider fileHistoryProvider
= provider
449 .getFileHistoryProvider();
450 IFileHistory fileHistoryFor
= fileHistoryProvider
451 .getFileHistoryFor((IResource
) getInput(),
452 IFileHistoryProvider
.SINGLE_LINE_OF_DESCENT
, monitor
);
453 fileRevisions
= fileHistoryFor
.getFileRevisions();
455 final Map fnewappliedPatches
= newappliedPatches
;
456 final Map
<ObjectId
,Tag
[]> ftags
= newtags
;
458 tree
.getDisplay().asyncExec(new Runnable() {
462 tree
.setItemCount(fileRevisions
.length
);
463 tree
.setData(fileRevisions
);
464 tree
.setLayoutData(new GridData(SWT
.FILL
,SWT
.FILL
,true,true));
465 System
.out
.println("inputchanged, invoking refresh");
466 appliedPatches
= fnewappliedPatches
;
469 done(Status
.OK_STATUS
);
473 return Status
.OK_STATUS
;
478 HistoryRefreshJob historyRefreshJob
= new HistoryRefreshJob("Git history refresh");
480 class GitHistoryContentProvider
implements ITreeContentProvider
,
481 ILazyTreeContentProvider
{
483 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
484 if (newInput
== null)
486 System
.out
.println(new Date()+"inputChanged(" + viewer
+ "," + oldInput
+ ","
488 if (historyRefreshJob
.cancel()) {
489 System
.out
.println("rescheduling");
490 historyRefreshJob
.schedule();
492 System
.out
.println("failed to cancel?");
496 public void dispose() {
499 public Object
[] getElements(Object inputElement
) {
500 System
.out
.println("getElements(" + inputElement
+ ")");
501 return fileRevisions
;
504 public boolean hasChildren(Object element
) {
505 // TODO Auto-generated method stub
509 public Object
getParent(Object element
) {
510 // System.out.println("getParent(" + element + ")");
514 public Object
[] getChildren(Object parentElement
) {
515 // System.out.println("getElements(" + parentElement + ")");
516 return fileRevisions
;
519 public void updateChildCount(Object element
, int currentChildCount
) {
520 viewer
.setChildCount(element
, fileRevisions
!=null ? fileRevisions
.length
: 0);
523 public void updateElement(Object parent
, int index
) {
524 System
.out
.println("updateElement("+parent
+","+index
);
525 viewer
.replace(parent
, index
, fileRevisions
!=null ? fileRevisions
[index
] : null);
529 private void createColumns() {
530 // X SelectionListener headerListener = getColumnListener(viewer);
532 TreeColumn col
= new TreeColumn(tree
, SWT
.NONE
);
533 col
.setResizable(true);
535 // X col.addSelectionListener(headerListener);
536 ((TableLayout
) tree
.getLayout()).addColumnData(new ColumnWeightData(15,
540 col
= new TreeColumn(tree
, SWT
.NONE
);
541 col
.setResizable(true);
542 col
.setText(TeamUIMessages
.GenericHistoryTableProvider_Revision
);
543 // X col.addSelectionListener(headerListener);
544 ((TableLayout
) tree
.getLayout()).addColumnData(new ColumnWeightData(15,
548 col
= new TreeColumn(tree
, SWT
.NONE
);
549 col
.setResizable(true);
551 // X col.addSelectionListener(headerListener);
552 ((TableLayout
) tree
.getLayout()).addColumnData(new ColumnWeightData(15,
555 col
= new TreeColumn(tree
, SWT
.NONE
);
556 col
.setResizable(true);
557 col
.setText(TeamUIMessages
.GenericHistoryTableProvider_RevisionTime
);
558 // X col.addSelectionListener(headerListener);
559 ((TableLayout
) tree
.getLayout()).addColumnData(new ColumnWeightData(30,
563 col
= new TreeColumn(tree
, SWT
.NONE
);
564 col
.setResizable(true);
565 col
.setText(TeamUIMessages
.GenericHistoryTableProvider_Author
);
566 // X col.addSelectionListener(headerListener);
567 ((TableLayout
) tree
.getLayout()).addColumnData(new ColumnWeightData(20,
571 col
= new TreeColumn(tree
, SWT
.NONE
);
572 col
.setResizable(true);
573 col
.setText(TeamUIMessages
.GenericHistoryTableProvider_Comment
);
574 // X col.addSelectionListener(headerListener);
575 ((TableLayout
) tree
.getLayout()).addColumnData(new ColumnWeightData(35,
579 public Control
getControl() {
580 return localComposite
;
583 public void setFocus() {
584 localComposite
.setFocus();
587 public String
getDescription() {
588 return "GIT History viewer";
591 public String
getName() {
592 return getInput().toString();
595 public boolean isValidInput(Object object
) {
596 // TODO Auto-generated method stub
600 public void refresh() {
601 // TODO Auto-generated method stub
605 public Object
getAdapter(Class adapter
) {
606 if (adapter
== IHistoryCompareAdapter
.class) {
612 public ICompareInput
getCompareInput(Object object
) {
613 // TODO Auto-generated method stub
617 public void prepareInput(ICompareInput input
,
618 CompareConfiguration configuration
, IProgressMonitor monitor
) {
619 System
.out
.println("prepareInput()");
620 // TODO Auto-generated method stub