IDEA-103464 Adding environment variables to a Tomcat server is a UX nightmare
authorVassiliy.Kudryashov <Vassiliy.Kudryashov@jetbrains.com>
Mon, 3 Nov 2014 12:29:52 +0000 (15:29 +0300)
committerVassiliy.Kudryashov <Vassiliy.Kudryashov@jetbrains.com>
Mon, 3 Nov 2014 12:31:00 +0000 (15:31 +0300)
platform/lang-api/src/com/intellij/execution/util/EnvVariablesTable.java
platform/lang-api/src/com/intellij/execution/util/ListTableWithButtons.java
platform/lang-api/src/com/intellij/execution/util/PathMappingTable.java
platform/platform-api/src/com/intellij/ui/ToolbarDecorator.java
platform/platform-api/src/com/intellij/ui/table/TableView.java
plugins/javaFX/src/org/jetbrains/plugins/javaFX/packaging/JavaFxArtifactPropertiesEditor.java
plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ManageTemplateVariablesDialog.java

index be4db4d892a4d42eee024251aa2f4a9480387faa..dc41cef0a1cc982f533de283c7a3c56c1e4375e0 100644 (file)
 
 package com.intellij.execution.util;
 
+import com.intellij.icons.AllIcons;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.ide.CopyPasteManager;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.AnActionButton;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.ui.ColumnInfo;
 import com.intellij.util.ui.ListTableModel;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.util.ArrayList;
 import java.util.List;
 
 public class EnvVariablesTable extends ListTableWithButtons<EnvironmentVariable> {
@@ -91,6 +103,61 @@ public class EnvVariablesTable extends ListTableWithButtons<EnvironmentVariable>
     return new EnvironmentVariable("", "", false);
   }
 
+  @Override
+  protected boolean isEmpty(EnvironmentVariable element) {
+    return element.getName().isEmpty() && element.getValue().isEmpty();
+  }
+
+  @Override
+  protected AnActionButton[] createExtraActions() {
+    AnActionButton copyButton = new AnActionButton(ActionsBundle.message("action.EditorCopy.text"), AllIcons.Actions.Copy) {
+      @Override
+      public void actionPerformed(@NotNull AnActionEvent e) {
+        stopEditing();
+        StringBuilder sb = new StringBuilder();
+        List<EnvironmentVariable> variables = getEnvironmentVariables();
+        for (EnvironmentVariable environmentVariable : variables) {
+          if (environmentVariable.getIsPredefined() || isEmpty(environmentVariable)) continue;
+          if (sb.length() > 0) sb.append('\n');
+          sb.append(StringUtil.escapeChar(environmentVariable.getName(), '=')).append('=')
+            .append(StringUtil.escapeChar(environmentVariable.getValue(), '='));
+        }
+        CopyPasteManager.getInstance().setContents(new StringSelection(sb.toString()));
+      }
+    };
+    AnActionButton pasteButton = new AnActionButton(ActionsBundle.message("action.EditorPaste.text"), AllIcons.Actions.Menu_paste) {
+      @Override
+      public void actionPerformed(@NotNull AnActionEvent e) {
+        stopEditing();
+        String content = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor);
+        if (content == null || !content.contains("=")) return;
+        List<EnvironmentVariable> parsed = new ArrayList<EnvironmentVariable>();
+        List<String> lines = StringUtil.split(content, "\n");
+        for (String line : lines) {
+          int pos = line.indexOf('=');
+          if (pos == -1) continue;
+          while (pos > 0 && line.charAt(pos - 1) == '\\') {
+            pos = line.indexOf('=', pos + 1);
+          }
+          parsed.add(new EnvironmentVariable(
+            StringUtil.unescapeStringCharacters(line.substring(0, pos)),
+            StringUtil.unescapeStringCharacters(line.substring(pos + 1)),
+            false));
+        }
+        List<EnvironmentVariable> variables =
+          new ArrayList<EnvironmentVariable>(ContainerUtil.filter(getEnvironmentVariables(), new Condition<EnvironmentVariable>() {
+            @Override
+            public boolean value(EnvironmentVariable variable) {
+              return variable.getIsPredefined();
+            }
+          }));
+        variables.addAll(parsed);
+        setValues(variables);
+      }
+    };
+    return new AnActionButton[]{copyButton, pasteButton};
+  }
+
   @Override
   protected EnvironmentVariable cloneElement(EnvironmentVariable envVariable) {
     return envVariable.clone();
index 28491c21fc2dd63d9f13cd30e3487b511d529aac..ef95a9adc0d73da94271cb2018bf070ebf8935c9 100644 (file)
@@ -29,6 +29,8 @@ import org.jetbrains.annotations.Nullable;
 import javax.swing.*;
 import javax.swing.table.DefaultTableCellRenderer;
 import javax.swing.table.TableCellRenderer;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
 import java.util.List;
 import java.util.Observable;
 
@@ -38,17 +40,52 @@ import java.util.Observable;
 public abstract class ListTableWithButtons<T> extends Observable {
   private final List<T> myElements = ContainerUtil.newArrayList();
   private final JPanel myPanel;
-  private final TableView myTableView;
+  private final TableView<T> myTableView;
   private boolean myIsEnabled = true;
 
   protected ListTableWithButtons() {
-    myTableView = new TableView(createListModel());
+    myTableView = new TableView(createListModel()) {
+      @Override
+      protected void createDefaultEditors() {
+        super.createDefaultEditors();
+        Object editor = defaultEditorsByColumnClass.get(String.class);
+        if (editor instanceof DefaultCellEditor) {
+          ((DefaultCellEditor)editor).getComponent().addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyPressed(KeyEvent e) {
+              final int column = myTableView.getEditingColumn();
+              final int row = myTableView.getEditingRow();
+              if (e.getModifiers() == 0 && (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB)) {
+                SwingUtilities.invokeLater(new Runnable() {
+                  @Override
+                  public void run() {
+                    stopEditing();
+                    int nextColumn = column < myTableView.getColumnCount() - 1? column + 1 : 0;
+                    int nextRow = nextColumn == 0 ? row + 1 : row;
+                    if (nextRow > myTableView.getRowCount() - 1) {
+                      if (myElements.isEmpty() || !ListTableWithButtons.this.isEmpty(myElements.get(myElements.size() - 1))) {
+                        ToolbarDecorator.findAddButton(myPanel).actionPerformed(null);
+                        return;
+                      } else {
+                        nextRow = 0;
+                      }
+                    }
+                    myTableView.editCellAt(nextRow, nextColumn);
+                  }
+                });
+              }
+            }
+          });
+        }
+      }
+    };
     myTableView.setRowHeight(new JTextField().getPreferredSize().height);
     myTableView.getTableViewModel().setSortable(false);
     myPanel = ToolbarDecorator.createDecorator(myTableView)
       .setAddAction(new AnActionButtonRunnable() {
         @Override
         public void run(AnActionButton button) {
+          if (!myElements.isEmpty() && isEmpty(myElements.get(myElements.size() - 1))) return;
           myTableView.stopEditing();
           setModified();
           SwingUtilities.invokeLater(new Runnable() {
@@ -56,6 +93,7 @@ public abstract class ListTableWithButtons<T> extends Observable {
             public void run() {
               myElements.add(createElement());
               myTableView.getTableViewModel().setItems(myElements);
+              myTableView.scrollRectToVisible(myTableView.getCellRect(myElements.size() - 1, 0, true));
               myTableView.getComponent().editCellAt(myElements.size() - 1, 0);
             }
           });
@@ -65,7 +103,7 @@ public abstract class ListTableWithButtons<T> extends Observable {
         public void run(AnActionButton button) {
           myTableView.stopEditing();
           setModified();
-          Object selected = getSelection();
+          T selected = getSelection();
           if (selected != null) {
             int selectedIndex = myElements.indexOf(selected);
             myElements.remove(selected);
@@ -80,7 +118,7 @@ public abstract class ListTableWithButtons<T> extends Observable {
             }
           }
         }
-      }).disableUpDownActions().createPanel();
+      }).disableUpDownActions().addExtraActions(createExtraActions()).createPanel();
 
     ToolbarDecorator.findRemoveButton(myPanel).addCustomUpdater(new AnActionButtonUpdater() {
       @Override
@@ -135,6 +173,12 @@ public abstract class ListTableWithButtons<T> extends Observable {
 
   protected abstract T createElement();
 
+  protected abstract boolean isEmpty(T element);
+
+  protected AnActionButton[] createExtraActions() {
+    return null;
+  }
+
 
   protected T getSelection() {
     int selIndex = myTableView.getComponent().getSelectionModel().getMinSelectionIndex();
index 715263265f1792745b0369380f267a92f21a8698..ce7e795014301c44f07cb986ac7b96725866a29f 100644 (file)
@@ -88,6 +88,11 @@ final class PathMappingTable extends ListTableWithButtons<PathMappingSettings.Pa
     return new PathMappingSettings.PathMapping();
   }
 
+  @Override
+  protected boolean isEmpty(PathMappingSettings.PathMapping element) {
+    return element.getLocalRoot().isEmpty() && element.getRemoteRoot().isEmpty();
+  }
+
   @Override
   protected PathMappingSettings.PathMapping cloneElement(PathMappingSettings.PathMapping envVariable) {
     return envVariable.clone();
index 80e3d1eec7fb7e1720f5beff67ce0bcbcf1cf0ca..853a9724d63d9039d20ff4b5818662ac2d2af949 100644 (file)
@@ -191,7 +191,16 @@ public abstract class ToolbarDecorator implements CommonActionsPanel.ListenerFac
   }
 
   public ToolbarDecorator addExtraAction(AnActionButton action) {
-    myExtraActions.add(action);
+    if (action != null) {
+      myExtraActions.add(action);
+    }
+    return this;
+  }
+
+  public ToolbarDecorator addExtraActions(AnActionButton... actions) {
+    for (AnActionButton action : actions) {
+      addExtraAction(action);
+    }
     return this;
   }
 
index 44b40e938cdbce667a97c1bb5b8e443984925d85..6051152c07fc63530633906f5a8c5b7ae3b0205d 100644 (file)
@@ -258,7 +258,7 @@ public class TableView<Item> extends BaseTableView implements ItemsProvider, Sel
     return this;
   }
 
-  public TableViewModel getTableViewModel() {
+  public TableViewModel<Item> getTableViewModel() {
     return getListTableModel();
   }
 
@@ -274,7 +274,9 @@ public class TableView<Item> extends BaseTableView implements ItemsProvider, Sel
     defaultEditorsByColumnClass.put(String.class, new UIDefaults.LazyValue() {
       @Override
       public Object createValue(UIDefaults table) {
-        return new DefaultCellEditor(GuiUtils.createUndoableTextField());
+        DefaultCellEditor editor = new DefaultCellEditor(GuiUtils.createUndoableTextField());
+        editor.setClickCountToStart(1);
+        return editor;
       }
     });
   }
index 0006db9918c761fc6a94b23b08cf75f5f4ce0d09..e4a7b47b673c4db1bfdbb5178bf4bf9bada67217 100644 (file)
@@ -23,7 +23,6 @@ import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.ui.TextFieldWithBrowseButton;
 import com.intellij.openapi.util.Comparing;
-import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.packaging.artifacts.Artifact;
 import com.intellij.packaging.ui.ArtifactPropertiesEditor;
@@ -316,6 +315,11 @@ public class JavaFxArtifactPropertiesEditor extends ArtifactPropertiesEditor {
         return new JavaFxManifestAttribute("", "");
       }
 
+      @Override
+      protected boolean isEmpty(JavaFxManifestAttribute element) {
+        return element.getName().isEmpty() && element.getValue().isEmpty();
+      }
+
       @Override
       protected JavaFxManifestAttribute cloneElement(JavaFxManifestAttribute attribute) {
         return new JavaFxManifestAttribute(attribute.getName(), attribute.getValue());
index 3c3ed235454afcec61fd46314c0797a70777a193..ebb8bd39997b605cda3a03df97709a5f2ea747b9 100644 (file)
@@ -206,6 +206,11 @@ public class ManageTemplateVariablesDialog extends DialogWrapper {
       return new TemplateVariable("", "");
     }
 
+    @Override
+    protected boolean isEmpty(TemplateVariable element) {
+      return element.getName().isEmpty() && element.getValue().isEmpty();
+    }
+
     @Override
     protected TemplateVariable cloneElement(final TemplateVariable variable) {
       return variable.clone();