IDEA-27603 (Indicate which branch is being worked on)
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / ui / CommitChangeListDialog.java
blob2164d5b938dcf302182dc000bab21ff679ce3276
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.openapi.vcs.changes.ui;
18 import com.intellij.ide.util.PropertiesComponent;
19 import com.intellij.openapi.Disposable;
20 import com.intellij.openapi.extensions.Extensions;
21 import com.intellij.openapi.actionSystem.AnAction;
22 import com.intellij.openapi.actionSystem.DataKey;
23 import com.intellij.openapi.actionSystem.DataSink;
24 import com.intellij.openapi.actionSystem.TypeSafeDataProvider;
25 import com.intellij.openapi.application.ModalityState;
26 import com.intellij.openapi.fileEditor.FileDocumentManager;
27 import com.intellij.openapi.progress.ProgressManager;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.ui.*;
30 import com.intellij.openapi.util.Comparing;
31 import com.intellij.openapi.util.Disposer;
32 import com.intellij.openapi.util.Ref;
33 import com.intellij.openapi.util.text.StringUtil;
34 import com.intellij.openapi.vcs.*;
35 import com.intellij.openapi.vcs.changes.*;
36 import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
37 import com.intellij.openapi.vcs.checkin.*;
38 import com.intellij.openapi.vcs.ui.CommitMessage;
39 import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.ui.IdeBorderFactory;
42 import com.intellij.ui.SeparatorFactory;
43 import com.intellij.util.Alarm;
44 import org.jetbrains.annotations.NonNls;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import javax.swing.*;
49 import java.awt.*;
50 import java.awt.event.ActionEvent;
51 import java.io.File;
52 import java.util.*;
53 import java.util.List;
55 /**
56 * @author max
58 public class CommitChangeListDialog extends DialogWrapper implements CheckinProjectPanel, TypeSafeDataProvider {
59 private final CommitMessage myCommitMessageArea;
60 private Splitter mySplitter;
61 private final JPanel myAdditionalOptionsPanel;
63 private final ChangesBrowser myBrowser;
64 private final ChangesBrowserExtender myBrowserExtender;
66 private CommitLegendPanel myLegend;
68 private final List<RefreshableOnComponent> myAdditionalComponents = new ArrayList<RefreshableOnComponent>();
69 private final List<CheckinHandler> myHandlers = new ArrayList<CheckinHandler>();
70 private final String myActionName;
71 private final Project myProject;
72 private final List<CommitExecutor> myExecutors;
73 private final Alarm myOKButtonUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
74 private String myLastKnownComment = "";
75 private final boolean myAllOfDefaultChangeListChangesIncluded;
76 @NonNls private static final String SPLITTER_PROPORTION_OPTION = "CommitChangeListDialog.SPLITTER_PROPORTION";
77 private final Action[] myExecutorActions;
78 private final boolean myShowVcsCommit;
79 private final Map<AbstractVcs, JPanel> myPerVcsOptionsPanels = new HashMap<AbstractVcs, JPanel>();
81 @Nullable
82 private final AbstractVcs myVcs;
83 private final boolean myIsAlien;
84 private boolean myDisposed = false;
85 private final JLabel myWarningLabel;
87 private final Map<String, CheckinChangeListSpecificComponent> myCheckinChangeListSpecificComponents;
89 private final Map<String, String> myListComments;
90 private String myLastSelectedListName;
91 private CommitLegendPanel.ChangeInfoCalculator myChangesInfoCalculator;
93 private static class MyUpdateButtonsRunnable implements Runnable {
94 private CommitChangeListDialog myDialog;
96 private MyUpdateButtonsRunnable(final CommitChangeListDialog dialog) {
97 myDialog = dialog;
100 public void cancel() {
101 myDialog = null;
104 public void run() {
105 if (myDialog != null) {
106 myDialog.updateButtons();
107 myDialog.updateLegend();
111 public void restart(final CommitChangeListDialog dialog) {
112 myDialog = dialog;
113 run();
117 private final MyUpdateButtonsRunnable myUpdateButtonsRunnable = new MyUpdateButtonsRunnable(this);
119 private static boolean commit(final Project project, final List<Change> changes, final LocalChangeList initialSelection,
120 final List<CommitExecutor> executors, final boolean showVcsCommit, final String comment) {
121 final ChangeListManager manager = ChangeListManager.getInstance(project);
122 final LocalChangeList defaultList = manager.getDefaultChangeList();
123 final ArrayList<LocalChangeList> changeLists = new ArrayList<LocalChangeList>(manager.getChangeListsCopy());
124 CommitChangeListDialog dialog =
125 new CommitChangeListDialog(project, changes, initialSelection, executors, showVcsCommit, defaultList, changeLists, null, false,
126 comment);
127 dialog.show();
128 return dialog.isOK();
131 public static void commitPaths(final Project project, Collection<FilePath> paths, final LocalChangeList initialSelection,
132 @Nullable final CommitExecutor executor, final String comment) {
133 final ChangeListManager manager = ChangeListManager.getInstance(project);
134 final Collection<Change> changes = new HashSet<Change>();
135 for (FilePath path : paths) {
136 changes.addAll(manager.getChangesIn(path));
139 commitChanges(project, changes, initialSelection, executor, comment);
142 public static boolean commitChanges(final Project project, final Collection<Change> changes, final LocalChangeList initialSelection,
143 final CommitExecutor executor, final String comment) {
144 if (executor == null) {
145 return commitChanges(project, changes, initialSelection, collectExecutors(project, changes), true, comment);
147 else {
148 return commitChanges(project, changes, initialSelection, Collections.singletonList(executor), false, comment);
152 public static List<CommitExecutor> collectExecutors(Project project, Collection<Change> changes) {
153 List<CommitExecutor> result = new ArrayList<CommitExecutor>();
154 final ChangeListManager manager = ChangeListManager.getInstance(project);
155 final List<AbstractVcs> vcses = getAffectedVcses(project, changes);
156 for (AbstractVcs vcs : vcses) {
157 result.addAll(vcs.getCommitExecutors());
159 result.addAll(manager.getRegisteredExecutors());
160 return result;
163 public static boolean commitChanges(final Project project, final Collection<Change> changes, final LocalChangeList initialSelection,
164 final List<CommitExecutor> executors, final boolean showVcsCommit, final String comment) {
165 if (changes.isEmpty()) {
166 Messages.showWarningDialog(project, VcsBundle.message("commit.dialog.no.changes.detected.text") ,
167 VcsBundle.message("commit.dialog.no.changes.detected.title"));
168 return false;
171 return commit(project, new ArrayList<Change>(changes), initialSelection, executors, showVcsCommit, comment);
174 public static void commitAlienChanges(final Project project, final List<Change> changes, final AbstractVcs vcs,
175 final String changelistName, final String comment) {
176 final LocalChangeList lcl = new AlienLocalChangeList(changes, changelistName);
177 new CommitChangeListDialog(project, changes, null, null, true, AlienLocalChangeList.DEFAULT_ALIEN, Collections.singletonList(lcl), vcs,
178 true, comment).show();
181 private CommitChangeListDialog(final Project project,
182 final List<Change> changes,
183 final LocalChangeList initialSelection,
184 final List<CommitExecutor> executors,
185 final boolean showVcsCommit, final LocalChangeList defaultChangeList,
186 final List<LocalChangeList> changeLists, final AbstractVcs singleVcs, final boolean isAlien,
187 final String comment) {
188 super(project, true);
189 myProject = project;
190 myExecutors = executors;
191 myShowVcsCommit = showVcsCommit;
192 myVcs = singleVcs;
193 myListComments = new HashMap<String, String>();
195 if (!myShowVcsCommit && ((myExecutors == null) || myExecutors.size() == 0)) {
196 throw new IllegalArgumentException("nothing found to execute commit with");
199 myAllOfDefaultChangeListChangesIncluded = changes.containsAll(defaultChangeList.getChanges());
201 myIsAlien = isAlien;
202 if (isAlien) {
203 AlienChangeListBrowser browser = new AlienChangeListBrowser(project, changeLists, changes, initialSelection, true, true, singleVcs);
204 myBrowser = browser;
205 myBrowserExtender = browser;
206 } else {
207 MultipleChangeListBrowser browser = new MultipleChangeListBrowser(project, changeLists, changes, initialSelection, true, true,
208 new Runnable() {
209 public void run() {
210 updateWarning();
213 new Runnable() {
214 public void run() {
215 for (CheckinHandler handler : myHandlers) {
216 handler.includedChangesChanged();
220 myBrowser = browser;
221 myBrowserExtender = browser.getExtender();
224 myBrowserExtender.addToolbarActions(this);
226 myBrowserExtender.addSelectedListChangeListener(new SelectedListChangeListener() {
227 public void selectedListChanged() {
228 updateOnListSelection();
231 myBrowser.setDiffExtendUIFactory(new ShowDiffAction.DiffExtendUIFactory() {
232 public List<? extends AnAction> createActions(final Change change) {
233 return myBrowser.createDiffActions(change);
236 @Nullable
237 public JComponent createBottomComponent() {
238 return new DiffCommitMessageEditor(CommitChangeListDialog.this);
242 myCommitMessageArea = new CommitMessage();
243 myCommitMessageArea.init();
245 if (comment != null) {
246 setCommitMessage(comment);
247 myLastKnownComment = comment;
248 myLastSelectedListName = initialSelection == null ? null : initialSelection.getName();
249 } else {
250 setCommitMessage(VcsConfiguration.getInstance(project).LAST_COMMIT_MESSAGE);
251 updateComment();
253 String messageFromVcs = getInitialMessageFromVcs();
254 if (messageFromVcs != null) {
255 myCommitMessageArea.setText(messageFromVcs);
259 myActionName = VcsBundle.message("commit.dialog.title");
261 myAdditionalOptionsPanel = new JPanel();
263 myAdditionalOptionsPanel.setLayout(new BorderLayout());
264 Box optionsBox = Box.createVerticalBox();
266 boolean hasVcsOptions = false;
267 Box vcsCommitOptions = Box.createVerticalBox();
268 final List<AbstractVcs> vcses = getAffectedVcses();
269 myCheckinChangeListSpecificComponents = new HashMap<String, CheckinChangeListSpecificComponent>();
270 for (AbstractVcs vcs : vcses) {
271 final CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment();
272 if (checkinEnvironment != null) {
273 final RefreshableOnComponent options = checkinEnvironment.createAdditionalOptionsPanel(this);
274 if (options != null) {
275 JPanel vcsOptions = new JPanel(new BorderLayout());
276 vcsOptions.add(options.getComponent(), BorderLayout.CENTER);
277 vcsOptions.add(SeparatorFactory.createSeparator(vcs.getDisplayName(), null), BorderLayout.NORTH);
278 vcsCommitOptions.add(vcsOptions);
279 myPerVcsOptionsPanels.put(vcs, vcsOptions);
280 myAdditionalComponents.add(options);
281 if (options instanceof CheckinChangeListSpecificComponent) {
282 myCheckinChangeListSpecificComponents.put(vcs.getName(), (CheckinChangeListSpecificComponent) options);
284 hasVcsOptions = true;
289 if (hasVcsOptions) {
290 vcsCommitOptions.add(Box.createVerticalGlue());
291 optionsBox.add(vcsCommitOptions);
294 boolean beforeVisible = false;
295 boolean afterVisible = false;
296 Box beforeBox = Box.createVerticalBox();
297 Box afterBox = Box.createVerticalBox();
298 final List<CheckinHandlerFactory> handlerFactories = ProjectLevelVcsManager.getInstance(project).getRegisteredCheckinHandlerFactories();
299 for (CheckinHandlerFactory factory : handlerFactories) {
300 final CheckinHandler handler = factory.createHandler(this);
301 myHandlers.add(handler);
302 final RefreshableOnComponent beforePanel = handler.getBeforeCheckinConfigurationPanel();
303 if (beforePanel != null) {
304 beforeBox.add(beforePanel.getComponent());
305 beforeVisible = true;
306 myAdditionalComponents.add(beforePanel);
309 final RefreshableOnComponent afterPanel = handler.getAfterCheckinConfigurationPanel();
310 if (afterPanel != null) {
311 afterBox.add(afterPanel.getComponent());
312 afterVisible = true;
313 myAdditionalComponents.add(afterPanel);
317 final String actionName = getCommitActionName();
318 final String borderTitleName = actionName.replace("_", "");
319 if (beforeVisible) {
320 beforeBox.add(Box.createVerticalGlue());
321 beforeBox.add(SeparatorFactory.createSeparator(VcsBundle.message("border.standard.checkin.options.group", borderTitleName), null), 0);
322 optionsBox.add(beforeBox);
325 if (afterVisible) {
326 afterBox.add(Box.createVerticalGlue());
327 afterBox.add(SeparatorFactory.createSeparator(VcsBundle.message("border.standard.after.checkin.options.group", borderTitleName), null), 0);
328 optionsBox.add(afterBox);
331 if (hasVcsOptions || beforeVisible || afterVisible) {
332 optionsBox.add(Box.createVerticalGlue());
333 myAdditionalOptionsPanel.add(optionsBox, BorderLayout.NORTH);
336 setOKButtonText(actionName);
338 if (myShowVcsCommit) {
339 setTitle(myActionName);
341 else {
342 setTitle(trimEllipsis(myExecutors.get(0).getActionText()));
345 restoreState();
347 if (myExecutors != null) {
348 myExecutorActions = new Action[myExecutors.size()];
350 for (int i = 0; i < myExecutors.size(); i++) {
351 final CommitExecutor commitExecutor = myExecutors.get(i);
352 myExecutorActions[i] = new CommitExecutorAction(commitExecutor, i == 0 && !myShowVcsCommit);
354 } else {
355 myExecutorActions = null;
358 myWarningLabel = new JLabel();
359 myWarningLabel.setUI(new MultiLineLabelUI());
360 myWarningLabel.setForeground(Color.red);
362 updateWarning();
364 init();
365 updateButtons();
366 updateVcsOptionsVisibility();
368 updateOnListSelection();
369 myCommitMessageArea.requestFocusInMessage();
371 for (EditChangelistSupport support : Extensions.getExtensions(EditChangelistSupport.EP_NAME, project)) {
372 support.installSearch(myCommitMessageArea.getTextField(), myCommitMessageArea.getTextField());
377 private void updateOnListSelection() {
378 updateComment();
379 updateVcsOptionsVisibility();
380 for (CheckinChangeListSpecificComponent component : myCheckinChangeListSpecificComponents.values()) {
381 component.onChangeListSelected((LocalChangeList) myBrowser.getSelectedChangeList());
385 private void updateWarning() {
386 // check for null since can be called from constructor before field initialization
387 if (myWarningLabel != null) {
388 myWarningLabel.setVisible(false);
389 final VcsException updateException = ((ChangeListManagerImpl)ChangeListManager.getInstance(myProject)).getUpdateException();
390 if (updateException != null) {
391 final String[] messages = updateException.getMessages();
392 if (messages != null || messages.length > 0) {
393 final String message = messages[0];
394 myWarningLabel.setText("Warning: not all local changes may be shown due to an error: " + message);
395 myWarningLabel.setVisible(true);
401 private void updateVcsOptionsVisibility() {
402 final List<AbstractVcs> affectedVcses = getAffectedVcses(myProject, myBrowser.getSelectedChangeList().getChanges());
403 for(Map.Entry<AbstractVcs, JPanel> entry: myPerVcsOptionsPanels.entrySet()) {
404 entry.getValue().setVisible(affectedVcses.contains(entry.getKey()));
408 protected Action[] createActions() {
409 Action[] result;
410 final int executorsSize = (myExecutors == null) ? 0 : myExecutors.size();
412 if (myShowVcsCommit) {
413 result = new Action[2 + executorsSize];
414 result[0] = getOKAction();
415 if (myExecutors != null) {
416 System.arraycopy(myExecutorActions, 0, result, 1, myExecutorActions.length);
419 else {
420 result = new Action[1 + executorsSize];
421 if (myExecutors != null) {
422 System.arraycopy(myExecutorActions, 0, result, 0, myExecutorActions.length);
425 result[result.length - 1] = getCancelAction();
426 return result;
429 private void execute(final CommitExecutor commitExecutor) {
430 if (!saveDialogState()) return;
431 saveComments(true);
432 final CommitSession session = commitExecutor.createCommitSession();
433 if (session == CommitSession.VCS_COMMIT) {
434 doOKAction();
435 return;
437 boolean isOK = true;
438 if (SessionDialog.createConfigurationUI(session, getIncludedChanges(), getCommitMessage())!= null) {
439 DialogWrapper sessionDialog = new SessionDialog(commitExecutor.getActionText(),
440 getProject(),
441 session,
442 getIncludedChanges(),
443 getCommitMessage());
444 sessionDialog.show();
445 isOK = sessionDialog.isOK();
447 if (isOK) {
448 final DefaultListCleaner defaultListCleaner = new DefaultListCleaner();
449 runBeforeCommitHandlers(new Runnable() {
450 public void run() {
451 try {
452 final boolean completed = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
453 public void run() {
454 session.execute(getIncludedChanges(), getCommitMessage());
456 }, commitExecutor.getActionText(), true, getProject());
458 if (completed) {
459 for (CheckinHandler handler : myHandlers) {
460 handler.checkinSuccessful();
463 defaultListCleaner.clean();
464 close(OK_EXIT_CODE);
466 else {
467 session.executionCanceled();
470 catch (Throwable e) {
471 Messages.showErrorDialog(VcsBundle.message("error.executing.commit", commitExecutor.getActionText(), e.getLocalizedMessage()),
472 commitExecutor.getActionText());
474 for (CheckinHandler handler : myHandlers) {
475 handler.checkinFailed(Arrays.asList(new VcsException(e)));
479 }, commitExecutor);
483 else {
484 session.executionCanceled();
488 @Nullable
489 private String getInitialMessageFromVcs() {
490 final List<Change> list = getIncludedChanges();
491 final Ref<String> result = new Ref<String>();
492 ChangesUtil.processChangesByVcs(myProject, list, new ChangesUtil.PerVcsProcessor<Change>() {
493 public void process(final AbstractVcs vcs, final List<Change> items) {
494 if (result.isNull()) {
495 CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment();
496 if (checkinEnvironment != null) {
497 final Collection<FilePath> paths = ChangesUtil.getPaths(items);
498 String defaultMessage = checkinEnvironment.getDefaultMessageFor(paths.toArray(new FilePath[paths.size()]));
499 if (defaultMessage != null) {
500 result.set(defaultMessage);
506 return result.get();
509 private void saveCommentIntoChangeList() {
510 if (myLastSelectedListName != null) {
511 final String actualCommentText = myCommitMessageArea.getComment();
512 final String saved = myListComments.get(myLastSelectedListName);
513 if (! Comparing.equal(saved, actualCommentText)) {
514 myListComments.put(myLastSelectedListName, actualCommentText);
519 private boolean isDefaultList(final LocalChangeList list) {
520 return VcsBundle.message("changes.default.changlist.name").equals(list.getName());
523 private void updateComment() {
524 final LocalChangeList list = (LocalChangeList) myBrowser.getSelectedChangeList();
525 if (list == null || (list.getName().equals(myLastSelectedListName))) {
526 return;
527 } else if (myLastSelectedListName != null) {
528 saveCommentIntoChangeList();
530 myLastSelectedListName = list.getName();
532 String listComment = list.getComment();
533 if (StringUtil.isEmptyOrSpaces(listComment)) {
534 final String listTitle = list.getName();
535 if (! isDefaultList(list)) {
536 listComment = listTitle;
538 else {
539 // use last know comment; it is already stored in list
540 listComment = myLastKnownComment;
544 myCommitMessageArea.setText(listComment);
548 @Override
549 public void dispose() {
550 myDisposed = true;
551 myBrowser.dispose();
552 Disposer.dispose(myCommitMessageArea);
553 Disposer.dispose(myOKButtonUpdateAlarm);
554 myUpdateButtonsRunnable.cancel();
555 super.dispose();
556 PropertiesComponent.getInstance().setValue(SPLITTER_PROPORTION_OPTION, String.valueOf(mySplitter.getProportion()));
559 public String getCommitActionName() {
560 String name = null;
561 for (AbstractVcs vcs : getAffectedVcses()) {
562 final CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment();
563 if (name == null && checkinEnvironment != null) {
564 name = checkinEnvironment.getCheckinOperationName();
566 else {
567 name = VcsBundle.message("commit.dialog.default.commit.operation.name");
570 return name != null ? name : VcsBundle.message("commit.dialog.default.commit.operation.name");
573 private boolean checkComment() {
574 if (VcsConfiguration.getInstance(myProject).FORCE_NON_EMPTY_COMMENT && (getCommitMessage().length() == 0)) {
575 int requestForCheckin = Messages.showYesNoDialog(VcsBundle.message("confirmation.text.check.in.with.empty.comment"),
576 VcsBundle.message("confirmation.title.check.in.with.empty.comment"),
577 Messages.getWarningIcon());
578 return requestForCheckin == OK_EXIT_CODE;
580 else {
581 return true;
585 private void stopUpdate() {
586 myDisposed = true;
587 myUpdateButtonsRunnable.cancel();
590 private void restartUpdate() {
591 myDisposed = false;
592 myUpdateButtonsRunnable.restart(this);
595 private void runBeforeCommitHandlers(final Runnable okAction, final CommitExecutor executor) {
596 Runnable proceedRunnable = new Runnable() {
597 public void run() {
598 FileDocumentManager.getInstance().saveAllDocuments();
600 for (CheckinHandler handler : myHandlers) {
601 final CheckinHandler.ReturnResult result = handler.beforeCheckin(executor);
602 if (result == CheckinHandler.ReturnResult.COMMIT) continue;
603 if (result == CheckinHandler.ReturnResult.CANCEL) {
604 restartUpdate();
605 return;
608 if (result == CheckinHandler.ReturnResult.CLOSE_WINDOW) {
609 final ChangeList changeList = myBrowser.getSelectedChangeList();
610 CommitHelper.moveToFailedList(changeList,
611 getCommitMessage(),
612 getIncludedChanges(),
613 VcsBundle.message("commit.dialog.rejected.commit.template", changeList.getName()),
614 myProject);
615 doCancelAction();
616 return;
620 okAction.run();
624 stopUpdate();
625 for(CheckinHandler handler: myHandlers) {
626 if (handler instanceof CheckinMetaHandler) {
627 ((CheckinMetaHandler) handler).runCheckinHandlers(proceedRunnable);
628 return;
631 proceedRunnable.run();
634 protected void doOKAction() {
635 if (!saveDialogState()) return;
636 saveComments(true);
637 final DefaultListCleaner defaultListCleaner = new DefaultListCleaner();
639 ensureDataIsActual(new Runnable() {
640 public void run() {
641 try {
642 runBeforeCommitHandlers(new Runnable() {
643 public void run() {
644 CommitChangeListDialog.super.doOKAction();
645 doCommit();
647 }, null);
649 defaultListCleaner.clean();
651 catch (InputException ex) {
652 ex.show();
658 private boolean saveDialogState() {
659 if (!checkComment()) {
660 return false;
663 saveCommentIntoChangeList();
664 VcsConfiguration.getInstance(myProject).saveCommitMessage(getCommitMessage());
665 try {
666 saveState();
668 catch(InputException ex) {
669 ex.show();
670 return false;
672 return true;
675 private class DefaultListCleaner {
676 private final boolean myToClean;
678 private DefaultListCleaner() {
679 final int selectedSize = getIncludedChanges().size();
680 final ChangeList selectedList = myBrowser.getSelectedChangeList();
681 final int totalSize = selectedList.getChanges().size();
682 myToClean = (totalSize == selectedSize) && (isDefaultList((LocalChangeList) selectedList));
685 void clean() {
686 if (myToClean) {
687 final ChangeListManager clManager = ChangeListManager.getInstance(myProject);
688 clManager.editComment(myLastSelectedListName, "");
693 private void saveComments(final boolean isOk) {
694 final ChangeListManager clManager = ChangeListManager.getInstance(myProject);
695 if (isOk) {
696 final int selectedSize = getIncludedChanges().size();
697 final ChangeList selectedList = myBrowser.getSelectedChangeList();
698 final int totalSize = selectedList.getChanges().size();
699 if (totalSize > selectedSize) {
700 myListComments.remove(myLastSelectedListName);
703 for (Map.Entry<String, String> entry : myListComments.entrySet()) {
704 final String name = entry.getKey();
705 final String value = entry.getValue();
706 clManager.editComment(name, value);
710 @Override
711 public void doCancelAction() {
712 for (CheckinChangeListSpecificComponent component : myCheckinChangeListSpecificComponents.values()) {
713 component.saveState();
715 saveCommentIntoChangeList();
716 saveComments(false);
717 //VcsConfiguration.getInstance(myProject).saveCommitMessage(getCommitMessage());
718 super.doCancelAction();
721 private Map<String, Object> getAdditionalDataForSubmit() {
722 final Map<String, Object> result = new HashMap<String, Object>();
723 for (Map.Entry<String, CheckinChangeListSpecificComponent> entry : myCheckinChangeListSpecificComponents.entrySet()) {
724 final Object data = entry.getValue().getDataForCommit();
725 if (data != null) {
726 result.put(entry.getKey(), data);
729 return result;
732 private void doCommit() {
733 final CommitHelper helper = new CommitHelper(
734 myProject,
735 myBrowser.getSelectedChangeList(),
736 getIncludedChanges(),
737 myActionName,
738 getCommitMessage(),
739 myHandlers,
740 myAllOfDefaultChangeListChangesIncluded, false, getAdditionalDataForSubmit());
742 if (myIsAlien) {
743 helper.doAlienCommit(myVcs);
744 } else {
745 helper.doCommit();
749 @Nullable
750 protected JComponent createCenterPanel() {
751 JPanel rootPane = new JPanel(new BorderLayout());
753 mySplitter = new Splitter(true);
754 mySplitter.setHonorComponentsMinimumSize(true);
755 mySplitter.setFirstComponent(myBrowser);
756 mySplitter.setSecondComponent(myCommitMessageArea);
757 mySplitter.setProportion(calcSplitterProportion());
758 mySplitter.setDividerWidth(3);
759 rootPane.add(mySplitter, BorderLayout.CENTER);
761 JComponent browserHeader = myBrowser.getHeaderPanel();
762 myBrowser.remove(browserHeader);
763 rootPane.add(browserHeader, BorderLayout.NORTH);
765 JPanel infoPanel = new JPanel(new BorderLayout());
766 myChangesInfoCalculator = new CommitLegendPanel.ChangeInfoCalculator();
767 myLegend = new CommitLegendPanel(myChangesInfoCalculator);
768 infoPanel.add(myLegend.getComponent(), BorderLayout.NORTH);
769 infoPanel.add(myAdditionalOptionsPanel, BorderLayout.CENTER);
770 rootPane.add(infoPanel, BorderLayout.EAST);
771 infoPanel.setBorder(IdeBorderFactory.createEmptyBorder(0, 10, 0, 0));
773 rootPane.add(myWarningLabel, BorderLayout.SOUTH);
775 return rootPane;
778 private static float calcSplitterProportion() {
779 try {
780 final String s = PropertiesComponent.getInstance().getValue(SPLITTER_PROPORTION_OPTION);
781 return s != null ? Float.valueOf(s).floatValue() : 0.5f;
783 catch (NumberFormatException e) {
784 return 0.5f;
788 public List<AbstractVcs> getAffectedVcses() {
789 if (! myShowVcsCommit) {
790 return Collections.emptyList();
792 return myBrowserExtender.getAffectedVcses();
795 private static List<AbstractVcs> getAffectedVcses(Project project, final Collection<Change> changes) {
796 Set<AbstractVcs> result = new HashSet<AbstractVcs>();
797 for (Change change : changes) {
798 final AbstractVcs vcs = ChangesUtil.getVcsForChange(change, project);
799 if (vcs != null) {
800 result.add(vcs);
803 return new ArrayList<AbstractVcs>(result);
806 public Collection<VirtualFile> getRoots() {
807 Set<VirtualFile> result = new HashSet<VirtualFile>();
808 for (Change change : myBrowser.getCurrentDisplayedChanges()) {
809 final FilePath filePath = ChangesUtil.getFilePath(change);
810 VirtualFile root = ProjectLevelVcsManager.getInstance(myProject).getVcsRootFor(filePath);
811 if (root != null) {
812 result.add(root);
815 return result;
818 public JComponent getComponent() {
819 return mySplitter;
822 public boolean hasDiffs() {
823 return !getIncludedChanges().isEmpty();
826 public Collection<VirtualFile> getVirtualFiles() {
827 List<VirtualFile> result = new ArrayList<VirtualFile>();
828 for (Change change: getIncludedChanges()) {
829 final FilePath path = ChangesUtil.getFilePath(change);
830 final VirtualFile vFile = path.getVirtualFile();
831 if (vFile != null) {
832 result.add(vFile);
836 return result;
839 public Collection<Change> getSelectedChanges() {
840 return new ArrayList<Change>(getIncludedChanges());
843 public Collection<File> getFiles() {
844 List<File> result = new ArrayList<File>();
845 for (Change change: getIncludedChanges()) {
846 final FilePath path = ChangesUtil.getFilePath(change);
847 final File file = path.getIOFile();
848 result.add(file);
851 return result;
854 public Project getProject() {
855 return myProject;
858 public void setCommitMessage(final String currentDescription) {
859 setCommitMessageText(currentDescription);
860 myCommitMessageArea.requestFocusInMessage();
863 public Object getContextInfo(Object object) {
864 // todo
865 return null;
868 public void setWarning(String s) {
869 // todo
872 private void setCommitMessageText(final String currentDescription) {
873 myLastKnownComment = currentDescription;
874 myCommitMessageArea.setText(currentDescription);
877 public String getCommitMessage() {
878 return myCommitMessageArea.getComment();
881 public void refresh() {
882 ChangeListManager.getInstance(myProject).invokeAfterUpdate(new Runnable() {
883 public void run() {
884 myBrowser.rebuildList();
885 for (RefreshableOnComponent component : myAdditionalComponents) {
886 component.refresh();
889 }, InvokeAfterUpdateMode.SILENT, "commit dialog", ModalityState.current()); // title not shown for silently
892 public void saveState() {
893 for (RefreshableOnComponent component : myAdditionalComponents) {
894 component.saveState();
898 public void restoreState() {
899 for (RefreshableOnComponent component : myAdditionalComponents) {
900 component.restoreState();
904 private void updateButtons() {
905 if (myDisposed) return;
906 setOKActionEnabled(hasDiffs());
907 if (myExecutorActions != null) {
908 for (Action executorAction : myExecutorActions) {
909 executorAction.setEnabled(hasDiffs());
912 myOKButtonUpdateAlarm.cancelAllRequests();
913 myOKButtonUpdateAlarm.addRequest(myUpdateButtonsRunnable, 300, ModalityState.stateForComponent(myBrowser));
916 private void updateLegend() {
917 if (myDisposed) return;
918 myChangesInfoCalculator.update(myBrowser.getCurrentDisplayedChanges(), myBrowserExtender.getCurrentIncludedChanges());
919 myLegend.update();
922 @NotNull
923 private List<Change> getIncludedChanges() {
924 return myBrowserExtender.getCurrentIncludedChanges();
927 @NonNls
928 protected String getDimensionServiceKey() {
929 return "CommitChangelistDialog";
932 public JComponent getPreferredFocusedComponent() {
933 return myCommitMessageArea.getTextField();
936 public void calcData(DataKey key, DataSink sink) {
937 if (key == CheckinProjectPanel.PANEL_KEY) {
938 sink.put(CheckinProjectPanel.PANEL_KEY, this);
940 else {
941 myBrowser.calcData(key, sink);
945 static String trimEllipsis(final String title) {
946 if (title.endsWith("...")) {
947 return title.substring(0, title.length() - 3);
949 else {
950 return title;
954 private void ensureDataIsActual(final Runnable runnable) {
955 ChangeListManager.getInstance(myProject).invokeAfterUpdate(runnable, InvokeAfterUpdateMode.SYNCHRONOUS_CANCELLABLE,
956 "Refreshing changelists...", ModalityState.current());
959 private class CommitExecutorAction extends AbstractAction {
960 private final CommitExecutor myCommitExecutor;
962 public CommitExecutorAction(final CommitExecutor commitExecutor, final boolean isDefault) {
963 super(commitExecutor.getActionText());
964 myCommitExecutor = commitExecutor;
965 if (isDefault) {
966 putValue(DEFAULT_ACTION, Boolean.TRUE);
970 public void actionPerformed(ActionEvent e) {
971 ensureDataIsActual(new Runnable() {
972 public void run() {
973 execute(myCommitExecutor);
979 private static class DiffCommitMessageEditor extends JPanel implements Disposable {
980 private CommitChangeListDialog myCommitDialog;
981 private final JTextArea myArea = new JTextArea();
983 public DiffCommitMessageEditor(final CommitChangeListDialog dialog) {
984 super(new BorderLayout());
985 myArea.setText(dialog.getCommitMessage());
986 myArea.setLineWrap(true);
987 myArea.setWrapStyleWord(true);
988 JScrollPane scrollPane = new JScrollPane(myArea);
989 setBorder(IdeBorderFactory.createTitledBorder(VcsBundle.message("diff.commit.message.title")));
990 add(scrollPane, BorderLayout.CENTER);
991 myCommitDialog = dialog;
994 public void dispose() {
995 if (myCommitDialog != null) {
996 myCommitDialog.setCommitMessageText(myArea.getText());
997 myCommitDialog = null;
1001 public Dimension getPreferredSize() {
1002 // we don't want to be squeezed to one line
1003 return new Dimension(400, 120);