IDEA-25295 (NPE on integrating change into branch)
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / ui / CommitChangeListDialog.java
blob84e44c10317729e0e02ef24bb86ccecaf5276432
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;
92 private static class MyUpdateButtonsRunnable implements Runnable {
93 private CommitChangeListDialog myDialog;
95 private MyUpdateButtonsRunnable(final CommitChangeListDialog dialog) {
96 myDialog = dialog;
99 public void cancel() {
100 myDialog = null;
103 public void run() {
104 if (myDialog != null) {
105 myDialog.updateButtons();
106 myDialog.updateLegend();
110 public void restart(final CommitChangeListDialog dialog) {
111 myDialog = dialog;
112 run();
116 private final MyUpdateButtonsRunnable myUpdateButtonsRunnable = new MyUpdateButtonsRunnable(this);
118 private static boolean commit(final Project project, final List<Change> changes, final LocalChangeList initialSelection,
119 final List<CommitExecutor> executors, final boolean showVcsCommit, final String comment) {
120 final ChangeListManager manager = ChangeListManager.getInstance(project);
121 final LocalChangeList defaultList = manager.getDefaultChangeList();
122 final ArrayList<LocalChangeList> changeLists = new ArrayList<LocalChangeList>(manager.getChangeListsCopy());
123 CommitChangeListDialog dialog =
124 new CommitChangeListDialog(project, changes, initialSelection, executors, showVcsCommit, defaultList, changeLists, null, false,
125 comment);
126 dialog.show();
127 return dialog.isOK();
130 public static void commitPaths(final Project project, Collection<FilePath> paths, final LocalChangeList initialSelection,
131 @Nullable final CommitExecutor executor, final String comment) {
132 final ChangeListManager manager = ChangeListManager.getInstance(project);
133 final Collection<Change> changes = new HashSet<Change>();
134 for (FilePath path : paths) {
135 changes.addAll(manager.getChangesIn(path));
138 commitChanges(project, changes, initialSelection, executor, comment);
141 public static boolean commitChanges(final Project project, final Collection<Change> changes, final LocalChangeList initialSelection,
142 final CommitExecutor executor, final String comment) {
143 final ChangeListManager manager = ChangeListManager.getInstance(project);
144 if (executor == null) {
145 return commitChanges(project, changes, initialSelection, manager.getRegisteredExecutors(), true, comment);
147 else {
148 return commitChanges(project, changes, initialSelection, Collections.singletonList(executor), false, comment);
152 public static boolean commitChanges(final Project project, final Collection<Change> changes, final LocalChangeList initialSelection,
153 final List<CommitExecutor> executors, final boolean showVcsCommit, final String comment) {
154 if (changes.isEmpty()) {
155 Messages.showWarningDialog(project, VcsBundle.message("commit.dialog.no.changes.detected.text") ,
156 VcsBundle.message("commit.dialog.no.changes.detected.title"));
157 return false;
160 return commit(project, new ArrayList<Change>(changes), initialSelection, executors, showVcsCommit, comment);
163 public static void commitAlienChanges(final Project project, final List<Change> changes, final AbstractVcs vcs,
164 final String changelistName, final String comment) {
165 final LocalChangeList lcl = new AlienLocalChangeList(changes, changelistName);
166 new CommitChangeListDialog(project, changes, null, null, true, AlienLocalChangeList.DEFAULT_ALIEN, Collections.singletonList(lcl), vcs,
167 true, comment).show();
170 private CommitChangeListDialog(final Project project,
171 final List<Change> changes,
172 final LocalChangeList initialSelection,
173 final List<CommitExecutor> executors,
174 final boolean showVcsCommit, final LocalChangeList defaultChangeList,
175 final List<LocalChangeList> changeLists, final AbstractVcs singleVcs, final boolean isAlien,
176 final String comment) {
177 super(project, true);
178 myProject = project;
179 myExecutors = executors;
180 myShowVcsCommit = showVcsCommit;
181 myVcs = singleVcs;
182 myListComments = new HashMap<String, String>();
184 if (!myShowVcsCommit && ((myExecutors == null) || myExecutors.size() == 0)) {
185 throw new IllegalArgumentException("nothing found to execute commit with");
188 myAllOfDefaultChangeListChangesIncluded = changes.containsAll(defaultChangeList.getChanges());
190 myIsAlien = isAlien;
191 if (isAlien) {
192 AlienChangeListBrowser browser = new AlienChangeListBrowser(project, changeLists, changes, initialSelection, true, true, singleVcs);
193 myBrowser = browser;
194 myBrowserExtender = browser;
195 } else {
196 MultipleChangeListBrowser browser = new MultipleChangeListBrowser(project, changeLists, changes, initialSelection, true, true,
197 new Runnable() {
198 public void run() {
199 updateWarning();
202 new Runnable() {
203 public void run() {
204 for (CheckinHandler handler : myHandlers) {
205 handler.includedChangesChanged();
209 myBrowser = browser;
210 myBrowserExtender = browser.getExtender();
213 myBrowserExtender.addToolbarActions(this);
215 myBrowserExtender.addSelectedListChangeListener(new SelectedListChangeListener() {
216 public void selectedListChanged() {
217 updateOnListSelection();
220 myBrowser.setDiffExtendUIFactory(new ShowDiffAction.DiffExtendUIFactory() {
221 public List<? extends AnAction> createActions(final Change change) {
222 return myBrowser.createDiffActions(change);
225 @Nullable
226 public JComponent createBottomComponent() {
227 return new DiffCommitMessageEditor(CommitChangeListDialog.this);
231 myCommitMessageArea = new CommitMessage();
232 myCommitMessageArea.init();
234 if (comment != null) {
235 setCommitMessage(comment);
236 myLastKnownComment = comment;
237 myLastSelectedListName = initialSelection == null ? null : initialSelection.getName();
238 } else {
239 setCommitMessage(VcsConfiguration.getInstance(project).LAST_COMMIT_MESSAGE);
240 updateComment();
242 String messageFromVcs = getInitialMessageFromVcs();
243 if (messageFromVcs != null) {
244 myCommitMessageArea.setText(messageFromVcs);
248 myActionName = VcsBundle.message("commit.dialog.title");
250 myAdditionalOptionsPanel = new JPanel();
252 myAdditionalOptionsPanel.setLayout(new BorderLayout());
253 Box optionsBox = Box.createVerticalBox();
255 boolean hasVcsOptions = false;
256 Box vcsCommitOptions = Box.createVerticalBox();
257 final List<AbstractVcs> vcses = getAffectedVcses();
258 myCheckinChangeListSpecificComponents = new HashMap<String, CheckinChangeListSpecificComponent>();
259 for (AbstractVcs vcs : vcses) {
260 final CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment();
261 if (checkinEnvironment != null) {
262 final RefreshableOnComponent options = checkinEnvironment.createAdditionalOptionsPanel(this);
263 if (options != null) {
264 JPanel vcsOptions = new JPanel(new BorderLayout());
265 vcsOptions.add(options.getComponent(), BorderLayout.CENTER);
266 vcsOptions.add(SeparatorFactory.createSeparator(vcs.getDisplayName(), null), BorderLayout.NORTH);
267 vcsCommitOptions.add(vcsOptions);
268 myPerVcsOptionsPanels.put(vcs, vcsOptions);
269 myAdditionalComponents.add(options);
270 if (options instanceof CheckinChangeListSpecificComponent) {
271 myCheckinChangeListSpecificComponents.put(vcs.getName(), (CheckinChangeListSpecificComponent) options);
273 hasVcsOptions = true;
278 if (hasVcsOptions) {
279 vcsCommitOptions.add(Box.createVerticalGlue());
280 optionsBox.add(vcsCommitOptions);
283 boolean beforeVisible = false;
284 boolean afterVisible = false;
285 Box beforeBox = Box.createVerticalBox();
286 Box afterBox = Box.createVerticalBox();
287 final List<CheckinHandlerFactory> handlerFactories = ProjectLevelVcsManager.getInstance(project).getRegisteredCheckinHandlerFactories();
288 for (CheckinHandlerFactory factory : handlerFactories) {
289 final CheckinHandler handler = factory.createHandler(this);
290 myHandlers.add(handler);
291 final RefreshableOnComponent beforePanel = handler.getBeforeCheckinConfigurationPanel();
292 if (beforePanel != null) {
293 beforeBox.add(beforePanel.getComponent());
294 beforeVisible = true;
295 myAdditionalComponents.add(beforePanel);
298 final RefreshableOnComponent afterPanel = handler.getAfterCheckinConfigurationPanel();
299 if (afterPanel != null) {
300 afterBox.add(afterPanel.getComponent());
301 afterVisible = true;
302 myAdditionalComponents.add(afterPanel);
306 final String actionName = getCommitActionName();
307 final String borderTitleName = actionName.replace("_", "");
308 if (beforeVisible) {
309 beforeBox.add(Box.createVerticalGlue());
310 beforeBox.add(SeparatorFactory.createSeparator(VcsBundle.message("border.standard.checkin.options.group", borderTitleName), null), 0);
311 optionsBox.add(beforeBox);
314 if (afterVisible) {
315 afterBox.add(Box.createVerticalGlue());
316 afterBox.add(SeparatorFactory.createSeparator(VcsBundle.message("border.standard.after.checkin.options.group", borderTitleName), null), 0);
317 optionsBox.add(afterBox);
320 if (hasVcsOptions || beforeVisible || afterVisible) {
321 optionsBox.add(Box.createVerticalGlue());
322 myAdditionalOptionsPanel.add(optionsBox, BorderLayout.NORTH);
325 setOKButtonText(actionName);
327 if (myShowVcsCommit) {
328 setTitle(myActionName);
330 else {
331 setTitle(trimEllipsis(myExecutors.get(0).getActionText()));
334 restoreState();
336 if (myExecutors != null) {
337 myExecutorActions = new Action[myExecutors.size()];
339 for (int i = 0; i < myExecutors.size(); i++) {
340 final CommitExecutor commitExecutor = myExecutors.get(i);
341 myExecutorActions[i] = new CommitExecutorAction(commitExecutor, i == 0 && !myShowVcsCommit);
343 } else {
344 myExecutorActions = null;
347 myWarningLabel = new JLabel();
348 myWarningLabel.setUI(new MultiLineLabelUI());
349 myWarningLabel.setForeground(Color.red);
351 updateWarning();
353 init();
354 updateButtons();
355 updateVcsOptionsVisibility();
357 updateOnListSelection();
358 myCommitMessageArea.requestFocusInMessage();
360 for (EditChangelistSupport support : Extensions.getExtensions(EditChangelistSupport.EP_NAME, project)) {
361 support.installSearch(myCommitMessageArea.getTextField(), myCommitMessageArea.getTextField());
366 private void updateOnListSelection() {
367 updateComment();
368 updateVcsOptionsVisibility();
369 for (CheckinChangeListSpecificComponent component : myCheckinChangeListSpecificComponents.values()) {
370 component.onChangeListSelected((LocalChangeList) myBrowser.getSelectedChangeList());
374 private void updateWarning() {
375 // check for null since can be called from constructor before field initialization
376 if (myWarningLabel != null) {
377 myWarningLabel.setVisible(false);
378 final VcsException updateException = ((ChangeListManagerImpl)ChangeListManager.getInstance(myProject)).getUpdateException();
379 if (updateException != null) {
380 final String[] messages = updateException.getMessages();
381 if (messages != null || messages.length > 0) {
382 final String message = messages[0];
383 myWarningLabel.setText("Warning: not all local changes may be shown due to an error: " + message);
384 myWarningLabel.setVisible(true);
390 private void updateVcsOptionsVisibility() {
391 final List<AbstractVcs> affectedVcses = getAffectedVcses(myBrowser.getSelectedChangeList().getChanges());
392 for(Map.Entry<AbstractVcs, JPanel> entry: myPerVcsOptionsPanels.entrySet()) {
393 entry.getValue().setVisible(affectedVcses.contains(entry.getKey()));
397 protected Action[] createActions() {
398 Action[] result;
399 final int executorsSize = (myExecutors == null) ? 0 : myExecutors.size();
401 if (myShowVcsCommit) {
402 result = new Action[2 + executorsSize];
403 result[0] = getOKAction();
404 if (myExecutors != null) {
405 System.arraycopy(myExecutorActions, 0, result, 1, myExecutorActions.length);
408 else {
409 result = new Action[1 + executorsSize];
410 if (myExecutors != null) {
411 System.arraycopy(myExecutorActions, 0, result, 0, myExecutorActions.length);
414 result[result.length - 1] = getCancelAction();
415 return result;
418 private void execute(final CommitExecutor commitExecutor) {
419 if (!saveDialogState()) return;
420 saveComments(true);
421 final CommitSession session = commitExecutor.createCommitSession();
422 boolean isOK = true;
423 if (SessionDialog.createConfigurationUI(session, getIncludedChanges(), getCommitMessage())!= null) {
424 DialogWrapper sessionDialog = new SessionDialog(commitExecutor.getActionText(),
425 getProject(),
426 session,
427 getIncludedChanges(),
428 getCommitMessage());
429 sessionDialog.show();
430 isOK = sessionDialog.isOK();
432 if (isOK) {
433 final DefaultListCleaner defaultListCleaner = new DefaultListCleaner();
434 runBeforeCommitHandlers(new Runnable() {
435 public void run() {
436 try {
437 final boolean completed = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
438 public void run() {
439 session.execute(getIncludedChanges(), getCommitMessage());
441 }, commitExecutor.getActionText(), true, getProject());
443 if (completed) {
444 for (CheckinHandler handler : myHandlers) {
445 handler.checkinSuccessful();
448 defaultListCleaner.clean();
449 close(OK_EXIT_CODE);
451 else {
452 session.executionCanceled();
455 catch (Throwable e) {
456 Messages.showErrorDialog(VcsBundle.message("error.executing.commit", commitExecutor.getActionText(), e.getLocalizedMessage()),
457 commitExecutor.getActionText());
459 for (CheckinHandler handler : myHandlers) {
460 handler.checkinFailed(Arrays.asList(new VcsException(e)));
464 }, commitExecutor);
468 else {
469 session.executionCanceled();
473 @Nullable
474 private String getInitialMessageFromVcs() {
475 final List<Change> list = getIncludedChanges();
476 final Ref<String> result = new Ref<String>();
477 ChangesUtil.processChangesByVcs(myProject, list, new ChangesUtil.PerVcsProcessor<Change>() {
478 public void process(final AbstractVcs vcs, final List<Change> items) {
479 if (result.isNull()) {
480 CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment();
481 if (checkinEnvironment != null) {
482 final Collection<FilePath> paths = ChangesUtil.getPaths(items);
483 String defaultMessage = checkinEnvironment.getDefaultMessageFor(paths.toArray(new FilePath[paths.size()]));
484 if (defaultMessage != null) {
485 result.set(defaultMessage);
491 return result.get();
494 private void saveCommentIntoChangeList() {
495 if (myLastSelectedListName != null) {
496 final String actualCommentText = myCommitMessageArea.getComment();
497 final String saved = myListComments.get(myLastSelectedListName);
498 if (! Comparing.equal(saved, actualCommentText)) {
499 myListComments.put(myLastSelectedListName, actualCommentText);
504 private boolean isDefaultList(final LocalChangeList list) {
505 return VcsBundle.message("changes.default.changlist.name").equals(list.getName());
508 private void updateComment() {
509 final LocalChangeList list = (LocalChangeList) myBrowser.getSelectedChangeList();
510 if (list == null || (list.getName().equals(myLastSelectedListName))) {
511 return;
512 } else if (myLastSelectedListName != null) {
513 saveCommentIntoChangeList();
515 myLastSelectedListName = list.getName();
517 String listComment = list.getComment();
518 if (StringUtil.isEmptyOrSpaces(listComment)) {
519 final String listTitle = list.getName();
520 if (! isDefaultList(list)) {
521 listComment = listTitle;
523 else {
524 // use last know comment; it is already stored in list
525 listComment = myLastKnownComment;
529 myCommitMessageArea.setText(listComment);
533 @Override
534 public void dispose() {
535 myDisposed = true;
536 myBrowser.dispose();
537 Disposer.dispose(myCommitMessageArea);
538 Disposer.dispose(myOKButtonUpdateAlarm);
539 myUpdateButtonsRunnable.cancel();
540 super.dispose();
541 PropertiesComponent.getInstance().setValue(SPLITTER_PROPORTION_OPTION, String.valueOf(mySplitter.getProportion()));
544 public String getCommitActionName() {
545 String name = null;
546 for (AbstractVcs vcs : getAffectedVcses()) {
547 final CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment();
548 if (name == null && checkinEnvironment != null) {
549 name = checkinEnvironment.getCheckinOperationName();
551 else {
552 name = VcsBundle.message("commit.dialog.default.commit.operation.name");
555 return name != null ? name : VcsBundle.message("commit.dialog.default.commit.operation.name");
558 private boolean checkComment() {
559 if (VcsConfiguration.getInstance(myProject).FORCE_NON_EMPTY_COMMENT && (getCommitMessage().length() == 0)) {
560 int requestForCheckin = Messages.showYesNoDialog(VcsBundle.message("confirmation.text.check.in.with.empty.comment"),
561 VcsBundle.message("confirmation.title.check.in.with.empty.comment"),
562 Messages.getWarningIcon());
563 return requestForCheckin == OK_EXIT_CODE;
565 else {
566 return true;
570 private void stopUpdate() {
571 myDisposed = true;
572 myUpdateButtonsRunnable.cancel();
575 private void restartUpdate() {
576 myDisposed = false;
577 myUpdateButtonsRunnable.restart(this);
580 private void runBeforeCommitHandlers(final Runnable okAction, final CommitExecutor executor) {
581 Runnable proceedRunnable = new Runnable() {
582 public void run() {
583 FileDocumentManager.getInstance().saveAllDocuments();
585 for (CheckinHandler handler : myHandlers) {
586 final CheckinHandler.ReturnResult result = handler.beforeCheckin(executor);
587 if (result == CheckinHandler.ReturnResult.COMMIT) continue;
588 if (result == CheckinHandler.ReturnResult.CANCEL) {
589 restartUpdate();
590 return;
593 if (result == CheckinHandler.ReturnResult.CLOSE_WINDOW) {
594 final ChangeList changeList = myBrowser.getSelectedChangeList();
595 CommitHelper.moveToFailedList(changeList,
596 getCommitMessage(),
597 getIncludedChanges(),
598 VcsBundle.message("commit.dialog.rejected.commit.template", changeList.getName()),
599 myProject);
600 doCancelAction();
601 return;
605 okAction.run();
609 stopUpdate();
610 for(CheckinHandler handler: myHandlers) {
611 if (handler instanceof CheckinMetaHandler) {
612 ((CheckinMetaHandler) handler).runCheckinHandlers(proceedRunnable);
613 return;
616 proceedRunnable.run();
619 protected void doOKAction() {
620 if (!saveDialogState()) return;
621 saveComments(true);
622 final DefaultListCleaner defaultListCleaner = new DefaultListCleaner();
624 ensureDataIsActual(new Runnable() {
625 public void run() {
626 try {
627 runBeforeCommitHandlers(new Runnable() {
628 public void run() {
629 CommitChangeListDialog.super.doOKAction();
630 doCommit();
632 }, null);
634 defaultListCleaner.clean();
636 catch (InputException ex) {
637 ex.show();
643 private boolean saveDialogState() {
644 if (!checkComment()) {
645 return false;
648 saveCommentIntoChangeList();
649 VcsConfiguration.getInstance(myProject).saveCommitMessage(getCommitMessage());
650 try {
651 saveState();
653 catch(InputException ex) {
654 ex.show();
655 return false;
657 return true;
660 private class DefaultListCleaner {
661 private final boolean myToClean;
663 private DefaultListCleaner() {
664 final int selectedSize = getIncludedChanges().size();
665 final ChangeList selectedList = myBrowser.getSelectedChangeList();
666 final int totalSize = selectedList.getChanges().size();
667 myToClean = (totalSize == selectedSize) && (isDefaultList((LocalChangeList) selectedList));
670 void clean() {
671 if (myToClean) {
672 final ChangeListManager clManager = ChangeListManager.getInstance(myProject);
673 clManager.editComment(myLastSelectedListName, "");
678 private void saveComments(final boolean isOk) {
679 final ChangeListManager clManager = ChangeListManager.getInstance(myProject);
680 if (isOk) {
681 final int selectedSize = getIncludedChanges().size();
682 final ChangeList selectedList = myBrowser.getSelectedChangeList();
683 final int totalSize = selectedList.getChanges().size();
684 if (totalSize > selectedSize) {
685 myListComments.remove(myLastSelectedListName);
688 for (Map.Entry<String, String> entry : myListComments.entrySet()) {
689 final String name = entry.getKey();
690 final String value = entry.getValue();
691 clManager.editComment(name, value);
695 @Override
696 public void doCancelAction() {
697 for (CheckinChangeListSpecificComponent component : myCheckinChangeListSpecificComponents.values()) {
698 component.saveState();
700 saveCommentIntoChangeList();
701 saveComments(false);
702 //VcsConfiguration.getInstance(myProject).saveCommitMessage(getCommitMessage());
703 super.doCancelAction();
706 private Map<String, Object> getAdditionalDataForSubmit() {
707 final Map<String, Object> result = new HashMap<String, Object>();
708 for (Map.Entry<String, CheckinChangeListSpecificComponent> entry : myCheckinChangeListSpecificComponents.entrySet()) {
709 final Object data = entry.getValue().getDataForCommit();
710 if (data != null) {
711 result.put(entry.getKey(), data);
714 return result;
717 private void doCommit() {
718 final CommitHelper helper = new CommitHelper(
719 myProject,
720 myBrowser.getSelectedChangeList(),
721 getIncludedChanges(),
722 myActionName,
723 getCommitMessage(),
724 myHandlers,
725 myAllOfDefaultChangeListChangesIncluded, false, getAdditionalDataForSubmit());
727 if (myIsAlien) {
728 helper.doAlienCommit(myVcs);
729 } else {
730 helper.doCommit();
734 @Nullable
735 protected JComponent createCenterPanel() {
736 JPanel rootPane = new JPanel(new BorderLayout());
738 mySplitter = new Splitter(true);
739 mySplitter.setHonorComponentsMinimumSize(true);
740 mySplitter.setFirstComponent(myBrowser);
741 mySplitter.setSecondComponent(myCommitMessageArea);
742 mySplitter.setProportion(calcSplitterProportion());
743 mySplitter.setDividerWidth(3);
744 rootPane.add(mySplitter, BorderLayout.CENTER);
746 JComponent browserHeader = myBrowser.getHeaderPanel();
747 myBrowser.remove(browserHeader);
748 rootPane.add(browserHeader, BorderLayout.NORTH);
750 JPanel infoPanel = new JPanel(new BorderLayout());
751 myLegend = new CommitLegendPanel();
752 infoPanel.add(myLegend.getComponent(), BorderLayout.NORTH);
753 infoPanel.add(myAdditionalOptionsPanel, BorderLayout.CENTER);
754 rootPane.add(infoPanel, BorderLayout.EAST);
755 infoPanel.setBorder(IdeBorderFactory.createEmptyBorder(0, 10, 0, 0));
757 rootPane.add(myWarningLabel, BorderLayout.SOUTH);
759 return rootPane;
762 private static float calcSplitterProportion() {
763 try {
764 final String s = PropertiesComponent.getInstance().getValue(SPLITTER_PROPORTION_OPTION);
765 return s != null ? Float.valueOf(s).floatValue() : 0.5f;
767 catch (NumberFormatException e) {
768 return 0.5f;
772 public List<AbstractVcs> getAffectedVcses() {
773 if (! myShowVcsCommit) {
774 return Collections.emptyList();
776 return myBrowserExtender.getAffectedVcses();
779 private List<AbstractVcs> getAffectedVcses(final Collection<Change> changes) {
780 Set<AbstractVcs> result = new HashSet<AbstractVcs>();
781 for (Change change : changes) {
782 final AbstractVcs vcs = ChangesUtil.getVcsForChange(change, myProject);
783 if (vcs != null) {
784 result.add(vcs);
787 return new ArrayList<AbstractVcs>(result);
790 public Collection<VirtualFile> getRoots() {
791 Set<VirtualFile> result = new HashSet<VirtualFile>();
792 for (Change change : myBrowser.getCurrentDisplayedChanges()) {
793 final FilePath filePath = ChangesUtil.getFilePath(change);
794 VirtualFile root = ProjectLevelVcsManager.getInstance(myProject).getVcsRootFor(filePath);
795 if (root != null) {
796 result.add(root);
799 return result;
802 public JComponent getComponent() {
803 return mySplitter;
806 public boolean hasDiffs() {
807 return !getIncludedChanges().isEmpty();
810 public Collection<VirtualFile> getVirtualFiles() {
811 List<VirtualFile> result = new ArrayList<VirtualFile>();
812 for (Change change: getIncludedChanges()) {
813 final FilePath path = ChangesUtil.getFilePath(change);
814 final VirtualFile vFile = path.getVirtualFile();
815 if (vFile != null) {
816 result.add(vFile);
820 return result;
823 public Collection<Change> getSelectedChanges() {
824 return new ArrayList<Change>(getIncludedChanges());
827 public Collection<File> getFiles() {
828 List<File> result = new ArrayList<File>();
829 for (Change change: getIncludedChanges()) {
830 final FilePath path = ChangesUtil.getFilePath(change);
831 final File file = path.getIOFile();
832 result.add(file);
835 return result;
838 public Project getProject() {
839 return myProject;
842 public void setCommitMessage(final String currentDescription) {
843 setCommitMessageText(currentDescription);
844 myCommitMessageArea.requestFocusInMessage();
847 public Object getContextInfo(Object object) {
848 // todo
849 return null;
852 public void setWarning(String s) {
853 // todo
856 private void setCommitMessageText(final String currentDescription) {
857 myLastKnownComment = currentDescription;
858 myCommitMessageArea.setText(currentDescription);
861 public String getCommitMessage() {
862 return myCommitMessageArea.getComment();
865 public void refresh() {
866 ChangeListManager.getInstance(myProject).invokeAfterUpdate(new Runnable() {
867 public void run() {
868 myBrowser.rebuildList();
869 for (RefreshableOnComponent component : myAdditionalComponents) {
870 component.refresh();
873 }, InvokeAfterUpdateMode.SILENT, "commit dialog", ModalityState.current()); // title not shown for silently
876 public void saveState() {
877 for (RefreshableOnComponent component : myAdditionalComponents) {
878 component.saveState();
882 public void restoreState() {
883 for (RefreshableOnComponent component : myAdditionalComponents) {
884 component.restoreState();
888 private void updateButtons() {
889 if (myDisposed) return;
890 setOKActionEnabled(hasDiffs());
891 if (myExecutorActions != null) {
892 for (Action executorAction : myExecutorActions) {
893 executorAction.setEnabled(hasDiffs());
896 myOKButtonUpdateAlarm.cancelAllRequests();
897 myOKButtonUpdateAlarm.addRequest(myUpdateButtonsRunnable, 300, ModalityState.stateForComponent(myBrowser));
900 private void updateLegend() {
901 if (myDisposed) return;
902 myLegend.update(myBrowser.getCurrentDisplayedChanges(), myBrowserExtender.getCurrentIncludedChanges());
905 @NotNull
906 private List<Change> getIncludedChanges() {
907 return myBrowserExtender.getCurrentIncludedChanges();
910 @NonNls
911 protected String getDimensionServiceKey() {
912 return "CommitChangelistDialog";
915 public JComponent getPreferredFocusedComponent() {
916 return myCommitMessageArea.getTextField();
919 public void calcData(DataKey key, DataSink sink) {
920 if (key == CheckinProjectPanel.PANEL_KEY) {
921 sink.put(CheckinProjectPanel.PANEL_KEY, this);
923 else {
924 myBrowser.calcData(key, sink);
928 static String trimEllipsis(final String title) {
929 if (title.endsWith("...")) {
930 return title.substring(0, title.length() - 3);
932 else {
933 return title;
937 private void ensureDataIsActual(final Runnable runnable) {
938 ChangeListManager.getInstance(myProject).invokeAfterUpdate(runnable, InvokeAfterUpdateMode.SYNCHRONOUS_CANCELLABLE,
939 "Refreshing changelists...", ModalityState.current());
942 private class CommitExecutorAction extends AbstractAction {
943 private final CommitExecutor myCommitExecutor;
945 public CommitExecutorAction(final CommitExecutor commitExecutor, final boolean isDefault) {
946 super(commitExecutor.getActionText());
947 myCommitExecutor = commitExecutor;
948 if (isDefault) {
949 putValue(DEFAULT_ACTION, Boolean.TRUE);
953 public void actionPerformed(ActionEvent e) {
954 ensureDataIsActual(new Runnable() {
955 public void run() {
956 execute(myCommitExecutor);
962 private static class DiffCommitMessageEditor extends JPanel implements Disposable {
963 private CommitChangeListDialog myCommitDialog;
964 private final JTextArea myArea = new JTextArea();
966 public DiffCommitMessageEditor(final CommitChangeListDialog dialog) {
967 super(new BorderLayout());
968 myArea.setText(dialog.getCommitMessage());
969 myArea.setLineWrap(true);
970 myArea.setWrapStyleWord(true);
971 JScrollPane scrollPane = new JScrollPane(myArea);
972 setBorder(IdeBorderFactory.createTitledBorder(VcsBundle.message("diff.commit.message.title")));
973 add(scrollPane, BorderLayout.CENTER);
974 myCommitDialog = dialog;
977 public void dispose() {
978 if (myCommitDialog != null) {
979 myCommitDialog.setCommitMessageText(myArea.getText());
980 myCommitDialog = null;
984 public Dimension getPreferredSize() {
985 // we don't want to be squeezed to one line
986 return new Dimension(400, 120);