Use text resources for the Commit dialog and add shortcuts.
[egit/imyousuf.git] / org.spearce.egit.ui / src / org / spearce / egit / ui / internal / dialogs / CommitDialog.java
blobfede9489588f3275c4daaf620f91ad1b18361b76
1 /*******************************************************************************
2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com.dewire.com>
4 * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com>
5 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
6 * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v1.0
10 * See LICENSE for the full license text, also available.
11 *******************************************************************************/
12 package org.spearce.egit.ui.internal.dialogs;
14 import java.io.File;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.Iterator;
21 import org.eclipse.core.resources.IFile;
22 import org.eclipse.core.resources.IProject;
23 import org.eclipse.jface.dialogs.Dialog;
24 import org.eclipse.jface.dialogs.IDialogConstants;
25 import org.eclipse.jface.dialogs.MessageDialog;
26 import org.eclipse.jface.layout.GridDataFactory;
27 import org.eclipse.jface.viewers.CheckboxTableViewer;
28 import org.eclipse.jface.viewers.IStructuredContentProvider;
29 import org.eclipse.jface.viewers.IStructuredSelection;
30 import org.eclipse.jface.viewers.ITableLabelProvider;
31 import org.eclipse.jface.viewers.Viewer;
32 import org.eclipse.jface.viewers.ViewerComparator;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.events.KeyAdapter;
35 import org.eclipse.swt.events.KeyEvent;
36 import org.eclipse.swt.events.SelectionAdapter;
37 import org.eclipse.swt.events.SelectionEvent;
38 import org.eclipse.swt.events.SelectionListener;
39 import org.eclipse.swt.graphics.Image;
40 import org.eclipse.swt.layout.GridLayout;
41 import org.eclipse.swt.widgets.Button;
42 import org.eclipse.swt.widgets.Composite;
43 import org.eclipse.swt.widgets.Control;
44 import org.eclipse.swt.widgets.Event;
45 import org.eclipse.swt.widgets.Label;
46 import org.eclipse.swt.widgets.Listener;
47 import org.eclipse.swt.widgets.Menu;
48 import org.eclipse.swt.widgets.MenuItem;
49 import org.eclipse.swt.widgets.Shell;
50 import org.eclipse.swt.widgets.Table;
51 import org.eclipse.swt.widgets.TableColumn;
52 import org.eclipse.swt.widgets.Text;
53 import org.eclipse.ui.model.WorkbenchLabelProvider;
54 import org.spearce.egit.core.project.RepositoryMapping;
55 import org.spearce.egit.ui.UIText;
56 import org.spearce.jgit.lib.Constants;
57 import org.spearce.jgit.lib.GitIndex;
58 import org.spearce.jgit.lib.PersonIdent;
59 import org.spearce.jgit.lib.Repository;
60 import org.spearce.jgit.lib.Tree;
61 import org.spearce.jgit.lib.TreeEntry;
62 import org.spearce.jgit.lib.GitIndex.Entry;
64 /**
65 * Dialog is shown to user when they request to commit files. Changes in the
66 * selected portion of the tree are shown.
68 public class CommitDialog extends Dialog {
70 class CommitContentProvider implements IStructuredContentProvider {
72 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
73 // Empty
76 public void dispose() {
77 // Empty
80 public Object[] getElements(Object inputElement) {
81 return items.toArray();
86 class CommitLabelProvider extends WorkbenchLabelProvider implements
87 ITableLabelProvider {
88 public String getColumnText(Object obj, int columnIndex) {
89 CommitItem item = (CommitItem) obj;
91 switch (columnIndex) {
92 case 0:
93 return item.status;
95 case 1:
96 return item.file.getProject().getName() + ": " //$NON-NLS-1$
97 + item.file.getProjectRelativePath();
99 default:
100 return null;
104 public Image getColumnImage(Object element, int columnIndex) {
105 if (columnIndex == 0)
106 return getImage(element);
107 return null;
111 ArrayList<CommitItem> items = new ArrayList<CommitItem>();
114 * @param parentShell
116 public CommitDialog(Shell parentShell) {
117 super(parentShell);
120 @Override
121 protected void createButtonsForButtonBar(Composite parent) {
122 createButton(parent, IDialogConstants.SELECT_ALL_ID, UIText.CommitDialog_SelectAll, false);
123 createButton(parent, IDialogConstants.DESELECT_ALL_ID, UIText.CommitDialog_DeselectAll, false);
125 createButton(parent, IDialogConstants.OK_ID, UIText.CommitDialog_Commit, true);
126 createButton(parent, IDialogConstants.CANCEL_ID,
127 IDialogConstants.CANCEL_LABEL, false);
130 Text commitText;
131 Text authorText;
132 Button amendingButton;
133 Button signedOffButton;
135 CheckboxTableViewer filesViewer;
137 @Override
138 protected Control createDialogArea(Composite parent) {
139 Composite container = (Composite) super.createDialogArea(parent);
140 parent.getShell().setText(UIText.CommitDialog_CommitChanges);
142 GridLayout layout = new GridLayout(2, false);
143 container.setLayout(layout);
145 Label label = new Label(container, SWT.LEFT);
146 label.setText(UIText.CommitDialog_CommitMessage);
147 label.setLayoutData(GridDataFactory.fillDefaults().span(2, 1).grab(true, false).create());
149 commitText = new Text(container, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
150 commitText.setLayoutData(GridDataFactory.fillDefaults().span(2, 1).grab(true, true)
151 .hint(600, 200).create());
153 // allow to commit with ctrl-enter
154 commitText.addKeyListener(new KeyAdapter() {
155 public void keyPressed(KeyEvent arg0) {
156 if (arg0.keyCode == SWT.CR
157 && (arg0.stateMask & SWT.CONTROL) > 0) {
158 okPressed();
159 } else if (arg0.keyCode == SWT.TAB
160 && (arg0.stateMask & SWT.SHIFT) == 0) {
161 arg0.doit = false;
162 commitText.traverse(SWT.TRAVERSE_TAB_NEXT);
167 new Label(container, SWT.LEFT).setText(UIText.CommitDialog_Author);
168 authorText = new Text(container, SWT.BORDER);
169 authorText.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
170 if (author != null)
171 authorText.setText(author);
173 amendingButton = new Button(container, SWT.CHECK);
174 if (amending) {
175 amendingButton.setSelection(amending);
176 amendingButton.setEnabled(false); // if already set, don't allow any changes
177 commitText.setText(previousCommitMessage);
178 } else if (!amendAllowed) {
179 amendingButton.setEnabled(false);
181 amendingButton.addSelectionListener(new SelectionListener() {
182 boolean alreadyAdded = false;
183 public void widgetSelected(SelectionEvent arg0) {
184 if (alreadyAdded)
185 return;
186 if (amendingButton.getSelection()) {
187 alreadyAdded = true;
188 String curText = commitText.getText();
189 if (curText.length() > 0)
190 curText += "\n"; //$NON-NLS-1$
191 commitText.setText(curText + previousCommitMessage);
195 public void widgetDefaultSelected(SelectionEvent arg0) {
196 // Empty
200 amendingButton.setText(UIText.CommitDialog_AmendPreviousCommit);
201 amendingButton.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).span(2, 1).create());
203 signedOffButton = new Button(container, SWT.CHECK);
204 signedOffButton.setSelection(signedOff);
205 signedOffButton.setText(UIText.CommitDialog_AddSOB);
206 signedOffButton.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).span(2, 1).create());
208 Table resourcesTable = new Table(container, SWT.H_SCROLL | SWT.V_SCROLL
209 | SWT.FULL_SELECTION | SWT.MULTI | SWT.CHECK | SWT.BORDER);
210 resourcesTable.setLayoutData(GridDataFactory.fillDefaults().hint(600,
211 200).span(2,1).grab(true, true).create());
213 resourcesTable.setHeaderVisible(true);
214 TableColumn statCol = new TableColumn(resourcesTable, SWT.LEFT);
215 statCol.setText(UIText.CommitDialog_Status);
216 statCol.setWidth(150);
217 statCol.addSelectionListener(new HeaderSelectionListener(CommitItem.Order.ByStatus));
219 TableColumn resourceCol = new TableColumn(resourcesTable, SWT.LEFT);
220 resourceCol.setText(UIText.CommitDialog_File);
221 resourceCol.setWidth(415);
222 resourceCol.addSelectionListener(new HeaderSelectionListener(CommitItem.Order.ByFile));
224 filesViewer = new CheckboxTableViewer(resourcesTable);
225 filesViewer.setContentProvider(new CommitContentProvider());
226 filesViewer.setLabelProvider(new CommitLabelProvider());
227 filesViewer.setInput(items);
228 filesViewer.setAllChecked(true);
229 filesViewer.getTable().setMenu(getContextMenu());
231 container.pack();
232 return container;
235 private Menu getContextMenu() {
236 Menu menu = new Menu(filesViewer.getTable());
237 MenuItem item = new MenuItem(menu, SWT.PUSH);
238 item.setText(UIText.CommitDialog_AddFileOnDiskToIndex);
239 item.addListener(SWT.Selection, new Listener() {
240 public void handleEvent(Event arg0) {
241 IStructuredSelection sel = (IStructuredSelection) filesViewer.getSelection();
242 if (sel.isEmpty()) {
243 return;
245 try {
246 ArrayList<GitIndex> changedIndexes = new ArrayList<GitIndex>();
247 for (Iterator<Object> it = sel.iterator(); it.hasNext();) {
248 CommitItem commitItem = (CommitItem) it.next();
250 IProject project = commitItem.file.getProject();
251 RepositoryMapping map = RepositoryMapping.getMapping(project);
253 Repository repo = map.getRepository();
254 GitIndex index = null;
255 index = repo.getIndex();
256 Entry entry = index.getEntry(map.getRepoRelativePath(commitItem.file));
257 if (entry != null && entry.isModified(map.getWorkDir())) {
258 entry.update(new File(map.getWorkDir(), entry.getName()));
259 if (!changedIndexes.contains(index))
260 changedIndexes.add(index);
263 if (!changedIndexes.isEmpty()) {
264 for (GitIndex idx : changedIndexes) {
265 idx.write();
267 filesViewer.refresh(true);
269 } catch (IOException e) {
270 e.printStackTrace();
271 return;
276 return menu;
279 private static String getFileStatus(IFile file) {
280 String prefix = UIText.CommitDialog_StatusUnknown;
282 try {
283 RepositoryMapping repositoryMapping = RepositoryMapping
284 .getMapping(file.getProject());
286 Repository repo = repositoryMapping.getRepository();
287 GitIndex index = repo.getIndex();
288 Tree headTree = repo.mapTree(Constants.HEAD);
290 String repoPath = repositoryMapping.getRepoRelativePath(file);
291 TreeEntry headEntry = headTree.findBlobMember(repoPath);
292 boolean headExists = headTree.existsBlob(repoPath);
294 Entry indexEntry = index.getEntry(repoPath);
295 if (headEntry == null) {
296 prefix = UIText.CommitDialog_StatusAdded;
297 if (indexEntry.isModified(repositoryMapping.getWorkDir()))
298 prefix = UIText.CommitDialog_StatusAddedIndexDiff;
299 } else if (indexEntry == null) {
300 prefix = UIText.CommitDialog_StatusRemoved;
301 } else if (headExists
302 && !headEntry.getId().equals(indexEntry.getObjectId())) {
303 prefix = UIText.CommitDialog_StatusModified;
305 if (indexEntry.isModified(repositoryMapping.getWorkDir()))
306 prefix = UIText.CommitDialog_StatusModifiedIndexDiff;
307 } else if (!new File(repositoryMapping.getWorkDir(), indexEntry
308 .getName()).isFile()) {
309 prefix = UIText.CommitDialog_StatusRemovedNotStaged;
310 } else if (indexEntry.isModified(repositoryMapping.getWorkDir())) {
311 prefix = UIText.CommitDialog_StatusModifiedNotStaged;
314 } catch (Exception e) {
317 return prefix;
321 * @return The message the user entered
323 public String getCommitMessage() {
324 return commitMessage;
328 * Preset a commit message. This might be for amending a commit.
329 * @param s the commit message
331 public void setCommitMessage(String s) {
332 this.commitMessage = s;
335 private String commitMessage = ""; //$NON-NLS-1$
336 private String author = null;
337 private boolean signedOff = false;
338 private boolean amending = false;
339 private boolean amendAllowed = true;
341 private ArrayList<IFile> selectedFiles = new ArrayList<IFile>();
342 private String previousCommitMessage = ""; //$NON-NLS-1$
345 * Pre-select suggested set of resources to commit
347 * @param items
349 public void setSelectedFiles(IFile[] items) {
350 Collections.addAll(selectedFiles, items);
354 * @return the resources selected by the user to commit.
356 public IFile[] getSelectedFiles() {
357 return selectedFiles.toArray(new IFile[0]);
360 class HeaderSelectionListener extends SelectionAdapter {
362 private CommitItem.Order order;
364 private boolean reversed;
366 public HeaderSelectionListener(CommitItem.Order order) {
367 this.order = order;
370 @Override
371 public void widgetSelected(SelectionEvent e) {
372 TableColumn column = (TableColumn)e.widget;
373 Table table = column.getParent();
375 if (column == table.getSortColumn()) {
376 reversed = !reversed;
377 } else {
378 reversed = false;
380 table.setSortColumn(column);
382 Comparator<CommitItem> comparator;
383 if (reversed) {
384 comparator = order.descending();
385 table.setSortDirection(SWT.DOWN);
386 } else {
387 comparator = order;
388 table.setSortDirection(SWT.UP);
391 filesViewer.setComparator(new CommitViewerComparator(comparator));
396 @Override
397 protected void okPressed() {
398 commitMessage = commitText.getText();
399 author = authorText.getText().trim();
400 signedOff = signedOffButton.getSelection();
401 amending = amendingButton.getSelection();
403 Object[] checkedElements = filesViewer.getCheckedElements();
404 selectedFiles.clear();
405 for (Object obj : checkedElements)
406 selectedFiles.add(((CommitItem) obj).file);
408 if (commitMessage.trim().length() == 0) {
409 MessageDialog.openWarning(getShell(), UIText.CommitDialog_ErrorNoMessage, UIText.CommitDialog_ErrorMustEnterCommitMessage);
410 return;
413 if (author.length() > 0) {
414 try {
415 new PersonIdent(author);
416 } catch (IllegalArgumentException e) {
417 MessageDialog.openWarning(getShell(), UIText.CommitDialog_ErrorInvalidAuthor, UIText.CommitDialog_ErrorInvalidAuthorSpecified);
418 return;
420 } else author = null;
422 if (selectedFiles.isEmpty() && !amending) {
423 MessageDialog.openWarning(getShell(), UIText.CommitDialog_ErrorNoItemsSelected, UIText.CommitDialog_ErrorNoItemsSelectedToBeCommitted);
424 return;
426 super.okPressed();
430 * Set the total list of changed resources, including additions and
431 * removals
433 * @param files potentially affected by a new commit
435 public void setFileList(ArrayList<IFile> files) {
436 items.clear();
437 for (IFile file : files) {
438 CommitItem item = new CommitItem();
439 item.status = getFileStatus(file);
440 item.file = file;
441 items.add(item);
445 @Override
446 protected void buttonPressed(int buttonId) {
447 if (IDialogConstants.SELECT_ALL_ID == buttonId) {
448 filesViewer.setAllChecked(true);
450 if (IDialogConstants.DESELECT_ALL_ID == buttonId) {
451 filesViewer.setAllChecked(false);
453 super.buttonPressed(buttonId);
457 * @return The author to set for the commit
459 public String getAuthor() {
460 return author;
464 * Pre-set author for the commit
466 * @param author
468 public void setAuthor(String author) {
469 this.author = author;
473 * @return whether to auto-add a signed-off line to the message
475 public boolean isSignedOff() {
476 return signedOff;
480 * Pre-set whether a signed-off line should be included in the commit
481 * message.
483 * @param signedOff
485 public void setSignedOff(boolean signedOff) {
486 this.signedOff = signedOff;
490 * @return whether the last commit is to be amended
492 public boolean isAmending() {
493 return amending;
497 * Pre-set whether the last commit is going to be amended
499 * @param amending
501 public void setAmending(boolean amending) {
502 this.amending = amending;
506 * Set the message from the previous commit for amending.
508 * @param string
510 public void setPreviousCommitMessage(String string) {
511 this.previousCommitMessage = string;
515 * Set whether the previous commit may be amended
517 * @param amendAllowed
519 public void setAmendAllowed(boolean amendAllowed) {
520 this.amendAllowed = amendAllowed;
523 @Override
524 protected int getShellStyle() {
525 return super.getShellStyle() | SWT.RESIZE;
529 class CommitItem {
530 String status;
532 IFile file;
534 public static enum Order implements Comparator<CommitItem> {
535 ByStatus() {
537 public int compare(CommitItem o1, CommitItem o2) {
538 return o1.status.compareTo(o2.status);
543 ByFile() {
545 public int compare(CommitItem o1, CommitItem o2) {
546 return o1.file.getProjectRelativePath().toString().
547 compareTo(o2.file.getProjectRelativePath().toString());
552 public Comparator<CommitItem> ascending() {
553 return this;
556 public Comparator<CommitItem> descending() {
557 return Collections.reverseOrder(this);
562 class CommitViewerComparator extends ViewerComparator {
564 public CommitViewerComparator(Comparator comparator){
565 super(comparator);
568 @SuppressWarnings("unchecked")
569 @Override
570 public int compare(Viewer viewer, Object e1, Object e2) {
571 return getComparator().compare(e1, e2);