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
;
50 import java
.awt
.event
.ActionEvent
;
53 import java
.util
.List
;
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
>();
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
) {
100 public void cancel() {
105 if (myDialog
!= null) {
106 myDialog
.updateButtons();
107 myDialog
.updateLegend();
111 public void restart(final CommitChangeListDialog dialog
) {
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,
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
);
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());
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"));
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);
190 myExecutors
= executors
;
191 myShowVcsCommit
= showVcsCommit
;
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());
203 AlienChangeListBrowser browser
= new AlienChangeListBrowser(project
, changeLists
, changes
, initialSelection
, true, true, singleVcs
);
205 myBrowserExtender
= browser
;
207 MultipleChangeListBrowser browser
= new MultipleChangeListBrowser(project
, changeLists
, changes
, initialSelection
, true, true,
215 for (CheckinHandler handler
: myHandlers
) {
216 handler
.includedChangesChanged();
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
);
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();
250 setCommitMessage(VcsConfiguration
.getInstance(project
).LAST_COMMIT_MESSAGE
);
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;
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());
313 myAdditionalComponents
.add(afterPanel
);
317 final String actionName
= getCommitActionName();
318 final String borderTitleName
= actionName
.replace("_", "");
320 beforeBox
.add(Box
.createVerticalGlue());
321 beforeBox
.add(SeparatorFactory
.createSeparator(VcsBundle
.message("border.standard.checkin.options.group", borderTitleName
), null), 0);
322 optionsBox
.add(beforeBox
);
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
);
342 setTitle(trimEllipsis(myExecutors
.get(0).getActionText()));
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
);
355 myExecutorActions
= null;
358 myWarningLabel
= new JLabel();
359 myWarningLabel
.setUI(new MultiLineLabelUI());
360 myWarningLabel
.setForeground(Color
.red
);
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() {
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() {
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
);
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();
429 private void execute(final CommitExecutor commitExecutor
) {
430 if (!saveDialogState()) return;
432 final CommitSession session
= commitExecutor
.createCommitSession();
433 if (session
== CommitSession
.VCS_COMMIT
) {
438 if (SessionDialog
.createConfigurationUI(session
, getIncludedChanges(), getCommitMessage())!= null) {
439 DialogWrapper sessionDialog
= new SessionDialog(commitExecutor
.getActionText(),
442 getIncludedChanges(),
444 sessionDialog
.show();
445 isOK
= sessionDialog
.isOK();
448 final DefaultListCleaner defaultListCleaner
= new DefaultListCleaner();
449 runBeforeCommitHandlers(new Runnable() {
452 final boolean completed
= ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
454 session
.execute(getIncludedChanges(), getCommitMessage());
456 }, commitExecutor
.getActionText(), true, getProject());
459 for (CheckinHandler handler
: myHandlers
) {
460 handler
.checkinSuccessful();
463 defaultListCleaner
.clean();
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
)));
484 session
.executionCanceled();
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
);
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
))) {
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
;
539 // use last know comment; it is already stored in list
540 listComment
= myLastKnownComment
;
544 myCommitMessageArea
.setText(listComment
);
549 public void dispose() {
552 Disposer
.dispose(myCommitMessageArea
);
553 Disposer
.dispose(myOKButtonUpdateAlarm
);
554 myUpdateButtonsRunnable
.cancel();
556 PropertiesComponent
.getInstance().setValue(SPLITTER_PROPORTION_OPTION
, String
.valueOf(mySplitter
.getProportion()));
559 public String
getCommitActionName() {
561 for (AbstractVcs vcs
: getAffectedVcses()) {
562 final CheckinEnvironment checkinEnvironment
= vcs
.getCheckinEnvironment();
563 if (name
== null && checkinEnvironment
!= null) {
564 name
= checkinEnvironment
.getCheckinOperationName();
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
;
585 private void stopUpdate() {
587 myUpdateButtonsRunnable
.cancel();
590 private void restartUpdate() {
592 myUpdateButtonsRunnable
.restart(this);
595 private void runBeforeCommitHandlers(final Runnable okAction
, final CommitExecutor executor
) {
596 Runnable proceedRunnable
= new Runnable() {
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
) {
608 if (result
== CheckinHandler
.ReturnResult
.CLOSE_WINDOW
) {
609 final ChangeList changeList
= myBrowser
.getSelectedChangeList();
610 CommitHelper
.moveToFailedList(changeList
,
612 getIncludedChanges(),
613 VcsBundle
.message("commit.dialog.rejected.commit.template", changeList
.getName()),
625 for(CheckinHandler handler
: myHandlers
) {
626 if (handler
instanceof CheckinMetaHandler
) {
627 ((CheckinMetaHandler
) handler
).runCheckinHandlers(proceedRunnable
);
631 proceedRunnable
.run();
634 protected void doOKAction() {
635 if (!saveDialogState()) return;
637 final DefaultListCleaner defaultListCleaner
= new DefaultListCleaner();
639 ensureDataIsActual(new Runnable() {
642 runBeforeCommitHandlers(new Runnable() {
644 CommitChangeListDialog
.super.doOKAction();
649 defaultListCleaner
.clean();
651 catch (InputException ex
) {
658 private boolean saveDialogState() {
659 if (!checkComment()) {
663 saveCommentIntoChangeList();
664 VcsConfiguration
.getInstance(myProject
).saveCommitMessage(getCommitMessage());
668 catch(InputException ex
) {
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
));
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
);
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
);
711 public void doCancelAction() {
712 for (CheckinChangeListSpecificComponent component
: myCheckinChangeListSpecificComponents
.values()) {
713 component
.saveState();
715 saveCommentIntoChangeList();
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();
726 result
.put(entry
.getKey(), data
);
732 private void doCommit() {
733 final CommitHelper helper
= new CommitHelper(
735 myBrowser
.getSelectedChangeList(),
736 getIncludedChanges(),
740 myAllOfDefaultChangeListChangesIncluded
, false, getAdditionalDataForSubmit());
743 helper
.doAlienCommit(myVcs
);
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
);
778 private static float calcSplitterProportion() {
780 final String s
= PropertiesComponent
.getInstance().getValue(SPLITTER_PROPORTION_OPTION
);
781 return s
!= null ? Float
.valueOf(s
).floatValue() : 0.5f
;
783 catch (NumberFormatException e
) {
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
);
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
);
818 public JComponent
getComponent() {
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();
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();
854 public Project
getProject() {
858 public void setCommitMessage(final String currentDescription
) {
859 setCommitMessageText(currentDescription
);
860 myCommitMessageArea
.requestFocusInMessage();
863 public Object
getContextInfo(Object object
) {
868 public void setWarning(String s
) {
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() {
884 myBrowser
.rebuildList();
885 for (RefreshableOnComponent component
: myAdditionalComponents
) {
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());
923 private List
<Change
> getIncludedChanges() {
924 return myBrowserExtender
.getCurrentIncludedChanges();
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);
941 myBrowser
.calcData(key
, sink
);
945 static String
trimEllipsis(final String title
) {
946 if (title
.endsWith("...")) {
947 return title
.substring(0, title
.length() - 3);
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
;
966 putValue(DEFAULT_ACTION
, Boolean
.TRUE
);
970 public void actionPerformed(ActionEvent e
) {
971 ensureDataIsActual(new Runnable() {
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);