diff --git a/lib/org/hamcrest/annotations.xml b/lib/org/hamcrest/annotations.xml
new file mode 100644
index 000000000000..36d1cf4640d1
--- /dev/null
+++ b/lib/org/hamcrest/annotations.xml
@@ -0,0 +1,11 @@
+
+ -
+
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt b/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt
index 0c8c4dc5e13f..1d3b65cc977f 100644
--- a/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt
+++ b/platform/built-in-server/testSrc/BinaryRequestHandlerTest.kt
@@ -17,6 +17,7 @@ import org.jetbrains.io.ChannelExceptionHandler
import org.jetbrains.io.Decoder
import org.jetbrains.io.MessageDecoder
import org.jetbrains.io.NettyUtil
+import org.jetbrains.testFramework.FixtureRule
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
diff --git a/platform/built-in-server/testSrc/RestApiTest.kt b/platform/built-in-server/testSrc/RestApiTest.kt
index 2f9586857102..b4716ebfac5e 100644
--- a/platform/built-in-server/testSrc/RestApiTest.kt
+++ b/platform/built-in-server/testSrc/RestApiTest.kt
@@ -1,19 +1,19 @@
package org.jetbrains.ide
+import com.google.gson.stream.JsonWriter
+import com.intellij.openapi.vfs.CharsetToolkit
import io.netty.handler.codec.http.HttpResponseStatus
+import org.hamcrest.CoreMatchers.equalTo
+import org.jetbrains.ide.TestManager.TestDescriptor
+import org.jetbrains.testFramework.FixtureRule
+import org.junit.Assert.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
-
-import org.hamcrest.CoreMatchers.equalTo
-import org.jetbrains.ide.TestManager.TestDescriptor
-import org.junit.Assert.assertThat
-import java.net.URL
-import java.net.HttpURLConnection
-import com.google.gson.stream.JsonWriter
-import java.io.OutputStreamWriter
-import com.intellij.openapi.vfs.CharsetToolkit
import java.io.BufferedOutputStream
+import java.io.OutputStreamWriter
+import java.net.HttpURLConnection
+import java.net.URL
public class RestApiTest {
private val fixtureManager = FixtureRule()
diff --git a/platform/built-in-server/testSrc/TestManager.kt b/platform/built-in-server/testSrc/TestManager.kt
index 50fa6cd3fe84..be3fc2ef57e3 100644
--- a/platform/built-in-server/testSrc/TestManager.kt
+++ b/platform/built-in-server/testSrc/TestManager.kt
@@ -1,5 +1,7 @@
package org.jetbrains.ide
+import com.intellij.openapi.application.invokeAndWaitIfNeed
+import com.intellij.openapi.application.writeAction
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.util.io.FileUtilRt
@@ -63,10 +65,10 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() {
}
invokeAndWaitIfNeed {
- val normalizedFilePath = FileUtilRt.toSystemIndependentName(filePath)
+ val normalizedFilePath = FileUtilRt.toSystemIndependentName(filePath!!)
if (annotation!!.relativeToProject) {
val root = projectFixture.getProject().getBaseDir()
- runWriteAction {
+ writeAction {
fileToDelete = root.findOrCreateChildData(this@TestManager, normalizedFilePath)
}
}
@@ -75,7 +77,7 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() {
ModuleRootModificationUtil.updateModel(projectFixture.getModule()) { model ->
val contentEntry = model.getContentEntries()[0]
val contentRoot = contentEntry.getFile()!!
- runWriteAction {
+ writeAction {
contentRoot.findChild(EXCLUDED_DIR_NAME)?.delete(this@TestManager)
fileToDelete = contentRoot.createChildDirectory(this@TestManager, EXCLUDED_DIR_NAME)
fileToDelete!!.createChildData(this@TestManager, normalizedFilePath)
@@ -87,7 +89,7 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() {
}
else {
val root = ModuleRootManager.getInstance(projectFixture.getModule()).getSourceRoots()[0]
- runWriteAction {
+ writeAction {
fileToDelete = root.findOrCreateChildData(this@TestManager, normalizedFilePath)
}
}
@@ -101,11 +103,11 @@ class TestManager(val projectFixture: IdeaProjectTestFixture) : TestWatcher() {
}
if (fileToDelete != null) {
- invokeAndWaitIfNeed { runWriteAction { fileToDelete?.delete(this@TestManager) } }
+ invokeAndWaitIfNeed { writeAction { fileToDelete?.delete(this@TestManager) } }
fileToDelete = null
}
- if (ioFileToDelete != null && !FileUtilRt.delete(ioFileToDelete)) {
+ if (ioFileToDelete != null && !FileUtilRt.delete(ioFileToDelete!!)) {
ioFileToDelete!!.deleteOnExit()
}
diff --git a/platform/core-api/src/com/intellij/openapi/options/SchemesManager.java b/platform/core-api/src/com/intellij/openapi/options/SchemesManager.java
index 88f6e37ce838..28844ab00844 100644
--- a/platform/core-api/src/com/intellij/openapi/options/SchemesManager.java
+++ b/platform/core-api/src/com/intellij/openapi/options/SchemesManager.java
@@ -46,8 +46,6 @@ public abstract class SchemesManager convertor) {
}
@@ -69,4 +70,11 @@ public abstract class SchemesManager schemes, @Nullable Condition removeCondition) {
}
+
+ /**
+ * Bundled / read-only (or overriding) scheme cannot be renamed or deleted.
+ */
+ public boolean isMetadataEditable(@NotNull E scheme) {
+ return true;
+ }
}
diff --git a/platform/core-impl/core-impl.iml b/platform/core-impl/core-impl.iml
index 6d93fd99a32c..e57a2b6e112a 100644
--- a/platform/core-impl/core-impl.iml
+++ b/platform/core-impl/core-impl.iml
@@ -11,5 +11,6 @@
+
\ No newline at end of file
diff --git a/platform/core-impl/src/com/intellij/openapi/application/actions.kt b/platform/core-impl/src/com/intellij/openapi/application/actions.kt
new file mode 100644
index 000000000000..4be236085b50
--- /dev/null
+++ b/platform/core-impl/src/com/intellij/openapi/application/actions.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * 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.intellij.openapi.application
+
+import javax.swing.SwingUtilities
+
+public inline fun writeAction(runnable: () -> Unit) {
+ val token = WriteAction.start()
+ try {
+ runnable()
+ }
+ finally {
+ token.finish()
+ }
+}
+
+public fun invokeAndWaitIfNeed(runnable: () -> Unit) {
+ val app = ApplicationManager.getApplication()
+ if (app == null) {
+ if (SwingUtilities.isEventDispatchThread()) runnable() else SwingUtilities.invokeAndWait(runnable)
+ }
+ else {
+ if (app.isDispatchThread()) runnable() else app.invokeAndWait(runnable, ModalityState.any())
+ }
+}
diff --git a/platform/core-impl/src/com/intellij/openapi/options/EmptySchemesManager.java b/platform/core-impl/src/com/intellij/openapi/options/EmptySchemesManager.java
index 55a74d08ebd1..f5bc6cee0c35 100644
--- a/platform/core-impl/src/com/intellij/openapi/options/EmptySchemesManager.java
+++ b/platform/core-impl/src/com/intellij/openapi/options/EmptySchemesManager.java
@@ -48,10 +48,6 @@ public class EmptySchemesManager extends SchemesManager {
return null;
}
- @Override
- public void save() {
- }
-
@Override
public void setCurrentSchemeName(String schemeName) {
}
diff --git a/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java b/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java
index 92329b428aa3..996ae90ea7b4 100644
--- a/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java
+++ b/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java
@@ -25,7 +25,7 @@ import java.util.Collection;
* @author traff
*/
public abstract class AbstractCollectionComboBoxModel extends CollectionListModel implements ComboBoxModel {
- private T mySelection;
+ protected T mySelection;
public AbstractCollectionComboBoxModel(@Nullable T selection) {
mySelection = selection;
diff --git a/platform/platform-impl/platform-impl.iml b/platform/platform-impl/platform-impl.iml
index 64dc847f675a..b6c974e918c2 100644
--- a/platform/platform-impl/platform-impl.iml
+++ b/platform/platform-impl/platform-impl.iml
@@ -42,5 +42,6 @@
+
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java
index 8f8b0789f240..7cd47436a47b 100644
--- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java
+++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java
@@ -43,11 +43,11 @@ public class QuickListsManager implements ExportableApplicationComponent {
private static final String LIST_TAG = "list";
private final ActionManager myActionManager;
- private final SchemesManager mySchemesManager;
+ private final SchemesManager mySchemeManager;
public QuickListsManager(@NotNull ActionManager actionManager, @NotNull SchemesManagerFactory schemesManagerFactory) {
myActionManager = actionManager;
- mySchemesManager = schemesManagerFactory.createSchemesManager(FILE_SPEC,
+ mySchemeManager = schemesManagerFactory.createSchemesManager(FILE_SPEC,
new BaseSchemeProcessor() {
@NotNull
@Override
@@ -73,7 +73,7 @@ public class QuickListsManager implements ExportableApplicationComponent {
@Override
@NotNull
public File[] getExportFiles() {
- return new File[]{mySchemesManager.getRootDirectory()};
+ return new File[]{mySchemeManager.getRootDirectory()};
}
@NotNull
@@ -99,7 +99,7 @@ public class QuickListsManager implements ExportableApplicationComponent {
public void initComponent() {
for (BundledQuickListsProvider provider : BundledQuickListsProvider.EP_NAME.getExtensions()) {
for (final String path : provider.getBundledListsRelativePaths()) {
- mySchemesManager.loadBundledScheme(path, provider, new ThrowableConvertor() {
+ mySchemeManager.loadBundledScheme(path, provider, new ThrowableConvertor() {
@Override
public QuickList convert(Element element) throws Throwable {
return createItem(element);
@@ -107,7 +107,7 @@ public class QuickListsManager implements ExportableApplicationComponent {
});
}
}
- mySchemesManager.loadSchemes();
+ mySchemeManager.loadSchemes();
registerActions();
}
@@ -116,20 +116,20 @@ public class QuickListsManager implements ExportableApplicationComponent {
}
@NotNull
- public List getQuickLists() {
- return mySchemesManager.getAllSchemes();
+ public SchemesManager getSchemeManager() {
+ return mySchemeManager;
}
@NotNull
public QuickList[] getAllQuickLists() {
- Collection lists = mySchemesManager.getAllSchemes();
+ Collection lists = mySchemeManager.getAllSchemes();
return lists.toArray(new QuickList[lists.size()]);
}
private void registerActions() {
// to prevent exception if 2 or more targets have the same name
Set registeredIds = new THashSet();
- for (QuickList list : mySchemesManager.getAllSchemes()) {
+ for (QuickList list : mySchemeManager.getAllSchemes()) {
String actionId = list.getActionId();
if (registeredIds.add(actionId)) {
myActionManager.registerAction(actionId, new InvokeQuickListAction(list));
@@ -145,7 +145,7 @@ public class QuickListsManager implements ExportableApplicationComponent {
public void setQuickLists(@NotNull List quickLists) {
unregisterActions();
- mySchemesManager.setSchemes(quickLists);
+ mySchemeManager.setSchemes(quickLists);
registerActions();
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java
deleted file mode 100644
index 4b455372bbf1..000000000000
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * 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.intellij.openapi.components.impl.stores;
-
-import com.intellij.openapi.components.RoamingType;
-import com.intellij.openapi.util.Condition;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collection;
-import java.util.Collections;
-
-public abstract class StreamProvider {
- public static final StreamProvider[] EMPTY_ARRAY = new StreamProvider[0];
-
- public boolean isEnabled() {
- return true;
- }
-
- /**
- * fileSpec Only main fileSpec, not version
- */
- public boolean isApplicable(@NotNull String fileSpec, @NotNull RoamingType roamingType) {
- return true;
- }
-
- /**
- * @param fileSpec
- * @param content bytes of content, size of array is not actual size of data, you must use {@code size}
- * @param size actual size of data
- */
- public abstract void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType) throws IOException;
-
- @Nullable
- public abstract InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException;
-
- @NotNull
- public Collection listSubFiles(@NotNull String fileSpec, @NotNull RoamingType roamingType) {
- return Collections.emptyList();
- }
-
- /**
- * You must close passed input stream.
- */
- public void processChildren(@NotNull String path, @NotNull RoamingType roamingType, @NotNull Condition filter, @NotNull ChildrenProcessor processor) {
- for (String name : listSubFiles(path, roamingType)) {
- if (!filter.value(name)) {
- continue;
- }
-
- InputStream input;
- try {
- input = loadContent(path + '/' + name, roamingType);
- }
- catch (IOException e) {
- StorageUtil.LOG.error(e);
- continue;
- }
-
- if (input != null && !processor.process(name, input)) {
- break;
- }
- }
- }
-
- public abstract static class ChildrenProcessor {
- public abstract boolean process(@NotNull String name, @NotNull InputStream input);
- }
-
- /**
- * Delete file or directory
- */
- public abstract void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType);
-}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt
new file mode 100644
index 000000000000..df166e62a043
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * 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.intellij.openapi.components.impl.stores
+
+import com.intellij.openapi.components.RoamingType
+import java.io.IOException
+import java.io.InputStream
+
+public interface StreamProvider {
+ public open fun isEnabled(): Boolean = true
+
+ /**
+ * fileSpec Only main fileSpec, not version
+ */
+ public open fun isApplicable(fileSpec: String, roamingType: RoamingType): Boolean = true
+
+ /**
+ * @param fileSpec
+ * *
+ * @param content bytes of content, size of array is not actual size of data, you must use `size`
+ * *
+ * @param size actual size of data
+ */
+ public fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType)
+
+ public fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream?
+
+ public open fun listSubFiles(fileSpec: String, roamingType: RoamingType): Collection = emptyList()
+
+ /**
+ * You must close passed input stream.
+ */
+ public open fun processChildren(path: String, roamingType: RoamingType, filter: (name: String) -> Boolean, processor: (name: String, input: InputStream, readOnly: Boolean) -> Boolean) {
+ for (name in listSubFiles(path, roamingType)) {
+ if (!filter(name)) {
+ continue
+ }
+
+ val input: InputStream?
+ try {
+ input = loadContent("$path/$name", roamingType)
+ }
+ catch (e: IOException) {
+ StorageUtil.LOG.error(e)
+ continue
+ }
+
+
+ if (input != null && !processor(name, input, false)) {
+ break
+ }
+ }
+ }
+
+ /**
+ * Delete file or directory
+ */
+ public fun delete(fileSpec: String, roamingType: RoamingType)
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java
index 20b8af578520..2f69a039db5c 100644
--- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java
@@ -30,7 +30,9 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
+import com.intellij.util.containers.OrderedSet;
import gnu.trove.THashMap;
+import gnu.trove.TObjectObjectProcedure;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -39,7 +41,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
-import java.awt.event.MouseEvent;
import java.lang.reflect.Field;
import java.util.*;
@@ -77,7 +78,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
private KeymapImpl myParent;
private boolean myCanModify = true;
- private final Map> myActionId2ListOfShortcuts = new THashMap>();
+ private final THashMap> myActionId2ListOfShortcuts = new THashMap>();
/**
* Don't use this field directly! Use it only through getKeystroke2ListOfIds.
@@ -134,21 +135,26 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@NotNull
public KeymapImpl copy() {
- KeymapImpl newKeymap = new KeymapImpl();
- return copyTo(newKeymap);
+ return copyTo(new KeymapImpl());
}
@NotNull
- public KeymapImpl copyTo(@NotNull KeymapImpl otherKeymap) {
+ public KeymapImpl copyTo(@NotNull final KeymapImpl otherKeymap) {
otherKeymap.myParent = myParent;
otherKeymap.myName = myName;
otherKeymap.myCanModify = canModify();
otherKeymap.cleanShortcutsCache();
- for (Map.Entry> entry : myActionId2ListOfShortcuts.entrySet()) {
- otherKeymap.myActionId2ListOfShortcuts.put(entry.getKey(), new LinkedHashSet(entry.getValue()));
- }
+ otherKeymap.myActionId2ListOfShortcuts.clear();
+ otherKeymap.myActionId2ListOfShortcuts.ensureCapacity(myActionId2ListOfShortcuts.size());
+ myActionId2ListOfShortcuts.forEachEntry(new TObjectObjectProcedure>() {
+ @Override
+ public boolean execute(String actionId, OrderedSet shortcuts) {
+ otherKeymap.myActionId2ListOfShortcuts.put(actionId, new OrderedSet(shortcuts));
+ return true;
+ }
+ });
return otherKeymap;
}
@@ -195,9 +201,9 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private void addShortcutSilently(String actionId, Shortcut shortcut, final boolean checkParentShortcut) {
- LinkedHashSet list = myActionId2ListOfShortcuts.get(actionId);
+ OrderedSet list = myActionId2ListOfShortcuts.get(actionId);
if (list == null) {
- list = new LinkedHashSet();
+ list = new OrderedSet();
myActionId2ListOfShortcuts.put(actionId, list);
Shortcut[] boundShortcuts = getBoundShortcuts(actionId);
if (boundShortcuts != null) {
@@ -230,7 +236,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@Override
public void removeShortcut(String actionId, Shortcut toDelete) {
- LinkedHashSet list = myActionId2ListOfShortcuts.get(actionId);
+ OrderedSet list = myActionId2ListOfShortcuts.get(actionId);
if (list != null) {
Iterator it = list.iterator();
while (it.hasNext()) {
@@ -253,7 +259,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
if (inherited != null) {
boolean affected = false;
- LinkedHashSet newShortcuts = new LinkedHashSet(inherited.length);
+ OrderedSet newShortcuts = new OrderedSet(inherited.length);
for (Shortcut eachInherited : inherited) {
if (toDelete.equals(eachInherited)) {
// skip this one
@@ -306,7 +312,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private void addAction2ShortcutsMap(final String actionId, final Map> strokesMap, final Class shortcutClass) {
- LinkedHashSet listOfShortcuts = _getShortcuts(actionId);
+ OrderedSet listOfShortcuts = _getShortcuts(actionId);
for (Shortcut shortcut : listOfShortcuts) {
if (!shortcutClass.isAssignableFrom(shortcut.getClass())) {
continue;
@@ -328,7 +334,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private void addKeystrokesMap(final String actionId, final Map> strokesMap) {
- LinkedHashSet listOfShortcuts = _getShortcuts(actionId);
+ OrderedSet listOfShortcuts = _getShortcuts(actionId);
for (Shortcut shortcut : listOfShortcuts) {
if (!(shortcut instanceof KeyboardShortcut)) {
continue;
@@ -347,14 +353,14 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
}
- private LinkedHashSet _getShortcuts(final String actionId) {
+ private OrderedSet _getShortcuts(final String actionId) {
KeymapManagerEx keymapManager = getKeymapManager();
- LinkedHashSet listOfShortcuts = myActionId2ListOfShortcuts.get(actionId);
+ OrderedSet listOfShortcuts = myActionId2ListOfShortcuts.get(actionId);
if (listOfShortcuts != null) {
return listOfShortcuts;
}
else {
- listOfShortcuts = new LinkedHashSet();
+ listOfShortcuts = new OrderedSet();
}
final String actionBinding = keymapManager.getActionBinding(actionId);
@@ -519,7 +525,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@NotNull
@Override
public Shortcut[] getShortcuts(String actionId) {
- LinkedHashSet shortcuts = myActionId2ListOfShortcuts.get(actionId);
+ OrderedSet shortcuts = myActionId2ListOfShortcuts.get(actionId);
if (shortcuts == null) {
Shortcut[] boundShortcuts = getBoundShortcuts(actionId);
@@ -539,7 +545,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@Nullable
private Shortcut[] getOwnShortcuts(String actionId) {
- LinkedHashSet own = myActionId2ListOfShortcuts.get(actionId);
+ OrderedSet own = myActionId2ListOfShortcuts.get(actionId);
if (own == null) return null;
return own.isEmpty() ? ourEmptyShortcutsArray : own.toArray(new Shortcut[own.size()]);
}
@@ -685,7 +691,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
// Add read shortcuts
for (String id : id2shortcuts.keySet()) {
- myActionId2ListOfShortcuts.put(id, new LinkedHashSet(2)); // It's a trick! After that parent's shortcuts are not added to the keymap
+ myActionId2ListOfShortcuts.put(id, new OrderedSet(2)); // It's a trick! After that parent's shortcuts are not added to the keymap
ArrayList shortcuts = id2shortcuts.get(id);
for (Shortcut shortcut : shortcuts) {
addShortcutSilently(id, shortcut, false);
@@ -711,7 +717,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
for (String actionId : ownActionIds) {
Element actionElement = new Element(ACTION);
actionElement.setAttribute(ID_ATTRIBUTE, actionId);
- // Save keyboad shortcuts
+ // Save keyboard shortcuts
Shortcut[] shortcuts = getShortcuts(actionId);
for (Shortcut shortcut : shortcuts) {
if (shortcut instanceof KeyboardShortcut) {
@@ -805,23 +811,23 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
// modifiers
int modifiers = shortcut.getModifiers();
- if ((MouseEvent.SHIFT_DOWN_MASK & modifiers) > 0) {
+ if ((InputEvent.SHIFT_DOWN_MASK & modifiers) > 0) {
buffer.append(SHIFT);
buffer.append(' ');
}
- if ((MouseEvent.CTRL_DOWN_MASK & modifiers) > 0) {
+ if ((InputEvent.CTRL_DOWN_MASK & modifiers) > 0) {
buffer.append(CONTROL);
buffer.append(' ');
}
- if ((MouseEvent.META_DOWN_MASK & modifiers) > 0) {
+ if ((InputEvent.META_DOWN_MASK & modifiers) > 0) {
buffer.append(META);
buffer.append(' ');
}
- if ((MouseEvent.ALT_DOWN_MASK & modifiers) > 0) {
+ if ((InputEvent.ALT_DOWN_MASK & modifiers) > 0) {
buffer.append(ALT);
buffer.append(' ');
}
- if ((MouseEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) {
+ if ((InputEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) {
buffer.append(ALT_GRAPH);
buffer.append(' ');
}
@@ -849,7 +855,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
cleanShortcutsCache();
}
- public boolean hasOwnActionId(String actionId) {
+ public boolean hasOwnActionId(@NotNull String actionId) {
return myActionId2ListOfShortcuts.containsKey(actionId);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java
index a10e6360d3c9..fdd72694b8bc 100644
--- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java
+++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java
@@ -86,7 +86,6 @@ public class ActionsTree {
if (rowBounds.intersects(clip)) {
Object node = getPathForRow(row).getLastPathComponent();
-
if (node instanceof DefaultMutableTreeNode) {
Object data = ((DefaultMutableTreeNode)node).getUserObject();
Rectangle fullRowRect = new Rectangle(visibleRect.x, rowBounds.y, visibleRect.width, rowBounds.height);
@@ -106,30 +105,20 @@ public class ActionsTree {
@Override
public void mouseMoved(MouseEvent e) {
String description = getDescription(e);
- if (description != null) {
- ActionMenu.showDescriptionInStatusBar(true, myTree, description);
- }
- else {
- ActionMenu.showDescriptionInStatusBar(false, myTree, null);
- }
+ ActionMenu.showDescriptionInStatusBar(description != null, myTree, description);
}
@Nullable
private String getDescription(@NotNull MouseEvent e) {
TreePath path = myTree.getPathForLocation(e.getX(), e.getY());
- if (path == null) return null;
+ DefaultMutableTreeNode node = path == null ? null : (DefaultMutableTreeNode)path.getLastPathComponent();
+ Object userObject = node == null ? null : node.getUserObject();
+ if (!(userObject instanceof String)) {
+ return null;
+ }
- DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
- if (node == null) return null;
-
- Object userObject = node.getUserObject();
- if (!(userObject instanceof String)) return null;
-
- String actionId = (String)userObject;
- AnAction action = ActionManager.getInstance().getActionOrStub(actionId);
- if (action == null) return null;
-
- return action.getTemplatePresentation().getDescription();
+ AnAction action = ActionManager.getInstance().getActionOrStub((String)userObject);
+ return action == null ? null : action.getTemplatePresentation().getDescription();
}
});
@@ -140,6 +129,11 @@ public class ActionsTree {
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
}
+ // silently replace current map
+ void setKeymap(@NotNull Keymap keymap) {
+ myKeymap = keymap;
+ }
+
public JComponent getComponent() {
return myComponent;
}
@@ -171,7 +165,7 @@ public class ActionsTree {
return (QuickList)userObject;
}
- public void reset(Keymap keymap, final QuickList[] allQuickLists) {
+ public void reset(@NotNull Keymap keymap, @NotNull QuickList[] allQuickLists) {
reset(keymap, allQuickLists, myFilter, null);
}
@@ -188,7 +182,7 @@ public class ActionsTree {
reset(myKeymap, currentQuickListIds, filter, null);
}
- private void reset(final Keymap keymap, final QuickList[] allQuickLists, String filter, @Nullable KeyboardShortcut shortcut) {
+ private void reset(@NotNull Keymap keymap, @NotNull QuickList[] allQuickLists, String filter, @Nullable KeyboardShortcut shortcut) {
myKeymap = keymap;
final PathsKeeper pathsKeeper = new PathsKeeper();
@@ -198,11 +192,11 @@ public class ActionsTree {
ActionManager actionManager = ActionManager.getInstance();
Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(myComponent));
- Group mainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, allQuickLists, filter, true,
- ActionsTreeUtil.isActionFiltered(actionManager, myKeymap, shortcut, filter, true));
+ Group mainGroup = ActionsTreeUtil.createMainGroup(project, keymap, allQuickLists, filter, true,
+ ActionsTreeUtil.isActionFiltered(actionManager, keymap, shortcut, filter, true));
if ((filter != null && filter.length() > 0 || shortcut != null) && mainGroup.initIds().isEmpty()){
- mainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, allQuickLists, filter, false,
- ActionsTreeUtil.isActionFiltered(actionManager, myKeymap, shortcut, filter, false));
+ mainGroup = ActionsTreeUtil.createMainGroup(project, keymap, allQuickLists, filter, false,
+ ActionsTreeUtil.isActionFiltered(actionManager, keymap, shortcut, filter, false));
}
myRoot = ActionsTreeUtil.createNode(mainGroup);
myMainGroup = mainGroup;
@@ -251,14 +245,7 @@ public class ActionsTree {
if (userObject instanceof QuickList) {
userObject = ((QuickList)userObject).getActionId();
}
-
- if (userObject instanceof String) {
- Shortcut[] shortcuts = myKeymap.getShortcuts((String)userObject);
- return KeymapUtil.getShortcutsText(shortcuts);
- }
- else {
- return "";
- }
+ return userObject instanceof String ? KeymapUtil.getShortcutsText(myKeymap.getShortcuts((String)userObject)) : "";
}
else {
return "???";
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java
index 0894b0dd7afc..b08cf0fdd087 100644
--- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java
+++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java
@@ -34,6 +34,7 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -214,7 +215,8 @@ public class KeyboardShortcutDialog extends DialogWrapper {
myConflictInfoArea.setForeground(JBColor.RED);
if (loaded) {
myConflictInfoArea.setText(KeyMapBundle.message("assigned.to.info.message", buffer.toString()));
- } else {
+ }
+ else {
myConflictInfoArea.setText("Assigned to " + buffer.toString() + " which is now not loaded but may be loaded later");
}
}
@@ -224,13 +226,10 @@ public class KeyboardShortcutDialog extends DialogWrapper {
mySecondStrokePanel.setEnabled(myEnableSecondKeystroke.isSelected());
}
+ @Nullable
public KeyboardShortcut getKeyboardShortcut() {
KeyStroke firstStroke = myFirstStrokePanel.getKeyStroke();
- if (firstStroke == null) {
- return null;
- }
- KeyStroke secondStroke = myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null;
- return new KeyboardShortcut(firstStroke, secondStroke);
+ return firstStroke == null ? null : new KeyboardShortcut(firstStroke, myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null);
}
static String getTextByKeyStroke(KeyStroke keyStroke) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java
index 36a5af720474..3df7eb33ef35 100644
--- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java
+++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java
@@ -119,8 +119,6 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
});
- private Keymap mySelectedKeymap;
-
private JButton myCopyButton;
private JButton myDeleteButton;
private JButton myResetToDefault;
@@ -146,7 +144,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void propertyChange(@NotNull final PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("ancestor") && evt.getNewValue() != null && evt.getOldValue() == null && myQuickListsModified) {
- processCurrentKeymapChanged(myQuickLists);
+ currentKeymapChanged();
myQuickListsModified = false;
}
}
@@ -185,9 +183,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myEditor.getComboBox().addActionListener(new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
- if (myEditor.getModel().getSelectedItem() != mySelectedKeymap) {
- processCurrentKeymapChanged(myQuickLists);
- }
+ currentKeymapChanged();
}
});
panel.add(createKeymapNamePanel(), new GridBagConstraints(3, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0));
@@ -207,10 +203,13 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void processCurrentKeymapChanged(@NotNull QuickList[] ids) {
myQuickLists = ids;
+ currentKeymapChanged();
+ }
+
+ private void currentKeymapChanged() {
myResetToDefault.setEnabled(false);
Keymap selectedKeymap = myEditor.getModel().getSelected();
- mySelectedKeymap = selectedKeymap;
boolean editable = selectedKeymap != null && selectedKeymap.canModify();
myDeleteButton.setEnabled(editable);
@@ -228,16 +227,12 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
else {
myBaseKeymapLabel.setText(KeyMapBundle.message("based.on.keymap.label", parent.getPresentableName()));
- if (selectedKeymap.canModify() && ((KeymapImpl)mySelectedKeymap).getOwnActionIds().length > 0) {
+ if (selectedKeymap.canModify() && ((KeymapImpl)selectedKeymap).getOwnActionIds().length > 0) {
myResetToDefault.setEnabled(true);
}
}
- resetActionsTree(selectedKeymap);
- }
-
- private void resetActionsTree(@NotNull Keymap keymap) {
- myActionsTree.reset(myEditor.getMutable(keymap), myQuickLists);
+ myActionsTree.reset(selectedKeymap, myQuickLists);
}
private JPanel createKeymapButtonsPanel() {
@@ -450,7 +445,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
private JPanel createFilteringPanel() {
//noinspection ConstantConditions
- resetActionsTree(myEditor.getModel().getSelected());
+ myActionsTree.reset(myEditor.getModel().getSelected(), myQuickLists);
final JLabel firstLabel = new JLabel(KeyMapBundle.message("filter.first.stroke.input"));
final JCheckBox enable2Shortcut = new JCheckBox(KeyMapBundle.message("filter.second.stroke.input"));
@@ -482,12 +477,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
});
IJSwingUtilities.adjustComponentsOnMac(firstLabel, firstShortcut);
- //noinspection deprecation
JPanel filterComponent = FormBuilder.createFormBuilder()
.addLabeledComponent(firstLabel, firstShortcut, true)
.addComponent(enable2Shortcut)
.setVerticalGap(0)
- .setIndent(5)
+ .setHorizontalGap(5)
.addComponent(secondShortcut)
.getPanel();
@@ -525,31 +519,21 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myActionsTree.filter(option, myQuickLists);
}
- private void addKeyboardShortcut(Shortcut shortcut) {
- String actionId = myActionsTree.getSelectedActionId();
- if (actionId == null) {
- return;
- }
-
- if (!createKeymapCopyIfNeeded()) return;
+ private void addKeyboardShortcut(@NotNull String actionId, @Nullable Shortcut shortcut) {
+ Keymap keymap = createKeymapCopyIfNeeded();
KeyboardShortcutDialog dialog = new KeyboardShortcutDialog(this, actionId, myQuickLists);
-
-
- KeyboardShortcut selectedKeyboardShortcut = shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null;
-
- dialog.setData(mySelectedKeymap, selectedKeyboardShortcut);
+ dialog.setData(keymap, shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null);
if (!dialog.showAndGet()) {
return;
}
KeyboardShortcut keyboardShortcut = dialog.getKeyboardShortcut();
-
if (keyboardShortcut == null) {
return;
}
- Map> conflicts = mySelectedKeymap.getConflicts(actionId, keyboardShortcut);
+ Map> conflicts = keymap.getConflicts(actionId, keyboardShortcut);
if (!conflicts.isEmpty()) {
int result = Messages.showYesNoCancelDialog(
this,
@@ -563,7 +547,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
if (result == Messages.YES) {
for (String id : conflicts.keySet()) {
for (KeyboardShortcut s : conflicts.get(id)) {
- mySelectedKeymap.removeShortcut(id, s);
+ keymap.removeShortcut(id, s);
}
}
}
@@ -573,20 +557,20 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
// if shortcut is already registered to this action, just select it in the list
- Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
+ Shortcut[] shortcuts = keymap.getShortcuts(actionId);
for (Shortcut s : shortcuts) {
if (s.equals(keyboardShortcut)) {
return;
}
}
- mySelectedKeymap.addShortcut(actionId, keyboardShortcut);
+ keymap.addShortcut(actionId, keyboardShortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
- mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut);
+ keymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut);
}
repaintLists();
- processCurrentKeymapChanged(myQuickLists);
+ currentKeymapChanged();
}
private void addMouseShortcut(Shortcut shortcut, ShortcutRestrictions restrictions) {
@@ -595,14 +579,14 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
- if (!createKeymapCopyIfNeeded()) return;
+ Keymap keymap = createKeymapCopyIfNeeded();
MouseShortcut mouseShortcut = shortcut instanceof MouseShortcut ? (MouseShortcut)shortcut : null;
MouseShortcutDialog dialog = new MouseShortcutDialog(
this,
mouseShortcut,
- mySelectedKeymap,
+ keymap,
actionId,
myActionsTree.getMainGroup(),
restrictions
@@ -617,7 +601,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
- String[] actionIds = mySelectedKeymap.getActionIds(mouseShortcut);
+ String[] actionIds = keymap.getActionIds(mouseShortcut);
if (actionIds.length > 1 || (actionIds.length == 1 && !actionId.equals(actionIds[0]))) {
int result = Messages.showYesNoCancelDialog(
this,
@@ -630,7 +614,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
if (result == Messages.YES) {
for (String id : actionIds) {
- mySelectedKeymap.removeShortcut(id, mouseShortcut);
+ keymap.removeShortcut(id, mouseShortcut);
}
}
else if (result != Messages.NO) {
@@ -640,50 +624,51 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
// if shortcut is already registered to this action, just select it in the list
- Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
+ Shortcut[] shortcuts = keymap.getShortcuts(actionId);
for (Shortcut shortcut1 : shortcuts) {
if (shortcut1.equals(mouseShortcut)) {
return;
}
}
- mySelectedKeymap.addShortcut(actionId, mouseShortcut);
+ keymap.addShortcut(actionId, mouseShortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
- mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut);
+ keymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut);
}
repaintLists();
- processCurrentKeymapChanged(myQuickLists);
+ currentKeymapChanged();
}
private void repaintLists() {
myActionsTree.getComponent().repaint();
}
- private boolean createKeymapCopyIfNeeded() {
- if (mySelectedKeymap.canModify()) return true;
-
- final Keymap selectedKeymap = myEditor.getModel().getSelected();
- if (selectedKeymap == null) {
- return false;
+ @NotNull
+ private Keymap createKeymapCopyIfNeeded() {
+ Keymap keymap = myEditor.getModel().getSelected();
+ assert keymap != null;
+ if (keymap.canModify()) {
+ Keymap mutable = myEditor.getMutable(keymap);
+ myActionsTree.setKeymap(mutable);
+ return mutable;
}
- KeymapImpl newKeymap = ((KeymapImpl)selectedKeymap).deriveKeymap();
-
- String newKeymapName = KeyMapBundle.message("new.keymap.name", selectedKeymap.getPresentableName());
+ String newKeymapName = KeyMapBundle.message("new.keymap.name", keymap.getPresentableName());
if (!tryNewKeymapName(newKeymapName)) {
for (int i = 0; ; i++) {
- newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", selectedKeymap.getPresentableName(), i);
+ newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", keymap.getPresentableName(), i);
if (tryNewKeymapName(newKeymapName)) {
break;
}
}
}
+ KeymapImpl newKeymap = ((KeymapImpl)keymap).deriveKeymap();
newKeymap.setName(newKeymapName);
newKeymap.setCanModify(true);
- final int indexOf = myEditor.getModel().getElementIndex(selectedKeymap);
+ int indexOf = myEditor.getModel().getElementIndex(keymap);
if (indexOf >= 0) {
myEditor.getModel().add(indexOf + 1, newKeymap);
}
@@ -692,28 +677,8 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
myEditor.getModel().setSelectedItem(newKeymap);
- processCurrentKeymapChanged(myQuickLists);
-
- return true;
- }
-
- private void removeShortcut(Shortcut shortcut) {
- String actionId = myActionsTree.getSelectedActionId();
- if (actionId == null) {
- return;
- }
-
- if (!createKeymapCopyIfNeeded()) return;
-
- if (shortcut == null) return;
-
- mySelectedKeymap.removeShortcut(actionId, shortcut);
- if (StringUtil.startsWithChar(actionId, '$')) {
- mySelectedKeymap.removeShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), shortcut);
- }
-
- repaintLists();
- processCurrentKeymapChanged(myQuickLists);
+ currentKeymapChanged();
+ return newKeymap;
}
private void copyKeymap() {
@@ -738,7 +703,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myEditor.getModel().add(newKeymap);
myEditor.getModel().setSelectedItem(newKeymap);
myEditor.getComboBox().getEditor().selectAll();
- processCurrentKeymapChanged(myQuickLists);
+ currentKeymapChanged();
}
private boolean tryNewKeymapName(String name) {
@@ -759,7 +724,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
myEditor.getModel().remove(keymap);
- processCurrentKeymapChanged(myQuickLists);
+ currentKeymapChanged();
}
private void resetKeymap() {
@@ -768,7 +733,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
((KeymapImpl)keymap).clearOwnActionsIds();
- processCurrentKeymapChanged(myQuickLists);
+ currentKeymapChanged();
}
@Override
@@ -784,11 +749,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myNonEnglishKeyboardSupportOption.setSelected(KeyboardSettingsExternalizable.getInstance().isNonEnglishKeyboardSupportEnabled());
}
- mySelectedKeymap = null;
+ Keymap selectedKeymap = null;
List list = getManagerKeymaps();
for (Keymap keymap : list) {
- if (mySelectedKeymap == null && keymap == KeymapManagerEx.getInstanceEx().getActiveKeymap()) {
- mySelectedKeymap = keymap;
+ if (selectedKeymap == null && keymap == KeymapManagerEx.getInstanceEx().getActiveKeymap()) {
+ selectedKeymap = keymap;
}
}
myEditor.reset(list);
@@ -797,23 +762,23 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
KeymapImpl keymap = new KeymapImpl();
keymap.setName(KeyMapBundle.message("keymap.no.name"));
myEditor.getModel().add(keymap);
- mySelectedKeymap = keymap;
+ selectedKeymap = keymap;
}
- myEditor.getModel().setSelectedItem(mySelectedKeymap);
+ myEditor.getModel().setSelectedItem(selectedKeymap);
- processCurrentKeymapChanged(myQuickLists);
+ currentKeymapChanged();
}
@Override
public void apply() throws ConfigurationException {
- myEditor.ensureNonEmptyNames("Quick list should have non empty name");
+ myEditor.ensureNonEmptyNames(KeyMapBundle.message("configuration.all.keymaps.should.have.non.empty.names.error.message"));
ensureUniqueKeymapNames();
KeymapManagerImpl keymapManager = (KeymapManagerImpl)KeymapManager.getInstance();
// we must specify the same filter, which was used to get original items
keymapManager.setKeymaps(myEditor.apply(), KEYMAP_FILTER);
- keymapManager.setActiveKeymap(mySelectedKeymap);
+ keymapManager.setActiveKeymap(myEditor.getModel().getSelected());
ActionToolbarImpl.updateAllToolbarsImmediately();
}
@@ -828,7 +793,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public boolean isModified() {
- return !Comparing.equal(mySelectedKeymap, KeymapManager.getInstance().getActiveKeymap()) || myEditor.isModified();
+ return !Comparing.equal(myEditor.getModel().getSelected(), KeymapManager.getInstance().getActiveKeymap()) || myEditor.isModified();
}
@NotNull
@@ -873,34 +838,59 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Nullable
public Shortcut[] getCurrentShortcuts(@NotNull String actionId) {
- return mySelectedKeymap == null ? null : mySelectedKeymap.getShortcuts(actionId);
+ Keymap keymap = myEditor.getModel().getSelected();
+ return keymap == null ? null : keymap.getShortcuts(actionId);
}
private void editSelection(InputEvent e) {
- final String actionId = myActionsTree.getSelectedActionId();
- if (actionId == null) return;
+ String actionId = myActionsTree.getSelectedActionId();
+ if (actionId == null) {
+ return;
+ }
+ DefaultActionGroup group = createEditActionGroup(actionId);
+ if (e instanceof MouseEvent && ((MouseEvent)e).isPopupTrigger()) {
+ ActionManager.getInstance()
+ .createActionPopupMenu(ActionPlaces.UNKNOWN, group)
+ .getComponent()
+ .show(e.getComponent(), ((MouseEvent)e).getX(), ((MouseEvent)e).getY());
+ }
+ else {
+ DataContext dataContext = DataManager.getInstance().getDataContext(this);
+ ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup("Edit Shortcuts",
+ group,
+ dataContext,
+ JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,
+ true);
+
+ if (e instanceof MouseEvent) {
+ popup.show(new RelativePoint((MouseEvent)e));
+ }
+ else {
+ popup.showInBestPositionFor(dataContext);
+ }
+ }
+ }
+
+ @NotNull
+ private DefaultActionGroup createEditActionGroup(@NotNull final String actionId) {
DefaultActionGroup group = new DefaultActionGroup();
-
- final Shortcut[] shortcuts = getCurrentShortcuts(actionId);
- final Set abbreviations = AbbreviationManager.getInstance().getAbbreviations(actionId);
-
final ShortcutRestrictions restrictions = ActionShortcutRestrictions.getInstance().getForActionId(actionId);
-
if (restrictions.allowKeyboardShortcut) {
group.add(new DumbAwareAction("Add Keyboard Shortcut") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
- Shortcut firstKeyboard = null;
- assert shortcuts != null;
- for (Shortcut shortcut : shortcuts) {
+ Shortcut firstShortcut = null;
+ Keymap keymap = myEditor.getModel().getSelected();
+ assert keymap != null;
+ for (Shortcut shortcut : keymap.getShortcuts(actionId)) {
if (shortcut instanceof KeyboardShortcut) {
- firstKeyboard = shortcut;
+ firstShortcut = shortcut;
break;
}
}
- addKeyboardShortcut(firstKeyboard);
+ addKeyboardShortcut(actionId, firstShortcut);
}
});
}
@@ -910,8 +900,9 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Shortcut firstMouse = null;
- assert shortcuts != null;
- for (Shortcut shortcut : shortcuts) {
+ Keymap keymap = myEditor.getModel().getSelected();
+ assert keymap != null;
+ for (Shortcut shortcut : keymap.getShortcuts(actionId)) {
if (shortcut instanceof MouseShortcut) {
firstMouse = shortcut;
break;
@@ -926,10 +917,9 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.add(new DumbAwareAction("Add Abbreviation") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
- final String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null);
+ String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null);
if (abbr != null) {
- String actionId = myActionsTree.getSelectedActionId();
- AbbreviationManager.getInstance().register(abbr, actionId);
+ AbbreviationManager.getInstance().register(abbr, myActionsTree.getSelectedActionId());
repaintLists();
}
}
@@ -943,29 +933,32 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.addSeparator();
- assert shortcuts != null;
- for (final Shortcut shortcut : shortcuts) {
+ Keymap keymap = myEditor.getModel().getSelected();
+ assert keymap != null;
+ for (final Shortcut shortcut : keymap.getShortcuts(actionId)) {
group.add(new DumbAwareAction("Remove " + KeymapUtil.getShortcutText(shortcut)) {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
- removeShortcut(shortcut);
+ Keymap keymap = createKeymapCopyIfNeeded();
+ keymap.removeShortcut(actionId, shortcut);
+ if (StringUtil.startsWithChar(actionId, '$')) {
+ keymap.removeShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), shortcut);
+ }
+
+ repaintLists();
+ currentKeymapChanged();
}
});
}
if (Registry.is("actionSystem.enableAbbreviations")) {
- for (final String abbreviation : abbreviations) {
+ for (final String abbreviation : AbbreviationManager.getInstance().getAbbreviations(actionId)) {
group.addAction(new DumbAwareAction("Remove Abbreviation '" + abbreviation + "'") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
AbbreviationManager.getInstance().remove(abbreviation, actionId);
repaintLists();
}
-
- @Override
- public void update(@NotNull AnActionEvent e) {
- super.update(e);
- }
});
}
}
@@ -973,37 +966,17 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.add(new DumbAwareAction("Reset Shortcuts") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
- ((KeymapImpl)mySelectedKeymap).clearOwnActionsId(actionId);
- processCurrentKeymapChanged(myQuickLists);
+ ((KeymapImpl)createKeymapCopyIfNeeded()).clearOwnActionsId(actionId);
+ currentKeymapChanged();
repaintLists();
}
@Override
public void update(@NotNull AnActionEvent e) {
- e.getPresentation().setVisible(mySelectedKeymap.canModify() && ((KeymapImpl)mySelectedKeymap).hasOwnActionId(actionId));
- super.update(e);
+ e.getPresentation().setVisible(((KeymapImpl)myEditor.getModel().getSelected()).hasOwnActionId(actionId));
}
});
- if (e instanceof MouseEvent && ((MouseEvent)e).isPopupTrigger()) {
- final ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group);
- popupMenu.getComponent().show(e.getComponent(), ((MouseEvent)e).getX(), ((MouseEvent)e).getY());
- }
- else {
- final DataContext dataContext = DataManager.getInstance().getDataContext(this);
- final ListPopup popup = JBPopupFactory.getInstance()
- .createActionGroupPopup("Edit Shortcuts",
- group,
- dataContext,
- JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,
- true);
-
- if (e instanceof MouseEvent) {
- popup.show(new RelativePoint((MouseEvent)e));
- }
- else {
- popup.showInBestPositionFor(dataContext);
- }
- }
+ return group;
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.form b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.form
index bb2584ca890d..b19432465dff 100644
--- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.form
+++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.form
@@ -32,7 +32,7 @@
-
+
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.java
index 666f9e74b657..27d2e6732741 100644
--- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.java
+++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/QuickListPanel.java
@@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ex.QuickList;
+import com.intellij.openapi.actionSystem.ex.QuickListsManager;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.ui.*;
@@ -36,7 +37,7 @@ class QuickListPanel {
private final CollectionListModel
\ No newline at end of file
diff --git a/platform/platform-tests/testData/options/different-extensions/1.icls b/platform/platform-tests/testData/options/different-extensions/1.icls
new file mode 100644
index 000000000000..2b2964d3870c
--- /dev/null
+++ b/platform/platform-tests/testData/options/different-extensions/1.icls
@@ -0,0 +1 @@
+
diff --git a/platform/platform-tests/testData/options/different-extensions/1.xml b/platform/platform-tests/testData/options/different-extensions/1.xml
new file mode 100644
index 000000000000..22a3a0bd0441
--- /dev/null
+++ b/platform/platform-tests/testData/options/different-extensions/1.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/platform/platform-tests/testData/options/options/1.xml b/platform/platform-tests/testData/options/options/1.xml
new file mode 100644
index 000000000000..e4c594a71fec
--- /dev/null
+++ b/platform/platform-tests/testData/options/options/1.xml
@@ -0,0 +1 @@
+
diff --git a/platform/platform-tests/testData/options/options1/1.xml b/platform/platform-tests/testData/options/options1/1.xml
new file mode 100644
index 000000000000..c023fb61fd3d
--- /dev/null
+++ b/platform/platform-tests/testData/options/options1/1.xml
@@ -0,0 +1 @@
+
diff --git a/platform/platform-tests/testData/options/options1/2.xml b/platform/platform-tests/testData/options/options1/2.xml
new file mode 100644
index 000000000000..e5b1786c1ad0
--- /dev/null
+++ b/platform/platform-tests/testData/options/options1/2.xml
@@ -0,0 +1 @@
+
diff --git a/platform/platform-tests/testSrc/bundledSchemes/default.xml b/platform/platform-tests/testSrc/bundledSchemes/default.xml
new file mode 100644
index 000000000000..0cb9dad410e4
--- /dev/null
+++ b/platform/platform-tests/testSrc/bundledSchemes/default.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java
deleted file mode 100644
index 5a47efde5c13..000000000000
--- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * 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.intellij.openapi.components.impl;
-
-import com.intellij.application.options.PathMacrosImpl;
-import com.intellij.openapi.Disposable;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.components.*;
-import com.intellij.openapi.components.impl.stores.*;
-import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.CharsetToolkit;
-import com.intellij.testFramework.LightPlatformTestCase;
-import com.intellij.util.messages.MessageBus;
-import com.intellij.util.xmlb.XmlSerializerUtil;
-import gnu.trove.THashMap;
-import org.intellij.lang.annotations.Language;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-public class ApplicationStoreTest extends LightPlatformTestCase {
- private File testAppConfig;
- private MyComponentStore componentStore;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
-
- String testAppConfigPath = System.getProperty("test.app.config.path");
- if (testAppConfigPath == null) {
- testAppConfig = FileUtil.createTempDirectory("testAppSettings", null);
- }
- else {
- testAppConfig = new File(FileUtil.expandUserHome(testAppConfigPath));
- }
- FileUtil.delete(testAppConfig);
-
- componentStore = new MyComponentStore(testAppConfig.getAbsolutePath());
- }
-
- @Override
- public void tearDown() throws Exception {
- try {
- Disposer.dispose(componentStore);
- componentStore = null;
- }
- finally {
- try {
- super.tearDown();
- }
- finally {
- FileUtil.delete(testAppConfig);
- }
- }
- }
-
- public void testStreamProviderSaveIfSeveralStoragesConfigured() throws Throwable {
- SeveralStoragesConfigured component = new SeveralStoragesConfigured();
-
- MyStreamProvider streamProvider = new MyStreamProvider();
- componentStore.getStateStorageManager().setStreamProvider(streamProvider);
-
- componentStore.initComponent(component, false);
- component.foo = "newValue";
- StoreUtil.save(componentStore, null);
-
- assertThat(
- streamProvider.data.get(RoamingType.PER_USER).get(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"),
- equalTo("\n" +
- " \n" +
- " \n" +
- " \n" +
- ""));
- }
-
- public void testLoadFromStreamProvider() throws Exception {
- SeveralStoragesConfigured component = new SeveralStoragesConfigured();
-
- MyStreamProvider streamProvider = new MyStreamProvider();
- THashMap map = new THashMap();
- map.put(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml", "\n" +
- " \n" +
- " \n" +
- " \n" +
- "");
- streamProvider.data.put(RoamingType.PER_USER, map);
-
- componentStore.getStateStorageManager().setStreamProvider(streamProvider);
- componentStore.initComponent(component, false);
- assertThat(component.foo, equalTo("newValue"));
- }
-
- public void testRemoveDeprecatedStorageOnWrite() throws Exception {
- doRemoveDeprecatedStorageOnWrite(new SeveralStoragesConfigured());
- }
-
- public void testRemoveDeprecatedStorageOnWrite2() throws Exception {
- doRemoveDeprecatedStorageOnWrite(new ActualStorageLast());
- }
-
- private void doRemoveDeprecatedStorageOnWrite(@NotNull Foo component) throws IOException {
- File oldFile = saveConfig("other.xml", "" +
- " \n" +
- " \n" +
- " \n" +
- "");
-
- saveConfig("proxy.settings.xml", "\n" +
- " \n" +
- " \n" +
- " \n" +
- "");
-
- componentStore.initComponent(component, false);
- assertThat(component.foo, equalTo("new"));
-
- component.foo = "new2";
- StoreUtil.save(componentStore, null);
-
- assertThat(oldFile.exists(), equalTo(false));
- }
-
- @NotNull
- private File saveConfig(@NotNull String fileName, @Language("XML") String data) throws IOException {
- File file = new File(testAppConfig, fileName);
- FileUtil.writeToFile(file, data);
- return file;
- }
-
- private static class MyStreamProvider extends StreamProvider {
- public final Map> data = new THashMap>();
-
- @Override
- public void saveContent(@NotNull String fileSpec,
- @NotNull byte[] content,
- int size,
- @NotNull RoamingType roamingType) {
- getMap(roamingType).put(fileSpec, new String(content, 0, size, CharsetToolkit.UTF8_CHARSET));
- }
-
- private Map getMap(@NotNull RoamingType roamingType) {
- Map map = data.get(roamingType);
- if (map == null) {
- map = new THashMap();
- data.put(roamingType, map);
- }
- return map;
- }
-
- @Nullable
- @Override
- public InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException {
- String data = getMap(roamingType).get(fileSpec);
- return data == null ? null : new ByteArrayInputStream(data.getBytes(CharsetToolkit.UTF8_CHARSET));
- }
-
- @Override
- public void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType) {
- Map map = data.get(roamingType);
- if (map != null) {
- map.remove(fileSpec);
- }
- }
- }
-
- static class MyComponentStore extends ComponentStoreImpl implements Disposable {
- private final StateStorageManager stateStorageManager;
-
- MyComponentStore(@NotNull final String testAppConfigPath) {
- TrackingPathMacroSubstitutor macroSubstitutor = new ApplicationPathMacroManager().createTrackingSubstitutor();
- stateStorageManager = new StateStorageManagerImpl(macroSubstitutor, "application", this, ApplicationManager.getApplication().getPicoContainer()) {
- @NotNull
- @Override
- protected StorageData createStorageData(@NotNull String fileSpec, @NotNull String filePath) {
- return new StorageData("application");
- }
-
- @Nullable
- @Override
- protected String getOldStorageSpec(@NotNull Object component, @NotNull String componentName, @NotNull StateStorageOperation operation) {
- return null;
- }
-
- @Override
- protected TrackingPathMacroSubstitutor getMacroSubstitutor(@NotNull final String fileSpec) {
- if (fileSpec.equals(StoragePathMacros.APP_CONFIG + "/" + PathMacrosImpl.EXT_FILE_NAME + ".xml")) {
- return null;
- }
- return super.getMacroSubstitutor(fileSpec);
- }
- };
-
- stateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, testAppConfigPath);
- }
-
- @Override
- public void load() {
- }
-
- @NotNull
- @Override
- public StateStorageManager getStateStorageManager() {
- return stateStorageManager;
- }
-
- @Override
- public void dispose() {
- }
-
- @Nullable
- @Override
- protected PathMacroManager getPathMacroManagerForDefaults() {
- return null;
- }
-
- @NotNull
- @Override
- protected MessageBus getMessageBus() {
- return ApplicationManager.getApplication().getMessageBus();
- }
- }
-
- abstract static class Foo {
- public String foo = "defaultValue";
- }
-
- @State(
- name = "HttpConfigurable",
- storages = {
- @Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"),
- @Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true)
- }
- )
- static class SeveralStoragesConfigured extends Foo implements PersistentStateComponent {
- @Nullable
- @Override
- public SeveralStoragesConfigured getState() {
- return this;
- }
-
- @Override
- public void loadState(SeveralStoragesConfigured state) {
- XmlSerializerUtil.copyBean(state, this);
- }
- }
-
- @State(
- name = "HttpConfigurable",
- storages = {
- @Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true),
- @Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml")
- }
- )
- static class ActualStorageLast extends Foo implements PersistentStateComponent {
- @Nullable
- @Override
- public ActualStorageLast getState() {
- return this;
- }
-
- @Override
- public void loadState(ActualStorageLast state) {
- XmlSerializerUtil.copyBean(state, this);
- }
- }
-}
diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.kt
new file mode 100644
index 000000000000..2178f1fb4698
--- /dev/null
+++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.kt
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * 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.intellij.openapi.components.impl
+
+import com.intellij.application.options.PathMacrosImpl
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.components.*
+import com.intellij.openapi.components.impl.stores.*
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.openapi.vfs.CharsetToolkit
+import com.intellij.testFramework.LightPlatformTestCase
+import com.intellij.util.messages.MessageBus
+import com.intellij.util.xmlb.XmlSerializerUtil
+import gnu.trove.THashMap
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.MatcherAssert.assertThat
+import org.intellij.lang.annotations.Language
+import java.io.ByteArrayInputStream
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+
+public class ApplicationStoreTest : LightPlatformTestCase() {
+ private var testAppConfig: File? = null
+ private var componentStore: MyComponentStore? = null
+
+ throws(Exception::class)
+ override fun setUp() {
+ super.setUp()
+
+ val testAppConfigPath = System.getProperty("test.app.config.path")
+ if (testAppConfigPath == null) {
+ testAppConfig = FileUtil.createTempDirectory("testAppSettings", null)
+ }
+ else {
+ testAppConfig = File(FileUtil.expandUserHome(testAppConfigPath))
+ }
+ FileUtil.delete(testAppConfig!!)
+
+ componentStore = MyComponentStore(testAppConfig!!.getAbsolutePath())
+ }
+
+ throws(Exception::class)
+ override fun tearDown() {
+ try {
+ Disposer.dispose(componentStore!!)
+ componentStore = null
+ }
+ finally {
+ try {
+ super.tearDown()
+ }
+ finally {
+ FileUtil.delete(testAppConfig!!)
+ }
+ }
+ }
+
+ public fun testStreamProviderSaveIfSeveralStoragesConfigured() {
+ val component = SeveralStoragesConfigured()
+
+ val streamProvider = MyStreamProvider()
+ componentStore!!.getStateStorageManager().setStreamProvider(streamProvider)
+
+ componentStore!!.initComponent(component, false)
+ component.foo = "newValue"
+ StoreUtil.save(componentStore!!, null)
+
+ assertThat(streamProvider.data.get(RoamingType.PER_USER)!!.get(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), equalTo("\n" + " \n" + " \n" + " \n" + ""))
+ }
+
+ public fun testLoadFromStreamProvider() {
+ val component = SeveralStoragesConfigured()
+
+ val streamProvider = MyStreamProvider()
+ val map = THashMap()
+ map.put(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml", "\n" + " \n" + " \n" + " \n" + "")
+ streamProvider.data.put(RoamingType.PER_USER, map)
+
+ componentStore!!.getStateStorageManager().setStreamProvider(streamProvider)
+ componentStore!!.initComponent(component, false)
+ assertThat(component.foo, equalTo("newValue"))
+ }
+
+ public fun testRemoveDeprecatedStorageOnWrite() {
+ }
+
+ public fun testRemoveDeprecatedStorageOnWrite2() {
+ doRemoveDeprecatedStorageOnWrite(ActualStorageLast())
+ }
+
+ private fun doRemoveDeprecatedStorageOnWrite(component: Foo) {
+ val oldFile = saveConfig("other.xml", "" + " \n" + " \n" + " \n" + "")
+
+ saveConfig("proxy.settings.xml", "\n" + " \n" + " \n" + " \n" + "")
+
+ componentStore!!.initComponent(component, false)
+ assertThat(component.foo, equalTo("new"))
+
+ component.foo = "new2"
+ StoreUtil.save(componentStore!!, null)
+
+ assertThat(oldFile.exists(), equalTo(false))
+ }
+
+ throws(IOException::class)
+ private fun saveConfig(fileName: String, Language("XML") data: String): File {
+ val file = File(testAppConfig, fileName)
+ FileUtil.writeToFile(file, data)
+ return file
+ }
+
+ private class MyStreamProvider : StreamProvider {
+ public val data: MutableMap> = THashMap()
+
+ override fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) {
+ getMap(roamingType).put(fileSpec, String(content, 0, size, CharsetToolkit.UTF8_CHARSET))
+ }
+
+ private fun getMap(roamingType: RoamingType): MutableMap {
+ var map = data.get(roamingType)
+ if (map == null) {
+ map = THashMap()
+ data.put(roamingType, map)
+ }
+ return map
+ }
+
+ throws(IOException::class)
+ override fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream? {
+ val data = getMap(roamingType).get(fileSpec)
+ return if (data == null) null else ByteArrayInputStream(data.toByteArray(CharsetToolkit.UTF8_CHARSET))
+ }
+
+ override fun delete(fileSpec: String, roamingType: RoamingType) {
+ val map = data.get(roamingType)
+ map?.remove(fileSpec)
+ }
+ }
+
+ class MyComponentStore(testAppConfigPath: String) : ComponentStoreImpl(), Disposable {
+ private val stateStorageManager: StateStorageManager
+
+ init {
+ val macroSubstitutor = ApplicationPathMacroManager().createTrackingSubstitutor()
+ stateStorageManager = object : StateStorageManagerImpl(macroSubstitutor, "application", this, ApplicationManager.getApplication().getPicoContainer()) {
+ override fun createStorageData(fileSpec: String, filePath: String): StorageData {
+ return StorageData("application")
+ }
+
+ override fun getOldStorageSpec(component: Any, componentName: String, operation: StateStorageOperation): String? {
+ return null
+ }
+
+ override fun getMacroSubstitutor(fileSpec: String): TrackingPathMacroSubstitutor? {
+ if (fileSpec == StoragePathMacros.APP_CONFIG + "/" + PathMacrosImpl.EXT_FILE_NAME + ".xml") {
+ return null
+ }
+ return super.getMacroSubstitutor(fileSpec)
+ }
+ }
+
+ stateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, testAppConfigPath)
+ }
+
+ override fun load() {
+ }
+
+ override fun getStateStorageManager(): StateStorageManager {
+ return stateStorageManager
+ }
+
+ override fun dispose() {
+ }
+
+ override fun getPathMacroManagerForDefaults(): PathMacroManager? {
+ return null
+ }
+
+ override fun getMessageBus(): MessageBus {
+ return ApplicationManager.getApplication().getMessageBus()
+ }
+ }
+
+ abstract class Foo {
+ public var foo: String = "defaultValue"
+ }
+
+ State(name = "HttpConfigurable", storages = arrayOf(Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true)))
+ class SeveralStoragesConfigured : Foo(), PersistentStateComponent {
+ override fun getState(): SeveralStoragesConfigured? {
+ return this
+ }
+
+ override fun loadState(state: SeveralStoragesConfigured) {
+ XmlSerializerUtil.copyBean(state, this)
+ }
+ }
+
+ State(name = "HttpConfigurable", storages = arrayOf(Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", deprecated = true), Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml")))
+ class ActualStorageLast : Foo(), PersistentStateComponent {
+ override fun getState(): ActualStorageLast? {
+ return this
+ }
+
+ override fun loadState(state: ActualStorageLast) {
+ XmlSerializerUtil.copyBean(state, this)
+ }
+ }
+}
diff --git a/platform/platform-tests/testSrc/com/intellij/options/MockStreamProvider.kt b/platform/platform-tests/testSrc/com/intellij/options/MockStreamProvider.kt
new file mode 100644
index 000000000000..e7ed0ff3b495
--- /dev/null
+++ b/platform/platform-tests/testSrc/com/intellij/options/MockStreamProvider.kt
@@ -0,0 +1,40 @@
+package com.intellij.options
+
+import com.intellij.openapi.components.RoamingType
+import com.intellij.openapi.components.impl.stores.StreamProvider
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.util.SmartList
+import java.io.File
+import java.io.FileInputStream
+import java.io.InputStream
+
+class MockStreamProvider(private val myBaseDir: File) : StreamProvider {
+ override fun isApplicable(fileSpec: String, roamingType: RoamingType) = roamingType === RoamingType.PER_USER
+
+ override fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) {
+ FileUtil.writeToFile(File(myBaseDir, fileSpec), content, 0, size)
+ }
+
+ override fun loadContent(fileSpec: String, roamingType: RoamingType): InputStream? {
+ val file = File(myBaseDir, fileSpec)
+ //noinspection IOResourceOpenedButNotSafelyClosed
+ return if (file.exists()) FileInputStream(file) else null
+ }
+
+ override fun listSubFiles(fileSpec: String, roamingType: RoamingType): Collection {
+ if (roamingType !== RoamingType.PER_USER) {
+ return emptyList()
+ }
+
+ val files = File(myBaseDir, fileSpec).listFiles() ?: return emptyList()
+ val names = SmartList()
+ for (file in files) {
+ names.add(file.getName())
+ }
+ return names
+ }
+
+ override fun delete(fileSpec: String, roamingType: RoamingType) {
+ FileUtil.delete(File(myBaseDir, fileSpec))
+ }
+}
diff --git a/platform/platform-tests/testSrc/com/intellij/options/SchemeManagerTest.kt b/platform/platform-tests/testSrc/com/intellij/options/SchemeManagerTest.kt
new file mode 100644
index 000000000000..2cb9d57572d9
--- /dev/null
+++ b/platform/platform-tests/testSrc/com/intellij/options/SchemeManagerTest.kt
@@ -0,0 +1,368 @@
+package com.intellij.options
+
+import com.intellij.openapi.application.invokeAndWaitIfNeed
+import com.intellij.openapi.components.RoamingType
+import com.intellij.openapi.options.BaseSchemeProcessor
+import com.intellij.openapi.options.ExternalizableScheme
+import com.intellij.openapi.options.SchemeManagerImpl
+import com.intellij.openapi.util.JDOMUtil
+import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.testFramework.PlatformTestUtil
+import com.intellij.util.SmartList
+import com.intellij.util.lang.CompoundRuntimeException
+import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters
+import com.intellij.util.xmlb.XmlSerializer
+import com.intellij.util.xmlb.annotations.Attribute
+import com.intellij.util.xmlb.annotations.Tag
+import com.intellij.util.xmlb.annotations.Transient
+import gnu.trove.THashMap
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.CoreMatchers.not
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.CoreMatchers.sameInstance
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.collection.IsMapContaining.hasKey
+import org.jdom.Element
+import org.jetbrains.testFramework.FixtureRule
+import org.jetbrains.testFramework.TemporaryDirectory
+import org.jetbrains.testFramework.exists
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+val FILE_SPEC = "REMOTE"
+
+class SchemeManagerTest {
+ private val fixtureManager = FixtureRule()
+ public Rule fun getFixtureManager(): FixtureRule = fixtureManager
+
+ private val tempDirManager = TemporaryDirectory()
+ public Rule fun getTemporaryFolder(): TemporaryDirectory = tempDirManager
+
+ private var localBaseDir: File? = null
+ private var remoteBaseDir: File? = null
+
+ private fun getTestDataPath() = PlatformTestUtil.getCommunityPath().replace(File.separatorChar, '/') + "/platform/platform-tests/testData/options"
+
+ public Test fun testLoadSchemes() {
+ doLoadSaveTest("options1", "1->first;2->second")
+ }
+
+ public Test fun testLoadSimpleSchemes() {
+ doLoadSaveTest("options", "1->1")
+ }
+
+ public Test fun testDeleteScheme() {
+ val manager = createAndLoad("options1")
+ manager.removeScheme(TestScheme("first"))
+ manager.save()
+
+ checkSchemes("2->second")
+ }
+
+ public Test fun testRenameScheme() {
+ val manager = createAndLoad("options1")
+
+ val scheme = manager.findSchemeByName("first")
+ assertThat(scheme, notNullValue())
+ assert(scheme != null)
+ scheme!!.setName("renamed")
+ manager.save()
+
+ checkSchemes("2->second;renamed->renamed")
+ }
+
+ public Test fun testRenameScheme2() {
+ val manager = createAndLoad("options1")
+
+ val first = manager.findSchemeByName("first")
+ assertThat(first, notNullValue())
+ assert(first != null)
+ first!!.setName("2")
+ val second = manager.findSchemeByName("second")
+ assertThat(second, notNullValue())
+ assert(second != null)
+ second!!.setName("1")
+ manager.save()
+
+ checkSchemes("1->1;2->2")
+ }
+
+ public Test fun testDeleteRenamedScheme() {
+ val manager = createAndLoad("options1")
+
+ val firstScheme = manager.findSchemeByName("first")
+ assertThat(firstScheme, notNullValue())
+ assert(firstScheme != null)
+ firstScheme!!.setName("first_renamed")
+ manager.save()
+
+ checkSchemes(File(remoteBaseDir, "REMOTE"), "first_renamed->first_renamed;2->second", true)
+ checkSchemes(localBaseDir!!, "", false)
+
+ firstScheme.setName("first_renamed2")
+ manager.removeScheme(firstScheme)
+ manager.save()
+
+ checkSchemes(File(remoteBaseDir, "REMOTE"), "2->second", true)
+ checkSchemes(localBaseDir!!, "", false)
+ }
+
+ public Test fun testDeleteAndCreateSchemeWithTheSameName() {
+ val manager = createAndLoad("options1")
+ val firstScheme = manager.findSchemeByName("first")
+ assertThat(firstScheme, notNullValue())
+
+ manager.removeScheme(firstScheme!!)
+ manager.addScheme(TestScheme("first"))
+ manager.save()
+ checkSchemes("2->second;first->first")
+ }
+
+ public Test fun testGenerateUniqueSchemeName() {
+ val manager = createAndLoad("options1")
+ val scheme = TestScheme("first")
+ manager.addNewScheme(scheme, false)
+
+ assertThat("first2", equalTo(scheme.getName()))
+ }
+
+ public Test fun `different extensions`() {
+ val dir = tempDirManager.newDirectory()
+ FileUtil.copyDir(File("${getTestDataPath()}/different-extensions"), dir, false)
+ val schemesManager = SchemeManagerImpl(FILE_SPEC, object: TestSchemesProcessor() {
+ override fun isUpgradeNeeded() = true
+
+ override fun getSchemeExtension() = ".icls"
+ }, RoamingType.PER_USER, null, dir)
+ schemesManager.loadSchemes()
+ val schemes = schemesManager.getAllSchemes()
+ assertThat(schemes.size(), equalTo(1))
+ val scheme = schemes.get(0)
+ assertThat(scheme.data, equalTo("true"))
+
+ assertThat(File(dir, "1.icls"), exists())
+ assertThat(File(dir, "1.xml"), exists())
+
+ scheme.data = "newTrue"
+ save(schemesManager)
+
+ assertThat(File(dir, "1.icls"), exists())
+ assertThat(File(dir, "1.xml"), not(exists()))
+ }
+
+ public Test fun setSchemes() {
+ val dir = tempDirManager.newDirectory()
+ val schemesManager = createSchemeManager(dir)
+ schemesManager.loadSchemes()
+ assertThat(schemesManager.getAllSchemes().isEmpty(), equalTo(true))
+
+ val scheme = TestScheme("s1")
+ schemesManager.setSchemes(listOf(scheme))
+
+ val schemes = schemesManager.getAllSchemes()
+ assertThat(schemes.size(), equalTo(1))
+ assertThat(schemes.get(0), sameInstance(scheme))
+
+ assertThat(File(dir, "s1.xml"), not(exists()))
+
+ scheme.data = "newTrue"
+ save(schemesManager)
+
+ assertThat(File(dir, "s1.xml"), exists())
+
+ schemesManager.setSchemes(emptyList())
+
+ save(schemesManager)
+
+ assertThat(dir, not(exists()))
+ }
+
+ public Test fun `save only if scheme differs from bundled`() {
+ val dir = tempDirManager.newDirectory()
+ var schemeManager = createSchemeManager(dir)
+ val converter: (Element) -> TestScheme = { XmlSerializer.deserialize(it, javaClass())!! }
+ val bundledPath = "/bundledSchemes/default"
+ schemeManager.loadBundledScheme(bundledPath, this, converter)
+ var schemes = schemeManager.getAllSchemes()
+ assertThat(schemes.size(), equalTo(1))
+ val customScheme = TestScheme("default")
+ assertThat(schemes.get(0), equalTo(customScheme))
+
+ save(schemeManager)
+ assertThat(dir, not(exists()))
+
+ save(schemeManager)
+ schemeManager.setSchemes(listOf(customScheme))
+ assertThat(dir, not(exists()))
+
+ schemes = schemeManager.getAllSchemes()
+ assertThat(schemes.size(), equalTo(1))
+ assertThat(schemes.get(0), sameInstance(customScheme))
+
+ customScheme.data = "foo"
+ save(schemeManager)
+ val schemeFile = File(dir, "default.xml")
+ assertThat(schemeFile, exists())
+
+ schemeManager = createSchemeManager(dir)
+ schemeManager.loadBundledScheme(bundledPath, this, converter)
+ schemeManager.loadSchemes()
+
+ schemes = schemeManager.getAllSchemes()
+ assertThat(schemes.get(0), equalTo(customScheme))
+ }
+
+ private fun createSchemeManager(dir: File) = SchemeManagerImpl(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir)
+
+ public Test fun `don't remove dir if no schemes but at least one non-hidden file exists`() {
+ val dir = tempDirManager.newDirectory()
+ val schemesManager = SchemeManagerImpl(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir)
+
+ val scheme = TestScheme("s1")
+ schemesManager.setSchemes(listOf(scheme))
+
+ save(schemesManager)
+
+ val schemeFile = File(dir, "s1.xml")
+ assertThat(schemeFile.exists(), equalTo(true))
+
+ schemesManager.setSchemes(emptyList())
+
+ FileUtil.writeToFile(File(dir, "empty"), byteArrayOf())
+
+ save(schemesManager)
+
+ assertThat(schemeFile.exists(), equalTo(false))
+ assertThat(dir.exists(), equalTo(true))
+ }
+
+ public Test fun rename() {
+ val dir = tempDirManager.newDirectory()
+ val schemesManager = SchemeManagerImpl(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, null, dir)
+ schemesManager.loadSchemes()
+ assertThat(schemesManager.getAllSchemes().isEmpty(), equalTo(true))
+
+ val scheme = TestScheme("s1")
+ schemesManager.setSchemes(listOf(scheme))
+
+ val schemes = schemesManager.getAllSchemes()
+ assertThat(schemes.size(), equalTo(1))
+ assertThat(schemes.get(0), sameInstance(scheme))
+
+ assertThat(File(dir, "s1.xml").exists(), equalTo(false))
+
+ scheme.data = "newTrue"
+ save(schemesManager)
+
+ assertThat(File(dir, "s1.xml").exists(), equalTo(true))
+
+ scheme.setName("s2")
+
+ save(schemesManager)
+
+ assertThat(File(dir, "s1.xml").exists(), equalTo(false))
+ assertThat(File(dir, "s2.xml").exists(), equalTo(true))
+ }
+
+ private fun save(schemeManager: SchemeManagerImpl) {
+ invokeAndWaitIfNeed {
+ schemeManager.save()
+ }
+ }
+
+ private fun createAndLoad(testData: String): SchemeManagerImpl {
+ createTempFiles(testData)
+ return createAndLoad()
+ }
+
+ private fun doLoadSaveTest(testData: String, expected: String, localExpected: String = "") {
+ val schemesManager = createAndLoad(testData)
+ schemesManager.save()
+ checkSchemes(File(remoteBaseDir, "REMOTE"), expected, true)
+ checkSchemes(localBaseDir!!, localExpected, false)
+ }
+
+ private fun checkSchemes(expected: String) {
+ checkSchemes(File(remoteBaseDir, "REMOTE"), expected, true)
+ checkSchemes(localBaseDir!!, "", false)
+ }
+
+ private fun createAndLoad(): SchemeManagerImpl {
+ val schemesManager = SchemeManagerImpl(FILE_SPEC, TestSchemesProcessor(), RoamingType.PER_USER, MockStreamProvider(remoteBaseDir!!), localBaseDir!!)
+ schemesManager.loadSchemes()
+ return schemesManager
+ }
+
+ private fun createTempFiles(testData: String) {
+ val temp = tempDirManager.newDirectory()
+ localBaseDir = File(temp, "__local")
+ remoteBaseDir = temp
+ FileUtil.copyDir(File("${getTestDataPath()}/$testData"), File(temp, "REMOTE"))
+ }
+}
+
+private fun checkSchemes(baseDir: File, expected: String, ignoreDeleted: Boolean) {
+ val filesToScheme = StringUtil.split(expected, ";")
+ val fileToSchemeMap = THashMap()
+ for (fileToScheme in filesToScheme) {
+ val index = fileToScheme.indexOf("->")
+ fileToSchemeMap.put(fileToScheme.substring(0, index), fileToScheme.substring(index + 2))
+ }
+
+ val files = baseDir.listFiles()
+ if (files != null) {
+ for (file in files) {
+ val fileName = FileUtil.getNameWithoutExtension(file)
+ if ("--deleted" == fileName && ignoreDeleted) {
+ assertThat