2 * Copyright 2000-2008 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 git4idea
.rebase
;
18 import com
.intellij
.openapi
.project
.Project
;
19 import com
.intellij
.openapi
.ui
.DialogWrapper
;
20 import com
.intellij
.openapi
.util
.SystemInfo
;
21 import com
.intellij
.openapi
.util
.io
.FileUtil
;
22 import com
.intellij
.openapi
.vfs
.VirtualFile
;
23 import com
.intellij
.util
.ListWithSelection
;
24 import com
.intellij
.util
.ui
.ComboBoxTableCellEditor
;
25 import com
.intellij
.util
.ui
.ComboBoxTableCellRenderer
;
26 import git4idea
.actions
.GitShowAllSubmittedFilesAction
;
27 import git4idea
.commands
.StringScanner
;
28 import git4idea
.config
.GitConfigUtil
;
29 import git4idea
.i18n
.GitBundle
;
30 import org
.jetbrains
.annotations
.NonNls
;
33 import javax
.swing
.event
.ListSelectionEvent
;
34 import javax
.swing
.event
.ListSelectionListener
;
35 import javax
.swing
.event
.TableModelEvent
;
36 import javax
.swing
.event
.TableModelListener
;
37 import javax
.swing
.table
.AbstractTableModel
;
38 import javax
.swing
.table
.TableColumn
;
39 import java
.awt
.event
.ActionEvent
;
40 import java
.awt
.event
.ActionListener
;
42 import java
.util
.ArrayList
;
43 import java
.util
.Arrays
;
46 * Editor for rebase entries. It allows reordering of
47 * the entries and changing commit status.
49 public class GitRebaseEditor
extends DialogWrapper
{
51 * The table that lists all commits
53 private JTable myCommitsTable
;
57 private JButton myMoveUpButton
;
59 * The move down button
61 private JButton myMoveDownButton
;
63 * The view commit button
65 private JButton myViewButton
;
69 private JPanel myPanel
;
73 private final MyTableModel myTableModel
;
77 private final String myFile
;
81 private final Project myProject
;
85 private final VirtualFile myGitRoot
;
87 * The cygwin drive prefix
89 @NonNls private static final String CYGDRIVE_PREFIX
= "/cygdrive/";
94 * @param project the project
95 * @param gitRoot the git root
96 * @param file the file to edit
97 * @throws IOException if file could not be loaded
99 protected GitRebaseEditor(final Project project
, final VirtualFile gitRoot
, String file
) throws IOException
{
100 super(project
, true);
103 setTitle(GitBundle
.getString("rebase.editor.title"));
104 if (SystemInfo
.isWindows
&& file
.startsWith(CYGDRIVE_PREFIX
)) {
105 final int prefixSize
= CYGDRIVE_PREFIX
.length();
106 file
= file
.substring(prefixSize
, prefixSize
+ 1) + ":" + file
.substring(prefixSize
+ 1);
109 myTableModel
= new MyTableModel();
110 myTableModel
.load(file
);
111 myCommitsTable
.setModel(myTableModel
);
112 myCommitsTable
.setSelectionMode(ListSelectionModel
.SINGLE_SELECTION
);
113 TableColumn actionColumn
= myCommitsTable
.getColumnModel().getColumn(MyTableModel
.ACTION
);
114 actionColumn
.setCellEditor(ComboBoxTableCellEditor
.INSTANCE
);
115 actionColumn
.setCellRenderer(ComboBoxTableCellRenderer
.INSTANCE
);
116 myCommitsTable
.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
117 public void valueChanged(final ListSelectionEvent e
) {
118 boolean selected
= myCommitsTable
.getSelectedRowCount() != 0;
119 myMoveUpButton
.setEnabled(selected
);
121 myViewButton
.setEnabled(true);
122 int row
= myCommitsTable
.getSelectedRow();
123 myMoveUpButton
.setEnabled(row
!= 0);
124 myMoveDownButton
.setEnabled(row
!= myTableModel
.myEntries
.size() - 1);
127 myMoveUpButton
.setEnabled(false);
128 myMoveDownButton
.setEnabled(false);
129 myViewButton
.setEnabled(false);
133 myViewButton
.addActionListener(new ActionListener() {
134 public void actionPerformed(final ActionEvent e
) {
135 int row
= myCommitsTable
.getSelectedRow();
139 GitRebaseEntry entry
= myTableModel
.myEntries
.get(row
);
140 GitShowAllSubmittedFilesAction
.showSubmittedFiles(project
, entry
.getCommit(), gitRoot
);
143 myMoveUpButton
.addActionListener(new ActionListener() {
144 public void actionPerformed(final ActionEvent e
) {
145 final int row
= myCommitsTable
.getSelectedRow();
146 if (myTableModel
.moveUp(row
)) {
147 myCommitsTable
.getSelectionModel().setSelectionInterval(row
- 1, row
- 1);
151 myMoveDownButton
.addActionListener(new ActionListener() {
152 public void actionPerformed(final ActionEvent e
) {
153 final int row
= myCommitsTable
.getSelectedRow();
154 if (myTableModel
.moveDown(row
)) {
155 myCommitsTable
.getSelectionModel().setSelectionInterval(row
+ 1, row
+ 1);
159 myTableModel
.addTableModelListener(new TableModelListener() {
160 public void tableChanged(final TableModelEvent e
) {
170 private void validateFields() {
171 final ArrayList
<GitRebaseEntry
> entries
= myTableModel
.myEntries
;
172 if (entries
.size() == 0) {
173 setErrorText(GitBundle
.getString("rebase.editor.invalid.entryset"));
174 setOKActionEnabled(false);
178 while (i
< entries
.size() && entries
.get(i
).getAction() == GitRebaseEntry
.Action
.skip
) {
181 if (i
< entries
.size() && entries
.get(i
).getAction() == GitRebaseEntry
.Action
.squash
) {
182 setErrorText(GitBundle
.getString("rebase.editor.invalid.squash"));
183 setOKActionEnabled(false);
187 setOKActionEnabled(true);
191 * Save entries back to the file
193 * @throws IOException if there is IO problem with saving
195 public void save() throws IOException
{
196 myTableModel
.save(myFile
);
202 protected JComponent
createCenterPanel() {
210 protected String
getDimensionServiceKey() {
211 return getClass().getName();
218 protected String
getHelpId() {
219 return "reference.VersionControl.Git.RebaseCommits";
225 * @throws IOException if file cannot be reset to empty one
227 public void cancel() throws IOException
{
228 myTableModel
.cancel(myFile
);
233 * The table model for the commits
235 private class MyTableModel
extends AbstractTableModel
{
239 private static final int ACTION
= 0;
241 * The commit hash column
243 private static final int COMMIT
= 1;
247 private static final int SUBJECT
= 2;
252 final ArrayList
<GitRebaseEntry
> myEntries
= new ArrayList
<GitRebaseEntry
>();
258 public Class
<?
> getColumnClass(final int columnIndex
) {
259 return columnIndex
== ACTION ? ListWithSelection
.class : String
.class;
266 public String
getColumnName(final int column
) {
269 return GitBundle
.getString("rebase.editor.action.column");
271 return GitBundle
.getString("rebase.editor.commit.column");
273 return GitBundle
.getString("rebase.editor.comment.column");
275 throw new IllegalArgumentException("Unsupported column index: " + column
);
282 public int getRowCount() {
283 return myEntries
.size();
289 public int getColumnCount() {
296 public Object
getValueAt(final int rowIndex
, final int columnIndex
) {
297 GitRebaseEntry e
= myEntries
.get(rowIndex
);
298 switch (columnIndex
) {
300 return new ListWithSelection
<GitRebaseEntry
.Action
>(Arrays
.asList(GitRebaseEntry
.Action
.values()), e
.getAction());
302 return e
.getCommit();
304 return e
.getSubject();
306 throw new IllegalArgumentException("Unsupported column index: " + columnIndex
);
314 @SuppressWarnings({"unchecked"})
315 public void setValueAt(final Object aValue
, final int rowIndex
, final int columnIndex
) {
316 assert columnIndex
== ACTION
;
317 GitRebaseEntry e
= myEntries
.get(rowIndex
);
318 e
.setAction((GitRebaseEntry
.Action
)aValue
);
319 fireTableCellUpdated(rowIndex
, columnIndex
);
326 public boolean isCellEditable(final int rowIndex
, final int columnIndex
) {
327 return columnIndex
== ACTION
;
331 * Load data from the file
333 * @param file the file to load
334 * @throws IOException if file could not be loaded
336 public void load(final String file
) throws IOException
{
337 String encoding
= GitConfigUtil
.getLogEncoding(myProject
, myGitRoot
);
338 final StringScanner s
= new StringScanner(new String(FileUtil
.loadFileText(new File(file
), encoding
)));
339 while (s
.hasMoreData()) {
340 if (s
.isEol() || s
.startsWith('#') || s
.startsWith("noop")) {
344 String action
= s
.spaceToken();
345 assert "pick".equals(action
) : "Initial action should be pick: " + action
;
346 String hash
= s
.spaceToken();
347 String comment
= s
.line();
348 myEntries
.add(new GitRebaseEntry(hash
, comment
));
353 * Save text to the file
355 * @param file the file to save to
356 * @throws IOException if there is IO problem
358 public void save(final String file
) throws IOException
{
359 String encoding
= GitConfigUtil
.getLogEncoding(myProject
, myGitRoot
);
360 PrintWriter out
= new PrintWriter(new OutputStreamWriter(new FileOutputStream(file
), encoding
));
362 for (GitRebaseEntry e
: myEntries
) {
363 if (e
.getAction() != GitRebaseEntry
.Action
.skip
) {
364 out
.println(e
.getAction().toString() + " " + e
.getCommit() + " " + e
.getSubject());
374 * Save text to the file
376 * @param file the file to save to
377 * @throws IOException if there is IO problem
379 public void cancel(final String file
) throws IOException
{
380 PrintWriter out
= new PrintWriter(new FileWriter(file
));
382 //noinspection HardCodedStringLiteral
383 out
.println("# rebase is cancelled");
392 * Move selected row up. If row cannot be moved up, do nothing and return false.
394 * @param row a row to move
395 * @return true if row was moved
397 public boolean moveUp(final int row
) {
398 if (row
< 1 || row
>= myEntries
.size()) {
401 myCommitsTable
.removeEditor();
402 GitRebaseEntry e
= myEntries
.get(row
);
403 myEntries
.set(row
, myEntries
.get(row
- 1));
404 myEntries
.set(row
- 1, e
);
405 fireTableRowsUpdated(row
- 1, row
);
410 * Move selected row down. If row cannot be moved down, do nothing and return false.
412 * @param row a row to move
413 * @return true if row was moved
415 public boolean moveDown(final int row
) {
416 if (row
< 0 || row
>= myEntries
.size() - 1) {
419 myCommitsTable
.removeEditor();
420 GitRebaseEntry e
= myEntries
.get(row
);
421 myEntries
.set(row
, myEntries
.get(row
+ 1));
422 myEntries
.set(row
+ 1, e
);
423 fireTableRowsUpdated(row
, row
+ 1);