Show tags in history view
[egit.git] / org.spearce.egit.ui / src / org / spearce / egit / ui / GitHistoryPage.java
blob8850405e7c97d8f8f9eeb8d20953a042ebd5c013
1 /*
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;
22 import java.util.Map;
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;
92 private Tree tree;
94 private IFileRevision[] fileRevisions;
96 protected boolean hintShowDiffNow;
98 public GitHistoryPage(Object object) {
99 setInput(object);
102 public boolean inputSet() {
103 if (viewer != null)
104 viewer.setInput(getInput());
105 // TODO Auto-generated method stub
106 return true;
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(
126 "Compare");
127 final GitCompareRevisionAction compareActionPrev = new GitCompareRevisionAction(
128 "Show commit");
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() {
144 TreeItem lastItem;
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));
154 if (patch!=null)
155 commitStr = "Patch: "+patch.getName();
156 } else {
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());
164 lastItem = item;
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(
179 selection2));
180 IProject project = ((IResource) getInput()).getProject();
181 GitProvider provider = (GitProvider)RepositoryProvider
182 .getProvider(project);
183 RepositoryMapping repositoryMapping = provider.getData().getRepositoryMapping(project);
184 try {
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() {
197 public void run() {
198 if (GitCompareRevisionAction.findReusableCompareEditor(GitHistoryPage.this.getSite().getPage()) != null)
199 compareActionPrev.run();
203 } else {
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);
225 tree.setMenu(menu);
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) {
245 return null;
248 public String getColumnText(Object element, int columnIndex) {
249 if (columnIndex == 0) {
250 int count = ((GitFileRevision) element).getCount();
251 if (count < 0)
252 return "";
253 else
254 if (count == 0)
255 return "HEAD";
256 else
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));
267 if (patch!=null)
268 return patch.getName();
271 if (id != null)
272 if (id.length() > 9) // make sure "Workspace" is spelled out
273 return id.substring(0, 7) + "..";
274 else
275 return id;
276 else
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();
284 if (tags != null) {
285 Tag[] matching = tags.get(oid);
286 if (matching != null) {
287 for (Tag t : matching) {
288 if (b.length() > 0)
289 b.append(' ');
290 b.append(t.getTag());
294 if (branches != null) {
295 if (b.length() >0)
296 b.append('\n');
297 String[] matching = branches.get(oid);
298 if (matching != null) {
299 for (String t : matching) {
300 if (b.length() > 0)
301 b.append(' ');
302 b.append(t);
306 return b.toString();
309 if (columnIndex == 3) {
310 Date d = new Date(((IFileRevision) element).getTimestamp());
311 if (d.getTime() == -1)
312 return "";
313 return d.toString();
315 if (columnIndex == 4)
316 return ((IFileRevision) element).getAuthor();
318 if (columnIndex == 5) {
319 String comment = ((IFileRevision) element).getComment();
320 if (comment == null)
321 return null;
322 int p = comment.indexOf('\n');
323 if (p >= 0)
324 return comment.substring(0, p);
325 else
326 return comment;
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) {
342 try {
343 TreeItem item = (TreeItem) event.item;
344 Tree parent = item.getParent();
345 if (parent == null) {
346 item.setText(new String[] { "hej", "san" });
347 item.setData("");
348 } else {
349 ITableLabelProvider p = (ITableLabelProvider) viewer
350 .getLabelProvider();
351 for (int i = 0; i < 6; ++i) {
352 String text = p.getColumnText(
353 fileRevisions[event.index], i);
354 if (text != null)
355 item.setText(i, text);
356 else
357 item.setText(i, "");
359 item.setData(fileRevisions[event.index]);
361 } catch (Throwable b) {
362 b.printStackTrace();
366 TableLayout layout = new TableLayout();
367 tree.setLayout(layout);
369 viewer = new TreeViewer(tree, SWT.VIRTUAL | SWT.FULL_SELECTION);
371 viewer.setUseHashlookup(true);
373 createColumns();
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) {
389 super(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;
400 try {
401 newappliedPatches = repositoryMapping.getRepository().getAppliedPatches();
402 } catch (IOException e) {
403 // TODO Auto-generated catch block
404 e.printStackTrace();
406 Map<ObjectId,Tag[]> newtags = new HashMap<ObjectId,Tag[]>();
407 try {
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 };
413 } else {
414 Tag[] n=new Tag[samecommit.length+1];
415 for (int j=0; j<samecommit.length; ++j)
416 n[j] = samecommit[j];
417 n[n.length-1] = t;
418 samecommit = n;
420 newtags.put(t.getObjId(), samecommit);
422 } catch (IOException e) {
423 // TODO Auto-generated catch block
424 e.printStackTrace();
426 Map<ObjectId, String[]> newBranches = new HashMap<ObjectId, String[]>();
427 try {
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 };
433 } else {
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;
438 samecommit = n;
440 newBranches.put(id, samecommit);
442 branches = newBranches;
443 } catch (IOException e) {
444 // TODO Auto-generated catch block
445 e.printStackTrace();
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() {
460 public void run() {
461 tree.removeAll();
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;
467 tags = ftags;
468 viewer.refresh();
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)
485 return;
486 System.out.println(new Date()+"inputChanged(" + viewer + "," + oldInput + ","
487 + newInput);
488 if (historyRefreshJob.cancel()) {
489 System.out.println("rescheduling");
490 historyRefreshJob.schedule();
491 } else {
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
506 return false;
509 public Object getParent(Object element) {
510 // System.out.println("getParent(" + element + ")");
511 return null;
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);
531 // count from head
532 TreeColumn col = new TreeColumn(tree, SWT.NONE);
533 col.setResizable(true);
534 col.setText("^");
535 // X col.addSelectionListener(headerListener);
536 ((TableLayout) tree.getLayout()).addColumnData(new ColumnWeightData(15,
537 true));
539 // revision
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,
545 true));
547 // tags
548 col = new TreeColumn(tree, SWT.NONE);
549 col.setResizable(true);
550 col.setText("Tags");
551 // X col.addSelectionListener(headerListener);
552 ((TableLayout) tree.getLayout()).addColumnData(new ColumnWeightData(15,
553 true));
554 // creation date
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,
560 true));
562 // author
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,
568 true));
570 // comment
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,
576 true));
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
597 return true;
600 public void refresh() {
601 // TODO Auto-generated method stub
605 public Object getAdapter(Class adapter) {
606 if (adapter == IHistoryCompareAdapter.class) {
607 return this;
609 return null;
612 public ICompareInput getCompareInput(Object object) {
613 // TODO Auto-generated method stub
614 return null;
617 public void prepareInput(ICompareInput input,
618 CompareConfiguration configuration, IProgressMonitor monitor) {
619 System.out.println("prepareInput()");
620 // TODO Auto-generated method stub