am aa22e26a: am 744fa997: am 8e0d3400: Merge "Adds renaming in the theme selector of the Theme Editor" into studio-1.3-dev automerge: df6dd83 automerge: fa79622

* commit 'aa22e26a489af87b0175b38c4f7de963130f3102':
  Adds renaming in the theme selector of the Theme Editor
diff --git a/android/src/com/android/tools/idea/editors/theme/AttributesPanel.java b/android/src/com/android/tools/idea/editors/theme/AttributesPanel.java
index 51bf996..124522c 100644
--- a/android/src/com/android/tools/idea/editors/theme/AttributesPanel.java
+++ b/android/src/com/android/tools/idea/editors/theme/AttributesPanel.java
@@ -79,6 +79,16 @@
     return ThemesListModel.SHOW_ALL_THEMES.equals(myThemeCombo.getSelectedItem());
   }
 
+  public boolean isRenameSelected() {
+    Object selectedItem = myThemeCombo.getSelectedItem();
+    if (!(selectedItem instanceof String)) {
+      // Selected themes are instances of EditedStyleItem
+      // So this method will return false on selecting a theme
+      return false;
+    }
+    return ((String)selectedItem).startsWith(ThemesListModel.RENAME);
+  }
+
   public ThemeEditorStyle getSelectedTheme() {
     Object item = myThemeCombo.getSelectedItem();
     if (item != null && !(item instanceof ThemeEditorStyle)) {
diff --git a/android/src/com/android/tools/idea/editors/theme/ThemeEditorComponent.java b/android/src/com/android/tools/idea/editors/theme/ThemeEditorComponent.java
index c1ac2d9..9fb59bb 100644
--- a/android/src/com/android/tools/idea/editors/theme/ThemeEditorComponent.java
+++ b/android/src/com/android/tools/idea/editors/theme/ThemeEditorComponent.java
@@ -45,6 +45,8 @@
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.Splitter;
+import com.intellij.psi.PsiElement;
+import com.intellij.refactoring.rename.RenameDialog;
 import com.intellij.util.Processor;
 import com.intellij.util.messages.MessageBusConnection;
 import com.intellij.util.ui.UIUtil;
@@ -262,6 +264,12 @@
           }
           return;
         }
+        if (myPanel.isRenameSelected()) {
+          if (!renameTheme()) {
+            myPanel.getThemeCombo().getModel().setSelectedItem(mySelectedTheme);
+          }
+          return;
+        }
         mySelectedTheme = myPanel.getSelectedTheme();
         saveCurrentSelectedTheme();
         myCurrentSubStyle = null;
@@ -343,6 +351,27 @@
     return false;
   }
 
+  /**
+   * Uses Android Studio refactoring to rename the current theme
+   * @return Whether the renaming is successful
+   */
+  private boolean renameTheme() {
+    assert mySelectedTheme.isProjectStyle();
+    PsiElement namePsiElement = mySelectedTheme.getNamePsiElement();
+    if (namePsiElement == null) {
+      return false;
+    }
+    RenameDialog renameDialog = new RenameDialog(myModule.getProject(), namePsiElement, null, null);
+    renameDialog.show();
+    if (renameDialog.isOK()) {
+      String newName = renameDialog.getNewName();
+      String newQualifiedName = mySelectedTheme.getName().replace(mySelectedTheme.getSimpleName(), newName);
+      reload(newQualifiedName);
+      return true;
+    }
+    return false;
+  }
+
   public void goToParent() {
     ThemeEditorStyle selectedStyle = getSelectedStyle();
     if (selectedStyle == null) {
@@ -605,11 +634,7 @@
       @Override
       public void tableChanged(TableModelEvent e) {
         if (e.getType() == TableModelEvent.UPDATE) {
-
-          if (e.getLastRow() == 0) { // Indicates a change in the theme name
-            reload(model.getThemeNameInXml());
-          }
-          else if (e.getLastRow() == TableModelEvent.HEADER_ROW) { // Indicates a change of the model
+          if (e.getLastRow() == TableModelEvent.HEADER_ROW) { // Indicates a change of the model
             myAttributesTable.updateRowHeights();
           }
         }
@@ -715,7 +740,7 @@
         return true;
       }
       int row = entry.getIdentifier().intValue();
-      if (entry.getModel().isSpecialRow(row)) {
+      if (entry.getModel().isThemeParentRow(row)) {
         return true;
       }
 
diff --git a/android/src/com/android/tools/idea/editors/theme/ThemesListModel.java b/android/src/com/android/tools/idea/editors/theme/ThemesListModel.java
index 6ad86ff..1364a3b 100644
--- a/android/src/com/android/tools/idea/editors/theme/ThemesListModel.java
+++ b/android/src/com/android/tools/idea/editors/theme/ThemesListModel.java
@@ -53,12 +53,14 @@
   };
   public static final String CREATE_NEW_THEME = "Create New Theme";
   public static final String SHOW_ALL_THEMES = "Show all themes";
+  public static final String RENAME = "Rename ";
   private static final Object[] EXTRA_OPTIONS = {SHOW_ALL_THEMES, SEPARATOR, CREATE_NEW_THEME};
 
   private ImmutableList<ThemeEditorStyle> myThemeList;
   private Object mySelectedObject;
   private int myNumberProjectThemes;
   private int mySizeBeforeExtraOptions;
+  private boolean isRenameAllowed;
 
   public ThemesListModel(@NotNull ThemeResolver themeResolver) {
     this(themeResolver, null);
@@ -134,12 +136,15 @@
 
   @Override
   public int getSize() {
-    return mySizeBeforeExtraOptions + EXTRA_OPTIONS.length;
+    return mySizeBeforeExtraOptions + EXTRA_OPTIONS.length + (isRenameAllowed ? 1 : 0);
   }
 
   @NotNull
   @Override
   public Object getElementAt(int index) {
+    if (isRenameAllowed && index == getSize() - 1) {
+      return renameOption();
+    }
     if (index >= mySizeBeforeExtraOptions) {
       return EXTRA_OPTIONS[index - mySizeBeforeExtraOptions];
     }
@@ -161,6 +166,12 @@
     }
     if (!Objects.equal(mySelectedObject, anItem)) {
       mySelectedObject = anItem;
+      if (mySelectedObject instanceof ThemeEditorStyle) {
+        isRenameAllowed = ((ThemeEditorStyle)mySelectedObject).isProjectStyle();
+      }
+      else {
+        isRenameAllowed = false;
+      }
       fireContentsChanged(this, -1, -1);
     }
   }
@@ -170,4 +181,12 @@
   public Object getSelectedItem() {
     return mySelectedObject;
   }
+
+  @NotNull
+  private String renameOption() {
+    assert mySelectedObject instanceof ThemeEditorStyle;
+    ThemeEditorStyle theme = (ThemeEditorStyle)mySelectedObject;
+    assert theme.isProjectStyle();
+    return RENAME + theme.getSimpleName();
+  }
 }
diff --git a/android/src/com/android/tools/idea/editors/theme/attributes/AttributesTableModel.java b/android/src/com/android/tools/idea/editors/theme/attributes/AttributesTableModel.java
index 93257cc..0a92723 100644
--- a/android/src/com/android/tools/idea/editors/theme/attributes/AttributesTableModel.java
+++ b/android/src/com/android/tools/idea/editors/theme/attributes/AttributesTableModel.java
@@ -15,7 +15,6 @@
  */
 package com.android.tools.idea.editors.theme.attributes;
 
-import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.resources.ResourceResolver;
 import com.android.resources.ResourceType;
@@ -26,7 +25,6 @@
 import com.android.tools.idea.editors.theme.ThemeEditorUtils;
 import com.android.tools.idea.editors.theme.attributes.editors.AttributeReferenceRendererEditor;
 import com.android.tools.idea.editors.theme.attributes.editors.ColorEditor.ColorInfo;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.fileEditor.FileEditorManager;
@@ -42,7 +40,6 @@
 import spantable.CellSpanModel;
 import com.intellij.openapi.diagnostic.Logger;
 
-import javax.swing.event.TableModelEvent;
 import javax.swing.table.AbstractTableModel;
 import java.awt.*;
 import java.awt.event.ActionEvent;
@@ -65,11 +62,6 @@
   private final Configuration myConfiguration;
   private List<TableLabel> myLabels;
   /**
-   * Used to store the name of the theme as it is in the styles.xml file
-   * May be different from the name of mySelectedStyle as a reloading of the theme may be necessary
-   */
-  private String myThemeNameInXml;
-  /**
    * Used to store the name of the theme parent as it is in the styles.xml file
    * May be different from the name of mySelectedStyle.getParent() as a reloading of the theme may be necessary
    */
@@ -83,10 +75,6 @@
 
   private final List<ThemePropertyChangedListener> myThemePropertyChangedListeners = new ArrayList<ThemePropertyChangedListener>();
   public final ParentAttribute parentAttribute = new ParentAttribute();
-  private final List<RowContents> mySpecialRows = ImmutableList.of(
-    new ThemeNameAttribute(),
-    parentAttribute
-  );
 
   private AttributeReferenceRendererEditor.ClickListener myGoToDefinitionListener;
 
@@ -143,10 +131,6 @@
     myThemePropertyChangedListeners.add(listener);
   }
 
-  public String getThemeNameInXml() {
-    return myThemeNameInXml;
-  }
-
   public AttributesTableModel(@NotNull ThemeEditorStyle selectedStyle,
                               @NotNull AttributesGrouper.GroupBy groupBy,
                               @NotNull Configuration configuration,
@@ -156,7 +140,6 @@
     myLabels = new ArrayList<TableLabel>();
     mySelectedStyle = selectedStyle;
     myGroupBy = groupBy;
-    myThemeNameInXml = mySelectedStyle.getName();
     myResourceResolver = configuration.getResourceResolver();
     myConfiguration = configuration;
 
@@ -173,11 +156,11 @@
   }
 
   public RowContents getRowContents(final int rowIndex) {
-    if (rowIndex < mySpecialRows.size()) {
-      return mySpecialRows.get(rowIndex);
+    if (rowIndex == 0) {
+      return parentAttribute;
     }
 
-    int offset = mySpecialRows.size();
+    int offset = 1;
     for (final TableLabel label : myLabels) {
       final int labelRowIndex = label.getRowPosition() + offset;
 
@@ -196,15 +179,15 @@
   }
 
   /**
-   * returns true if this row is not a attribute or a label and is the Theme name or Theme parent.
+   * returns true if this row is not a attribute or a label and is the Theme parent.
    */
-  public boolean isSpecialRow(int row) {
-    return row < mySpecialRows.size();
+  public boolean isThemeParentRow(int row) {
+    return row == 0;
   }
 
   @Override
   public int getRowCount() {
-    return myAttributes.size() + myLabels.size() + mySpecialRows.size();
+    return myAttributes.size() + myLabels.size() + 1;
   }
 
   @Override
@@ -252,7 +235,7 @@
   }
 
   /**
-   * Basically a union type, RowContents = LabelContents | AttributeContents | ParentAttribute | ThemeNameAttribute
+   * Basically a union type, RowContents = LabelContents | AttributeContents | ParentAttribute
    */
   public interface RowContents {
     int getColumnSpan(int column);
@@ -279,60 +262,6 @@
     ActionListener getResetCallback();
   }
 
-  /**
-   * Deals with setting up the theme name as an attribute that can be changed
-   */
-  private class ThemeNameAttribute implements RowContents {
-    @Override
-    public int getColumnSpan(int column) {
-      return 1;
-    }
-
-    @Override
-    public Object getValueAt(int column) {
-      if (column == 0) {
-        return "Theme Name";
-      }
-      else {
-        return mySelectedStyle.getSimpleName();
-      }
-    }
-
-    @Override
-    public void setValueAt(int column, Object value) {
-      if (column == 0) {
-        throw new RuntimeException("Tried to setValue at parent attribute label");
-      }
-      else {
-        String newName = (String) value;
-        mySelectedStyle.setName(newName);
-        myThemeNameInXml = SdkConstants.STYLE_RESOURCE_PREFIX + newName;
-
-        fireTableChanged(new TableModelEvent(AttributesTableModel.this, 0));
-      }
-    }
-
-    @Override
-    public Class<?> getCellClass(int column) {
-      return String.class;
-    }
-
-    @Override
-    public boolean isCellEditable(int column) {
-      return (column == 1 && !mySelectedStyle.isReadOnly());
-    }
-
-    @Override
-    public ActionListener getGoToDefinitionCallback() {
-      return null;
-    }
-
-    @Override
-    public ActionListener getResetCallback() {
-      return null;
-    }
-  }
-
   public class ParentAttribute implements RowContents {
     private ActionListener myGotoDefinitionCallback;
 
diff --git a/android/src/com/android/tools/idea/editors/theme/datamodels/ThemeEditorStyle.java b/android/src/com/android/tools/idea/editors/theme/datamodels/ThemeEditorStyle.java
index dca2c3a..147ba4a 100644
--- a/android/src/com/android/tools/idea/editors/theme/datamodels/ThemeEditorStyle.java
+++ b/android/src/com/android/tools/idea/editors/theme/datamodels/ThemeEditorStyle.java
@@ -35,7 +35,6 @@
 import com.intellij.psi.xml.XmlAttribute;
 import com.intellij.psi.xml.XmlAttributeValue;
 import com.intellij.psi.xml.XmlTag;
-import com.intellij.refactoring.rename.RenameProcessor;
 import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper;
 import org.jetbrains.android.util.AndroidResourceUtil;
 import org.jetbrains.annotations.NotNull;
@@ -258,36 +257,6 @@
     }
   }
 
-  /**
-   * Uses refactor to change the name of the themes in all the xml files
-   * The theme needs to be reloaded in ThemeEditorComponent for the change to be complete
-   * THIS METHOD DOES NOT DIRECTLY MODIFY THE VALUE ONE GETS WHEN EVALUATING getName()
-   */
-  public void setName(@NotNull final String newName) {
-    if (!isProjectStyle()) {
-      throw new UnsupportedOperationException("Non project styles can not be modified");
-    }
-
-    if (newName.equals(getSimpleName())) {
-      return;
-    }
-
-    for (XmlTag sourceXml : getSourceXmls()) {
-      final XmlAttribute nameAttribute = sourceXml.getAttribute("name");
-      if (nameAttribute == null) {
-        return;
-      }
-
-      final XmlAttributeValue attributeValue = nameAttribute.getValueElement();
-      if (attributeValue == null) {
-        return;
-      }
-
-      RenameProcessor processor = new RenameProcessor(myProject, new ValueResourceElementWrapper(attributeValue), newName, false, false);
-      processor.run();
-    }
-  }
-
   @NotNull
   public StyleResolver getResolver() {
     return myThemeResolver;
@@ -342,4 +311,27 @@
     }
   }
 
+  /**
+   * Returns a PsiElement of the name attribute for this theme
+   * made from a RANDOM sourceXml
+   */
+  @Nullable
+  public PsiElement getNamePsiElement() {
+    List<XmlTag> sourceXmls = getSourceXmls();
+    if (sourceXmls.isEmpty()){
+      return null;
+    }
+    // Any sourceXml will do to get the name attribute from
+    final XmlAttribute nameAttribute = sourceXmls.get(0).getAttribute("name");
+    if (nameAttribute == null) {
+      return null;
+    }
+
+    XmlAttributeValue attributeValue = nameAttribute.getValueElement();
+    if (attributeValue == null) {
+      return null;
+    }
+
+    return new ValueResourceElementWrapper(attributeValue);
+  }
 }
diff --git a/android/testSrc/com/android/tools/idea/editors/theme/ThemeNameChangeTest.java b/android/testSrc/com/android/tools/idea/editors/theme/ThemeNameChangeTest.java
deleted file mode 100644
index 90d51bd..0000000
--- a/android/testSrc/com/android/tools/idea/editors/theme/ThemeNameChangeTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tools.idea.editors.theme;
-
-
-import com.android.tools.idea.configurations.Configuration;
-import com.android.tools.idea.configurations.ConfigurationManager;
-import com.android.tools.idea.editors.theme.datamodels.ThemeEditorStyle;
-import com.intellij.openapi.vfs.VirtualFile;
-import org.jetbrains.android.AndroidTestCase;
-
-import java.util.Collection;
-
-/**
- * Tests the theme name changing feature in the Theme Editor
- */
-public class ThemeNameChangeTest extends AndroidTestCase {
-
-  /**
-   * Tests that changing the name of a theme propagates to that theme's children
-   */
-  public void testThemeNameChange() {
-    VirtualFile myLayout = myFixture.copyFileToProject("themeEditor/layout.xml", "res/layout/layout.xml");
-    myFixture.copyFileToProject("themeEditor/themeNameChangeStyles.xml", "res/values/styles.xml");
-
-    ConfigurationManager configurationManager = myFacet.getConfigurationManager();
-    Configuration configuration = configurationManager.getConfiguration(myLayout);
-    ThemeResolver themeResolver = new ThemeResolver(configuration);
-    ThemeEditorStyle theme = themeResolver.getTheme("@style/TestTheme");
-
-    assertNotNull(theme);
-    theme.setName("NewName");
-
-    configuration.setTheme(null);
-    themeResolver = new ThemeResolver(configuration);
-
-    // Checks the theme with the old name does not exist anymore
-    theme = themeResolver.getTheme("@style/TestTheme");
-    assertNull(theme);
-
-    // Checks the theme with the new name exists
-    theme = themeResolver.getTheme("@style/NewName");
-    assertNotNull(theme);
-    assertTrue(themeResolver.isTheme(theme));
-
-    // Checks the children of the theme have been updated
-    ThemeEditorStyle childTheme = themeResolver.getTheme("@style/NewChildTheme");
-    assertNotNull(childTheme);
-    assertTrue(theme.equals(childTheme.getParent()));
-
-    // Checks that the renaming correctly handles the Parent.Child naming convention
-    childTheme = themeResolver.getTheme("@style/TestTheme.Child");
-    assertNull(childTheme);
-    childTheme = themeResolver.getTheme("@style/NewName.Child");
-    assertNotNull(childTheme);
-
-    childTheme = themeResolver.getTheme("@style/TestTheme.OtherChild");
-    // The name of this child theme is not modified
-    // Because it explicitly specifies its parent in the xml file
-    // So the Parent.Child convention is ignored in that case
-    assertNotNull(childTheme);
-    assertTrue(theme.equals(childTheme.getParent()));
-  }
-
-  /**
-   * Tests that nothing happens when changing a theme's name to its current name
-   */
-  public void testNoChange() {
-    VirtualFile myLayout = myFixture.copyFileToProject("themeEditor/layout.xml", "res/layout/layout.xml");
-    myFixture.copyFileToProject("themeEditor/themeNameChangeStyles.xml", "res/values/styles.xml");
-
-    ConfigurationManager configurationManager = myFacet.getConfigurationManager();
-    Configuration configuration = configurationManager.getConfiguration(myLayout);
-    ThemeResolver themeResolver = new ThemeResolver(configuration);
-    ThemeEditorStyle theme = themeResolver.getTheme("@style/TestTheme");
-
-    assertNotNull(theme);
-    theme.setName("TestTheme");
-
-    configuration.setTheme(null);
-    ThemeResolver newThemeResolver = new ThemeResolver(configuration);
-    Collection<ThemeEditorStyle> oldThemes = themeResolver.getThemes();
-    Collection<ThemeEditorStyle> newThemes = newThemeResolver.getThemes();
-    assertTrue(oldThemes.size() == newThemes.size());
-    for (ThemeEditorStyle oldTheme : oldThemes) {
-      assertTrue(newThemes.contains(oldTheme));
-    }
-  }
-}