diff options
Diffstat (limited to 'platform/platform-impl')
116 files changed, 3344 insertions, 2343 deletions
diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ErrorReportConfigurable.java b/platform/platform-impl/src/com/intellij/diagnostic/ErrorReportConfigurable.java index 7d5688c9d1d8..3f865dd19abd 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/ErrorReportConfigurable.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/ErrorReportConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -21,17 +21,11 @@ import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.WriteExternalException; -import org.apache.commons.codec.binary.Base64; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.util.Base64; import org.jdom.Element; import org.jetbrains.annotations.NotNull; -/** - * Created by IntelliJ IDEA. - * User: stathik - * Date: Aug 11, 2003 - * Time: 8:59:04 PM - * To change this template use Options | File Templates. - */ public class ErrorReportConfigurable implements JDOMExternalizable, NamedComponent { public String ITN_LOGIN = ""; public String ITN_PASSWORD_CRYPT = ""; @@ -43,31 +37,36 @@ public class ErrorReportConfigurable implements JDOMExternalizable, NamedCompone return ServiceManager.getService(ErrorReportConfigurable.class); } + @Override public void readExternal(Element element) throws InvalidDataException { DefaultJDOMExternalizer.readExternal(this, element); - if (! KEEP_ITN_PASSWORD) + if (!KEEP_ITN_PASSWORD) { ITN_PASSWORD_CRYPT = ""; + } } + @Override public void writeExternal(Element element) throws WriteExternalException { String itnPassword = ITN_PASSWORD_CRYPT; - if (! KEEP_ITN_PASSWORD) + if (!KEEP_ITN_PASSWORD) { ITN_PASSWORD_CRYPT = ""; + } DefaultJDOMExternalizer.writeExternal(this, element); ITN_PASSWORD_CRYPT = itnPassword; } + @Override @NotNull public String getComponentName() { return "ErrorReportConfigurable"; } - public String getPlainItnPassword () { - return new String(new Base64().decode(ErrorReportConfigurable.getInstance().ITN_PASSWORD_CRYPT.getBytes())); + public String getPlainItnPassword() { + return new String(Base64.decode(getInstance().ITN_PASSWORD_CRYPT), CharsetToolkit.UTF8_CHARSET); } - public void setPlainItnPassword (String password) { - ITN_PASSWORD_CRYPT = new String(new Base64().encode(password.getBytes())); + public void setPlainItnPassword(String password) { + ITN_PASSWORD_CRYPT = Base64.encode(password.getBytes(CharsetToolkit.UTF8_CHARSET)); } } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.java b/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.java index 092c346d40b7..fb34ba2342cf 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -19,32 +19,25 @@ import com.intellij.ide.BrowserUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.ui.ClickListener; -import com.intellij.util.net.HTTPProxySettingsDialog; +import com.intellij.util.net.HttpConfigurable; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; -/** - * Created by IntelliJ IDEA. - * User: stathik - * Date: Aug 8, 2003 - * Time: 3:49:50 PM - * To change this template use Options | File Templates. - */ public class JetBrainsAccountDialog extends DialogWrapper { private JTextField myItnLoginTextField; private JPasswordField myItnPasswordTextField; private JCheckBox myRememberITNPasswordCheckBox; - public void storeInfo () { + public void storeInfo() { ErrorReportConfigurable.getInstance().ITN_LOGIN = myItnLoginTextField.getText(); ErrorReportConfigurable.getInstance().setPlainItnPassword(new String(myItnPasswordTextField.getPassword())); ErrorReportConfigurable.getInstance().KEEP_ITN_PASSWORD = myRememberITNPasswordCheckBox.isSelected(); } - public void loadInfo () { + public void loadInfo() { myItnLoginTextField.setText(ErrorReportConfigurable.getInstance().ITN_LOGIN); myItnPasswordTextField.setText(ErrorReportConfigurable.getInstance().getPlainItnPassword()); myRememberITNPasswordCheckBox.setSelected(ErrorReportConfigurable.getInstance().KEEP_ITN_PASSWORD); @@ -64,6 +57,7 @@ public class JetBrainsAccountDialog extends DialogWrapper { protected JLabel mySendingSettingsLabel; private JLabel myCreateAccountLabel; + @Override protected String getDimensionServiceKey() { return "#com.intellij.diagnostic.AbstractSendErrorDialog"; } @@ -73,6 +67,7 @@ public class JetBrainsAccountDialog extends DialogWrapper { return myItnLoginTextField; } + @Override protected void init() { setTitle(ReportMessages.ERROR_REPORT); getContentPane().add(myMainPanel); @@ -80,14 +75,12 @@ public class JetBrainsAccountDialog extends DialogWrapper { new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent e, int clickCount) { - HTTPProxySettingsDialog settingsDialog = new HTTPProxySettingsDialog (); - settingsDialog.pack(); - settingsDialog.show(); + HttpConfigurable.editConfigurable(myMainPanel); return true; } }.installOn(mySendingSettingsLabel); - mySendingSettingsLabel.setCursor(new Cursor (Cursor.HAND_CURSOR)); + mySendingSettingsLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); loadInfo(); @@ -98,9 +91,9 @@ public class JetBrainsAccountDialog extends DialogWrapper { return true; } }.installOn(myCreateAccountLabel); - myCreateAccountLabel.setCursor(new Cursor (Cursor.HAND_CURSOR)); + myCreateAccountLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); - super.init (); + super.init(); } @Override @@ -109,6 +102,7 @@ public class JetBrainsAccountDialog extends DialogWrapper { super.doOKAction(); } + @Override protected JComponent createCenterPanel() { return myMainPanel; } diff --git a/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java b/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java index 0aeb20dbda90..c288150bd7ab 100644 --- a/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java +++ b/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -137,7 +137,9 @@ public class CommandLineProcessor { if (args.size() > 0) { String command = args.get(0); for(ApplicationStarter starter: Extensions.getExtensions(ApplicationStarter.EP_NAME)) { - if (starter instanceof ApplicationStarterEx && command.equals(starter.getCommandName())) { + if (command.equals(starter.getCommandName()) && + starter instanceof ApplicationStarterEx && + ((ApplicationStarterEx)starter).canProcessExternalCommandLine()) { LOG.info("Processing command with " + starter); ((ApplicationStarterEx) starter).processExternalCommandLine(ArrayUtil.toStringArray(args)); return null; diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index a7d07c9fa1e8..23491c2bdd6a 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -130,9 +130,9 @@ public class IdeEventQueue extends EventQueue { private final Set<EventDispatcher> myDispatchers = new LinkedHashSet<EventDispatcher>(); - private final Set<EventDispatcher> myPostprocessors = new LinkedHashSet<EventDispatcher>(); - + private final Set<EventDispatcher> myPostProcessors = new LinkedHashSet<EventDispatcher>(); private final Set<Runnable> myReady = new HashSet<Runnable>(); + private boolean myKeyboardBusy; private boolean myDispatchingFocusEvent; @@ -148,16 +148,14 @@ public class IdeEventQueue extends EventQueue { private IdeEventQueue() { addIdleTimeCounterRequest(); - final KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - //noinspection HardCodedStringLiteral + final KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); keyboardFocusManager.addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent e) { final Application application = ApplicationManager.getApplication(); if (application == null) { - // We can get focus event before application is initialized return; } @@ -171,6 +169,12 @@ public class IdeEventQueue extends EventQueue { }); addDispatcher(new WindowsAltSupressor(), null); + + Application app = ApplicationManager.getApplication(); + if (app != null && app.isUnitTestMode()) { + //noinspection AssignmentToStaticFieldFromInstanceMethod + ourAppIsLoaded = true; + } } @@ -289,11 +293,11 @@ public class IdeEventQueue extends EventQueue { } public void addPostprocessor(EventDispatcher dispatcher, @Nullable Disposable parent) { - _addProcessor(dispatcher, parent, myPostprocessors); + _addProcessor(dispatcher, parent, myPostProcessors); } public void removePostprocessor(EventDispatcher dispatcher) { - myPostprocessors.remove(dispatcher); + myPostProcessors.remove(dispatcher); } private static void _addProcessor(final EventDispatcher dispatcher, Disposable parent, final Set<EventDispatcher> set) { @@ -344,8 +348,26 @@ public class IdeEventQueue extends EventQueue { } } + private static boolean ourAppIsLoaded = false; + + private static boolean appIsLoaded() { + if (ourAppIsLoaded) return true; + boolean loaded = IdeaApplication.isLoaded(); + if (loaded) ourAppIsLoaded = true; + return loaded; + } + @Override public void dispatchEvent(AWTEvent e) { + if (!appIsLoaded()) { + try { + super.dispatchEvent(e); + } + catch (Throwable t) { + processException(t); + } + return; + } fixNonEnglishKeyboardLayouts(e); @@ -362,15 +384,13 @@ public class IdeEventQueue extends EventQueue { _dispatchEvent(e, false); } catch (Throwable t) { - if (!myToolkitBugsProcessor.process(t)) { - PluginManager.processException(t); - } + processException(t); } finally { myIsInInputEvent = wasInputEvent; myCurrentEvent = oldEvent; - for (EventDispatcher each : myPostprocessors) { + for (EventDispatcher each : myPostProcessors) { each.dispatch(e); } @@ -380,6 +400,12 @@ public class IdeEventQueue extends EventQueue { } } + private void processException(Throwable t) { + if (!myToolkitBugsProcessor.process(t)) { + PluginManager.processException(t); + } + } + private static void fixNonEnglishKeyboardLayouts(AWTEvent e) { if (!Registry.is("ide.non.english.keyboard.layout.fix")) return; if (e instanceof KeyEvent) { @@ -722,9 +748,7 @@ public class IdeEventQueue extends EventQueue { super.dispatchEvent(e); } catch (Throwable t) { - if (!myToolkitBugsProcessor.process(t)) { - PluginManager.processException(t); - } + processException(t); } finally { myDispatchingFocusEvent = false; @@ -760,7 +784,7 @@ public class IdeEventQueue extends EventQueue { } private static boolean typeAheadDispatchToFocusManager(AWTEvent e) { - if (e instanceof KeyEvent && appIsLoaded()) { + if (e instanceof KeyEvent) { final KeyEvent event = (KeyEvent)e; if (!event.isConsumed()) { final IdeFocusManager focusManager = IdeFocusManager.findInstanceByComponent(event.getComponent()); @@ -771,15 +795,6 @@ public class IdeEventQueue extends EventQueue { return false; } - private static boolean ourAppIsLoaded = false; - - private static boolean appIsLoaded() { - if (ourAppIsLoaded) return true; - boolean loaded = IdeaApplication.isLoaded(); - if (loaded) ourAppIsLoaded = true; - return loaded; - } - public void flushQueue() { while (true) { AWTEvent event = peekEvent(); diff --git a/platform/platform-impl/src/com/intellij/ide/IdeRepaintManager.java b/platform/platform-impl/src/com/intellij/ide/IdeRepaintManager.java index d7118949e5de..4a0e11bf5ff9 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeRepaintManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeRepaintManager.java @@ -128,19 +128,23 @@ public class IdeRepaintManager extends RepaintManager { final Exception exception = new Exception(); StackTraceElement[] stackTrace = exception.getStackTrace(); for (StackTraceElement st : stackTrace) { - if (repaint && st.getClassName().startsWith("javax.swing.")) { + String className = st.getClassName(); + String methodName = st.getMethodName(); + + if (repaint && className.startsWith("javax.swing.")) { fromSwing = true; } - if (repaint && "imageUpdate".equals(st.getMethodName())) { + if (repaint && "imageUpdate".equals(methodName)) { swingKnownNonAwtOperations = true; } - if (st.getClassName().startsWith("javax.swing.JEditorPane") && st.getMethodName().equals("read")) { + if ("read".equals(methodName) && className.startsWith("javax.swing.JEditorPane") || + "setCharacterAttributes".equals(methodName) && className.startsWith("javax.swing.text.DefaultStyledDocument")) { swingKnownNonAwtOperations = true; break; } - if ("repaint".equals(st.getMethodName())) { + if ("repaint".equals(methodName)) { repaint = true; fromSwing = false; } diff --git a/platform/platform-impl/src/com/intellij/ide/UiActivityMonitorImpl.java b/platform/platform-impl/src/com/intellij/ide/UiActivityMonitorImpl.java index c25c68b4bb2b..e9de64905415 100644 --- a/platform/platform-impl/src/com/intellij/ide/UiActivityMonitorImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/UiActivityMonitorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -16,7 +16,6 @@ package com.intellij.ide; import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.ModalityStateListener; @@ -24,7 +23,6 @@ import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.BusyObject; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.registry.Registry; import com.intellij.util.containers.FactoryMap; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -34,16 +32,16 @@ import javax.swing.*; import java.util.*; public class UiActivityMonitorImpl extends UiActivityMonitor implements ModalityStateListener, Disposable { - private final FactoryMap<Project, BusyContainer> myObjects = new FactoryMap<Project, BusyContainer>() { @Override protected BusyContainer create(Project key) { if (isEmpty()) { installListener(); } - return key == null ? new BusyContainer(key) : new BusyContainer(null) { + return key == null ? new BusyContainer(null) : new BusyContainer(null) { + @NotNull @Override - protected BusyImpl createBusyImpl(HashSet<UiActivity> key) { + protected BusyImpl createBusyImpl(@NotNull Set<UiActivity> key) { return new BusyImpl(key, this) { @Override public boolean isReady() { @@ -63,7 +61,8 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality private boolean myActive; - private BusyObject myEmptyBusy = new BusyObject.Impl() { + @NotNull + private final BusyObject myEmptyBusy = new BusyObject.Impl() { @Override public boolean isReady() { return true; @@ -84,17 +83,12 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality @Override public void beforeModalityStateChanged(boolean entering) { - if (isUnitTestMode()) { - maybeReady(); - } - else { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - maybeReady(); - } - }); - } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + maybeReady(); + } + }); } public void maybeReady() { @@ -103,15 +97,17 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } } + @NotNull @Override - public BusyObject getBusy(@NotNull Project project, UiActivity... toWatch) { + public BusyObject getBusy(@NotNull Project project, @NotNull UiActivity... toWatch) { if (!isActive()) return myEmptyBusy; return _getBusy(project, toWatch); } + @NotNull @Override - public BusyObject getBusy(UiActivity... toWatch) { + public BusyObject getBusy(@NotNull UiActivity... toWatch) { if (!isActive()) return myEmptyBusy; return _getBusy(null, toWatch); @@ -129,10 +125,10 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality if (!isActive()) return; - invokeLaterIfNeeded(new MyRunnable() { + UIUtil.invokeLaterIfNeeded(new Runnable() { @Override - public void run(Throwable allocation) { - getBusyContainer(project).addActivity(activity, allocation, effectiveModalityState); + public void run() { + getBusyContainer(project).addActivity(activity, effectiveModalityState); } }); } @@ -141,9 +137,9 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality public void removeActivity(@NotNull final Project project, @NotNull final UiActivity activity) { if (!isActive()) return; - invokeLaterIfNeeded(new MyRunnable() { + UIUtil.invokeLaterIfNeeded(new Runnable() { @Override - public void run(Throwable allocation) { + public void run() { _getBusy(project).removeActivity(activity); } }); @@ -162,10 +158,10 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality public void addActivity(@NotNull final UiActivity activity, @NotNull final ModalityState effectiveModalityState) { if (!isActive()) return; - invokeLaterIfNeeded(new MyRunnable() { + UIUtil.invokeLaterIfNeeded(new Runnable() { @Override - public void run(Throwable allocation) { - getBusyContainer(null).addActivity(activity, allocation, effectiveModalityState); + public void run() { + getBusyContainer(null).addActivity(activity, effectiveModalityState); } }); } @@ -174,15 +170,16 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality public void removeActivity(@NotNull final UiActivity activity) { if (!isActive()) return; - invokeLaterIfNeeded(new MyRunnable() { + UIUtil.invokeLaterIfNeeded(new Runnable() { @Override - public void run(Throwable allocation) { + public void run() { _getBusy(null).removeActivity(activity); } }); } - private BusyImpl _getBusy(@Nullable Project key, UiActivity... toWatch) { + @NotNull + private BusyImpl _getBusy(@Nullable Project key, @NotNull UiActivity... toWatch) { return getBusyContainer(key).getOrCreateBusy(toWatch); } @@ -204,6 +201,7 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality return myObjects.get(null); } + @Override public void clear() { final Set<Project> keys = myObjects.keySet(); for (Project each : keys) { @@ -215,7 +213,7 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality public void setActive(boolean active) { if (myActive == active) return; - if (myActive && !active) { + if (myActive) { clear(); } @@ -227,25 +225,19 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } private static class ActivityInfo { - private final Throwable myAllocation; private final ModalityState myEffectiveState; - private ActivityInfo(@Nullable Throwable allocation, @NotNull ModalityState effectiveState) { - myAllocation = allocation; + private ActivityInfo(@NotNull ModalityState effectiveState) { myEffectiveState = effectiveState; } - @Nullable - public Throwable getAllocation() { - return myAllocation; - } - @NotNull public ModalityState getEffectiveState() { return myEffectiveState; } } + @NotNull protected ModalityState getCurrentState() { return ModalityState.current(); } @@ -258,9 +250,9 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality protected final Set<UiActivity> myToWatch; protected final UiActivity[] myToWatchArray; - private UiActivityMonitorImpl.BusyContainer myContainer; + private final UiActivityMonitorImpl.BusyContainer myContainer; - private BusyImpl(Set<UiActivity> toWatch, BusyContainer container) { + private BusyImpl(@NotNull Set<UiActivity> toWatch, @NotNull BusyContainer container) { myToWatch = toWatch; myToWatchArray = toWatch.toArray(new UiActivity[toWatch.size()]); myContainer = container; @@ -274,9 +266,7 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality boolean isOwnReady() { Map<UiActivity, ActivityInfo> infoToCheck = new HashMap<UiActivity, ActivityInfo>(); - final Iterator<Set<UiActivity>> activitySets = myContainer.myActivities2Object.keySet().iterator(); - while (activitySets.hasNext()) { - Set<UiActivity> eachActivitySet = activitySets.next(); + for (Set<UiActivity> eachActivitySet : myContainer.myActivities2Object.keySet()) { final BusyImpl eachBusyObject = myContainer.myActivities2Object.get(eachActivitySet); if (eachBusyObject == this) continue; @@ -304,19 +294,16 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality return true; } + public void addActivity(@NotNull UiActivity activity, @NotNull ModalityState effectiveModalityState) { + if (!myToWatch.isEmpty() && !myToWatch.contains(activity)) return; - public void addActivity(UiActivity activity, Throwable allocation, ModalityState effectiveModalityState) { - if (!myToWatch.isEmpty()) { - if (!myToWatch.contains(activity)) return; - } - - myActivities.put(activity, new ActivityInfo(allocation, effectiveModalityState)); + myActivities.put(activity, new ActivityInfo(effectiveModalityState)); myQueuedToRemove.remove(activity); - myContainer.onActivityAdded(this, activity); + myContainer.onActivityAdded(activity); } - public void removeActivity(final UiActivity activity) { + public void removeActivity(@NotNull final UiActivity activity) { if (!myActivities.containsKey(activity)) return; myQueuedToRemove.add(activity); @@ -333,87 +320,48 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality onReady(); } }; - if (isUnitTestMode()) { - runnable.run(); - } - else { - SwingUtilities.invokeLater(runnable); - } + SwingUtilities.invokeLater(runnable); } - - public void clear() { - UiActivity[] activities = myActivities.keySet().toArray(new UiActivity[myActivities.size()]); - for (UiActivity each : activities) { - removeActivity(each); - } - } - } - - private static void invokeLaterIfNeeded(final MyRunnable runnable) { - final Throwable allocation = Registry.is("ide.debugMode") ? new Exception() : null; - - if (isUnitTestMode()) { - runnable.run(allocation); - } - else { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - runnable.run(allocation); - } - }); - } - } - - private interface MyRunnable { - void run(Throwable allocation); - } - - private static boolean isUnitTestMode() { - Application app = ApplicationManager.getApplication(); - return app == null || app.isUnitTestMode(); } public class BusyContainer implements Disposable { + private final Map<Set<UiActivity>, BusyImpl> myActivities2Object = new HashMap<Set<UiActivity>, BusyImpl>(); + private final Map<BusyImpl, Set<UiActivity>> myObject2Activities = new HashMap<BusyImpl, Set<UiActivity>>(); - private Map<Set<UiActivity>, BusyImpl> myActivities2Object = new HashMap<Set<UiActivity>, BusyImpl>(); - private Map<BusyImpl, Set<UiActivity>> myObject2Activities = new HashMap<BusyImpl, Set<UiActivity>>(); - - private Set<UiActivity> myActivities = new HashSet<UiActivity>(); - - private BusyImpl myDefault; + private final Set<UiActivity> myActivities = new HashSet<UiActivity>(); private boolean myRemovingActivityNow; @Nullable private final Project myProject; public BusyContainer(@Nullable Project project) { myProject = project; - myDefault = registerBusyObject(new HashSet<UiActivity>()); + registerBusyObject(new HashSet<UiActivity>()); if (project != null) { Disposer.register(project, this); } } - public BusyImpl getOrCreateBusy(UiActivity... activities) { - final HashSet<UiActivity> key = new HashSet<UiActivity>(); + @NotNull + public BusyImpl getOrCreateBusy(@NotNull UiActivity... activities) { + Set<UiActivity> key = new HashSet<UiActivity>(); key.addAll(Arrays.asList(activities)); if (myActivities2Object.containsKey(key)) { return myActivities2Object.get(key); } - else { - return registerBusyObject(key); - } + return registerBusyObject(key); } - private BusyImpl registerBusyObject(HashSet<UiActivity> key) { + @NotNull + private BusyImpl registerBusyObject(@NotNull Set<UiActivity> key) { final BusyImpl busy = createBusyImpl(key); myActivities2Object.put(key, busy); myObject2Activities.put(busy, key); return busy; } - protected BusyImpl createBusyImpl(HashSet<UiActivity> key) { + @NotNull + protected BusyImpl createBusyImpl(@NotNull Set<UiActivity> key) { return new BusyImpl(key, this); } @@ -437,11 +385,11 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } } - public void onActivityAdded(BusyImpl busy, UiActivity activity) { + public void onActivityAdded(@NotNull UiActivity activity) { myActivities.add(activity); } - public void onActivityRemoved(BusyImpl busy, UiActivity activity) { + public void onActivityRemoved(@NotNull BusyImpl busy, @NotNull UiActivity activity) { if (myRemovingActivityNow) return; final Map<BusyImpl, Set<UiActivity>> toRemove = new HashMap<BusyImpl, Set<UiActivity>>(); @@ -450,9 +398,7 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality myRemovingActivityNow = true; myActivities.remove(activity); - final Iterator<BusyImpl> objects = myObject2Activities.keySet().iterator(); - while (objects.hasNext()) { - BusyImpl each = objects.next(); + for (BusyImpl each : myObject2Activities.keySet()) { if (each != busy) { each.removeActivity(activity); } @@ -472,11 +418,11 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } } - public void addActivity(UiActivity activity, Throwable allocation, ModalityState state) { + public void addActivity(@NotNull UiActivity activity, @NotNull ModalityState state) { getOrCreateBusy(activity); final Set<BusyImpl> busies = myObject2Activities.keySet(); for (BusyImpl each : busies) { - each.addActivity(activity, allocation, state); + each.addActivity(activity, state); } } diff --git a/platform/platform-impl/src/com/intellij/ide/actionMacro/EditMacrosDialog.java b/platform/platform-impl/src/com/intellij/ide/actionMacro/EditMacrosDialog.java deleted file mode 100644 index 8b2c9acfaef5..000000000000 --- a/platform/platform-impl/src/com/intellij/ide/actionMacro/EditMacrosDialog.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2000-2009 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.ide.actionMacro; - -import com.intellij.openapi.options.ex.SingleConfigurableEditor; -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.Nullable; - -/** - * Created by IntelliJ IDEA. - * User: max - * Date: Jul 22, 2003 - * Time: 3:30:56 PM - * To change this template use Options | File Templates. - */ -public class EditMacrosDialog extends SingleConfigurableEditor { - public EditMacrosDialog(@Nullable Project project) { - super(project, new ActionMacroConfigurable()); - } - - protected String getDimensionServiceKey(){ - return "#com.intellij.ide.actionMacro.EditMacrosDialog"; - } -} diff --git a/platform/platform-impl/src/com/intellij/ide/actionMacro/actions/EditMacrosAction.java b/platform/platform-impl/src/com/intellij/ide/actionMacro/actions/EditMacrosAction.java index cb0d0ffd4619..c46241d1c4d9 100644 --- a/platform/platform-impl/src/com/intellij/ide/actionMacro/actions/EditMacrosAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actionMacro/actions/EditMacrosAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -16,29 +16,22 @@ package com.intellij.ide.actionMacro.actions; import com.intellij.ide.actionMacro.ActionMacro; +import com.intellij.ide.actionMacro.ActionMacroConfigurable; import com.intellij.ide.actionMacro.ActionMacroManager; -import com.intellij.ide.actionMacro.EditMacrosDialog; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.DumbAware; -/** - * Created by IntelliJ IDEA. - * User: max - * Date: Jul 22, 2003 - * Time: 3:33:04 PM - * To change this template use Options | File Templates. - */ public class EditMacrosAction extends AnAction implements DumbAware { + @Override public void actionPerformed(AnActionEvent e) { - EditMacrosDialog dialog = new EditMacrosDialog(CommonDataKeys.PROJECT.getData(e.getDataContext())); - dialog.show(); + ShowSettingsUtil.getInstance().editConfigurable(e.getProject(), "#com.intellij.ide.actionMacro.EditMacrosDialog", new ActionMacroConfigurable()); } + @Override public void update(AnActionEvent e) { - final ActionMacroManager manager = ActionMacroManager.getInstance(); - ActionMacro[] macros = manager.getAllMacros(); + ActionMacro[] macros = ActionMacroManager.getInstance().getAllMacros(); e.getPresentation().setEnabled(macros != null && macros.length > 0); } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/BaseNavigateToSourceAction.java b/platform/platform-impl/src/com/intellij/ide/actions/BaseNavigateToSourceAction.java index 1c7855d2eedb..709c3b458112 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/BaseNavigateToSourceAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/BaseNavigateToSourceAction.java @@ -19,7 +19,9 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.DumbAware; import com.intellij.pom.Navigatable; import com.intellij.pom.NavigatableWithText; +import com.intellij.pom.PomTargetPsiElement; import com.intellij.util.OpenSourceUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public abstract class BaseNavigateToSourceAction extends AnAction implements DumbAware { @@ -37,7 +39,7 @@ public abstract class BaseNavigateToSourceAction extends AnAction implements Dum public void update(AnActionEvent event) { DataContext dataContext = event.getDataContext(); - final Navigatable target = getTarget(dataContext); + final Navigatable target = findTargetForUpdate(dataContext); boolean enabled = target != null; if (ActionPlaces.isPopupPlace(event.getPlace())) { event.getPresentation().setVisible(enabled); @@ -49,36 +51,21 @@ public abstract class BaseNavigateToSourceAction extends AnAction implements Dum else { event.getPresentation().setEnabled(enabled); } - if (target != null && target instanceof NavigatableWithText) { - //as myFocusEditor is always ignored - Main Menu|View always contains 2 actions with the same name and actually same behaviour - if (!myFocusEditor) { - event.getPresentation().setVisible(false); - return; - } - final String navigateActionText = ((NavigatableWithText)target).getNavigateActionText(myFocusEditor); - if (navigateActionText != null) { - event.getPresentation().setText(navigateActionText); - } - else { - event.getPresentation().setText(getTemplatePresentation().getText()); - } - } - else { - event.getPresentation().setText(getTemplatePresentation().getText()); - } + //as myFocusEditor is always ignored - Main Menu|View always contains 2 actions with the same name and actually same behaviour + event.getPresentation().setVisible(target == null || myFocusEditor); + String navigateActionText = myFocusEditor && target instanceof NavigatableWithText? + ((NavigatableWithText)target).getNavigateActionText(true) : null; + event.getPresentation().setText(navigateActionText == null ? getTemplatePresentation().getText() : navigateActionText); } @Nullable - private Navigatable getTarget(final DataContext dataContext) { - if (!myFocusEditor && CommonDataKeys.EDITOR.getData(dataContext) != null) { - // makes no sense in editor and conflicts with another action there (ctrl+enter) - return null; - } - + private Navigatable findTargetForUpdate(@NotNull DataContext dataContext) { Navigatable[] navigatables = getNavigatables(dataContext); - if (navigatables != null) { - for (Navigatable navigatable : navigatables) { - if (navigatable.canNavigate()) return navigatable; + if (navigatables == null) return null; + + for (Navigatable navigatable : navigatables) { + if (navigatable.canNavigate()) { + return navigatable instanceof PomTargetPsiElement ? ((PomTargetPsiElement)navigatable).getTarget() : navigatable; } } return null; diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java index 9bdc5948cffe..136f4b166e1a 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java @@ -23,6 +23,7 @@ import com.intellij.notification.Notifications; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; @@ -104,11 +105,15 @@ public class CreateDesktopEntryAction extends DumbAwareAction { final String message = ApplicationBundle.message("desktop.entry.success", ApplicationNamesInfo.getInstance().getProductName()); - Notifications.Bus.notify( - new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Desktop entry created", message, NotificationType.INFORMATION) - ); + if (ApplicationManager.getApplication() != null) { + Notifications.Bus + .notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Desktop entry created", message, NotificationType.INFORMATION)); + } } catch (Exception e) { + if (ApplicationManager.getApplication() == null) { + throw new RuntimeException(e); + } final String message = e.getMessage(); if (!StringUtil.isEmptyOrSpaces(message)) { LOG.warn(e); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/OpenFileAction.java b/platform/platform-impl/src/com/intellij/ide/actions/OpenFileAction.java index 4cabfe9b5fe9..37b09394dd89 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/OpenFileAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/OpenFileAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -19,14 +19,14 @@ import com.intellij.ide.IdeBundle; import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.fileChooser.FileElement; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.fileChooser.PathChooserDialog; import com.intellij.openapi.fileChooser.impl.FileChooserUtil; import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.FileEditorProvider; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager; import com.intellij.openapi.fileTypes.FileType; @@ -34,7 +34,6 @@ import com.intellij.openapi.fileTypes.ex.FileTypeChooser; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -47,54 +46,20 @@ import org.jetbrains.annotations.Nullable; import java.util.List; public class OpenFileAction extends AnAction implements DumbAware { + @Override public void actionPerformed(AnActionEvent e) { - @Nullable final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); + final Project project = e.getProject(); final boolean showFiles = project != null || PlatformProjectOpenProcessor.getInstanceIfItExists() != null; + final FileChooserDescriptor descriptor = showFiles ? new ProjectOrFileChooserDescriptor() : new ProjectOnlyFileChooserDescriptor(); + descriptor.putUserData(PathChooserDialog.PREFER_LAST_OVER_EXPLICIT, showFiles); - final FileChooserDescriptor descriptor = new OpenProjectFileChooserDescriptor(true) { - @Override - public boolean isFileSelectable(VirtualFile file) { - if (super.isFileSelectable(file)) { - return true; - } - if (file.isDirectory()) { - return false; - } - return showFiles && !FileElement.isArchive(file); - } - - @Override - public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { - if (!file.isDirectory() && isFileSelectable(file)) { - if (!showHiddenFiles && FileElement.isFileHidden(file)) return false; - return true; - } - return super.isFileVisible(file, showHiddenFiles); - } - - @Override - public boolean isChooseMultiple() { - return showFiles; - } - }; - descriptor.setTitle(showFiles ? "Open File or Project" : "Open Project"); - - VirtualFile userHomeDir = null; - if (SystemInfo.isUnix) { - userHomeDir = VfsUtil.getUserHomeDir(); - } - - descriptor.putUserData(PathChooserDialog.PREFER_LAST_OVER_EXPLICIT, Boolean.TRUE); - - FileChooser.chooseFiles(descriptor, project, userHomeDir, new Consumer<List<VirtualFile>>() { + FileChooser.chooseFiles(descriptor, project, VfsUtil.getUserHomeDir(), new Consumer<List<VirtualFile>>() { @Override public void consume(final List<VirtualFile> files) { for (VirtualFile file : files) { - if (!descriptor.isFileSelectable(file)) { // on Mac, it could be selected anyway - Messages.showInfoMessage(project, - file.getPresentableUrl() + " contains no " + - ApplicationNamesInfo.getInstance().getFullProductName() + " project", - "Cannot Open Project"); + if (!descriptor.isFileSelectable(file)) { + String message = IdeBundle.message("error.dir.contains.no.project", file.getPresentableUrl()); + Messages.showInfoMessage(project, message, IdeBundle.message("title.cannot.open.project")); return; } } @@ -103,9 +68,8 @@ public class OpenFileAction extends AnAction implements DumbAware { }); } - private static void doOpenFile(@Nullable final Project project, - @NotNull final List<VirtualFile> result) { - for (final VirtualFile file : result) { + private static void doOpenFile(@Nullable Project project, @NotNull List<VirtualFile> result) { + for (VirtualFile file : result) { if (file.isDirectory()) { Project openedProject; if (ProjectAttachProcessor.canAttachToProject()) { @@ -118,9 +82,8 @@ public class OpenFileAction extends AnAction implements DumbAware { return; } - if (OpenProjectFileChooserDescriptor.isProjectFile(file) && - // if the ipr-based project is already open, just open the ipr file - (project == null || !file.equals(project.getProjectFile()))) { + // try to open as a project - unless the file is an .ipr of the current one + if ((project == null || !file.equals(project.getProjectFile())) && OpenProjectFileChooserDescriptor.isProjectFile(file)) { Project openedProject = ProjectUtil.openOrImport(file.getPath(), project, false); if (openedProject != null) { FileChooserUtil.setLastOpenedFile(openedProject, file); @@ -143,26 +106,54 @@ public class OpenFileAction extends AnAction implements DumbAware { } } - public static void openFile(final String filePath, final Project project) { - final VirtualFile file = LocalFileSystem.getInstance().findFileByPath(filePath); + public static void openFile(String filePath, @NotNull Project project) { + VirtualFile file = LocalFileSystem.getInstance().findFileByPath(filePath); if (file != null && file.isValid()) { openFile(file, project); } } - public static void openFile(final VirtualFile virtualFile, final Project project) { - FileEditorProviderManager editorProviderManager = FileEditorProviderManager.getInstance(); - if (editorProviderManager.getProviders(project, virtualFile).length == 0) { - Messages.showMessageDialog(project, - IdeBundle.message("error.files.of.this.type.cannot.be.opened", - ApplicationNamesInfo.getInstance().getProductName()), - IdeBundle.message("title.cannot.open.file"), - Messages.getErrorIcon()); + public static void openFile(VirtualFile file, @NotNull Project project) { + FileEditorProvider[] providers = FileEditorProviderManager.getInstance().getProviders(project, file); + if (providers.length == 0) { + String message = IdeBundle.message("error.files.of.this.type.cannot.be.opened", ApplicationNamesInfo.getInstance().getProductName()); + Messages.showErrorDialog(project, message, IdeBundle.message("title.cannot.open.file")); return; } - OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile); + OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file); FileEditorManager.getInstance(project).openTextEditor(descriptor, true); } + private static class ProjectOnlyFileChooserDescriptor extends OpenProjectFileChooserDescriptor { + public ProjectOnlyFileChooserDescriptor() { + super(true); + setTitle(IdeBundle.message("title.open.project")); + } + } + + // vanilla OpenProjectFileChooserDescriptor only accepts project files; this on is overridden to accept any files + private static class ProjectOrFileChooserDescriptor extends OpenProjectFileChooserDescriptor { + private final FileChooserDescriptor myStandardDescriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(); + + public ProjectOrFileChooserDescriptor() { + super(true); + setTitle(IdeBundle.message("title.open.file.or.project")); + } + + @Override + public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { + return file.isDirectory() ? super.isFileVisible(file, showHiddenFiles) : myStandardDescriptor.isFileVisible(file, showHiddenFiles); + } + + @Override + public boolean isFileSelectable(VirtualFile file) { + return file.isDirectory() ? super.isFileSelectable(file) : myStandardDescriptor.isFileSelectable(file); + } + + @Override + public boolean isChooseMultiple() { + return true; + } + } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/OpenProjectFileChooserDescriptor.java b/platform/platform-impl/src/com/intellij/ide/actions/OpenProjectFileChooserDescriptor.java index 51d6362b36d8..b462fcbc11ad 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/OpenProjectFileChooserDescriptor.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/OpenProjectFileChooserDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -15,74 +15,91 @@ */ package com.intellij.ide.actions; -import com.intellij.icons.AllIcons; import com.intellij.ide.highlighter.ProjectFileType; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.fileChooser.FileElement; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.projectImport.ProjectOpenProcessor; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import javax.swing.*; +/** + * Intended for use in actions related to opening or importing existing projects. + * <strong>Due to a high I/O impact SHOULD NOT be used in any other cases.</strong> + */ public class OpenProjectFileChooserDescriptor extends FileChooserDescriptor { private static final Icon ourProjectIcon = IconLoader.getIcon(ApplicationInfoEx.getInstanceEx().getSmallIconUrl()); - public OpenProjectFileChooserDescriptor(final boolean chooseFiles) { + public OpenProjectFileChooserDescriptor(boolean chooseFiles) { super(chooseFiles, true, chooseFiles, chooseFiles, false, false); } - public boolean isFileSelectable(final VirtualFile file) { - if (file == null) return false; + @Override + public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { + return super.isFileVisible(file, showHiddenFiles) && (file.isDirectory() || isProjectFile(file)); + } + + @Override + public boolean isFileSelectable(VirtualFile file) { return isProjectDirectory(file) || isProjectFile(file); } - public Icon getIcon(final VirtualFile file) { - if (isProjectDirectory(file)) { - return dressIcon(file, ourProjectIcon); - } - final Icon icon = getImporterIcon(file); - if (icon != null) { - return dressIcon(file, icon); + @Override + public Icon getIcon(VirtualFile file) { + if (canInspectDirectory(file)) { + if (isIprFile(file) || isIdeaDirectory(file)) { + return dressIcon(file, ourProjectIcon); + } + Icon icon = getImporterIcon(file); + if (icon != null) { + return dressIcon(file, icon); + } } return super.getIcon(file); } - @Nullable - private static Icon getImporterIcon(final VirtualFile virtualFile) { - final ProjectOpenProcessor provider = ProjectOpenProcessor.getImportProvider(virtualFile); + private static boolean canInspectDirectory(VirtualFile file) { + if (file.getParent() == null) return false; + + VirtualFile home = VfsUtil.getUserHomeDir(); + if (home == null) return false; // unnatural situation + VirtualFile homes = home.getParent(); + if (homes == null) return false; // another one + if (homes.equals(file.getParent()) || VfsUtilCore.isAncestor(file, homes, false)) return false; + + return true; + } + + private static Icon getImporterIcon(VirtualFile file) { + ProjectOpenProcessor provider = ProjectOpenProcessor.getImportProvider(file); if (provider != null) { - return virtualFile.isDirectory() && provider.lookForProjectsInDirectory() ? AllIcons.Nodes.IdeaModule : provider.getIcon(virtualFile); + return file.isDirectory() && provider.lookForProjectsInDirectory() ? ourProjectIcon : provider.getIcon(file); } return null; } - public boolean isFileVisible(final VirtualFile file, final boolean showHiddenFiles) { - if (!showHiddenFiles && FileElement.isFileHidden(file)) return false; - return isProjectFile(file) || super.isFileVisible(file, showHiddenFiles) && file.isDirectory(); + public static boolean isProjectFile(@NotNull VirtualFile file) { + return !file.isDirectory() && file.isValid() && (isIprFile(file) || hasImportProvider(file)); } - public static boolean isProjectFile(final VirtualFile file) { - if (isIprFile(file)) return true; - final ProjectOpenProcessor importProvider = ProjectOpenProcessor.getImportProvider(file); - return importProvider != null; + private static boolean isProjectDirectory(@NotNull VirtualFile file) { + return file.isDirectory() && file.isValid() && (isIdeaDirectory(file) || hasImportProvider(file)); } private static boolean isIprFile(VirtualFile file) { - if ((!file.isDirectory() && file.getName().toLowerCase().endsWith(ProjectFileType.DOT_DEFAULT_EXTENSION))) { - return true; - } - return false; + return ProjectFileType.DEFAULT_EXTENSION.equalsIgnoreCase(file.getExtension()); + } + + private static boolean isIdeaDirectory(VirtualFile file) { + return file.findChild(Project.DIRECTORY_STORE_FOLDER) != null; } - private static boolean isProjectDirectory(final VirtualFile virtualFile) { - // the root directory of any drive is never an IDEA project - if (virtualFile.getParent() == null) return false; - // NOTE: For performance reasons, it's very important not to iterate through all of the children here. - if (virtualFile.isDirectory() && virtualFile.isValid() && virtualFile.findChild(Project.DIRECTORY_STORE_FOLDER) != null) return true; - return false; + private static boolean hasImportProvider(VirtualFile file) { + return ProjectOpenProcessor.getImportProvider(file) != null; } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java index 4df11f7261ca..2c5cc5b02282 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java @@ -15,23 +15,20 @@ */ package com.intellij.ide.actions; +import com.intellij.ide.ui.search.SearchUtil; +import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.options.Configurable; -import com.intellij.openapi.options.ConfigurableGroup; -import com.intellij.openapi.options.SearchableConfigurable; -import com.intellij.openapi.options.ShowSettingsUtil; -import com.intellij.openapi.options.ex.ConfigurableExtensionPointUtil; -import com.intellij.openapi.options.ex.IdeConfigurablesGroup; -import com.intellij.openapi.options.ex.MixedConfigurableGroup; -import com.intellij.openapi.options.ex.ProjectConfigurablesGroup; -import com.intellij.openapi.options.ex.SingleConfigurableEditor; +import com.intellij.openapi.options.*; +import com.intellij.openapi.options.ex.*; +import com.intellij.openapi.options.newEditor.IdeSettingsDialog; import com.intellij.openapi.options.newEditor.OptionsEditor; import com.intellij.openapi.options.newEditor.OptionsEditorDialog; -import com.intellij.openapi.options.newEditor.PreferencesDialog; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.navigation.Place; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NotNull; @@ -47,7 +44,7 @@ import java.util.concurrent.atomic.AtomicBoolean; */ public class ShowSettingsUtilImpl extends ShowSettingsUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.ShowSettingsUtilImpl"); - private AtomicBoolean myShown = new AtomicBoolean(false); + private final AtomicBoolean myShown = new AtomicBoolean(false); @NotNull private static Project getProject(@Nullable Project project) { @@ -55,12 +52,16 @@ public class ShowSettingsUtilImpl extends ShowSettingsUtil { } @NotNull - private static DialogWrapper getDialog(@Nullable Project project, @NotNull ConfigurableGroup[] groups, @Nullable Configurable toSelect) { + public static DialogWrapper getDialog(@Nullable Project project, @NotNull ConfigurableGroup[] groups, @Nullable Configurable toSelect) { + project = getProject(project); + final ConfigurableGroup[] filteredGroups = filterEmptyGroups(groups); + if (Registry.is("ide.new.settings.dialog")) { + return new IdeSettingsDialog(project, filteredGroups, toSelect); + } + //noinspection deprecation return Registry.is("ide.perProjectModality") - ? new OptionsEditorDialog(getProject(project), filterEmptyGroups(groups), toSelect, true) - : Registry.is("ide.new.preferences") - ? new PreferencesDialog(getProject(project), filterEmptyGroups(groups)) - : new OptionsEditorDialog(getProject(project), filterEmptyGroups(groups), toSelect); + ? new OptionsEditorDialog(project, filteredGroups, toSelect, true) + : new OptionsEditorDialog(project, filteredGroups, toSelect); } @NotNull @@ -73,7 +74,7 @@ public class ShowSettingsUtilImpl extends ShowSettingsUtil { new ProjectConfigurablesGroup(project), new IdeConfigurablesGroup()}; - return Registry.is("ide.file.settings.order.new") + return Registry.is("ide.new.settings.dialog") ? MixedConfigurableGroup.getGroups(getConfigurables(groups, true)) : groups; } @@ -139,41 +140,34 @@ public class ShowSettingsUtilImpl extends ShowSettingsUtil { @Override public void showSettingsDialog(@Nullable final Project project, @NotNull final String nameToSelect) { - ConfigurableGroup[] group = getConfigurableGroups(project, true); - + ConfigurableGroup[] groups = getConfigurableGroups(project, true); Project actualProject = getProject(project); - group = filterEmptyGroups(group); + groups = filterEmptyGroups(groups); + getDialog(actualProject, groups, findPreselectedByDisplayName(nameToSelect, groups)).show(); + } - OptionsEditorDialog dialog; - if (Registry.is("ide.perProjectModality")) { - dialog = new OptionsEditorDialog(actualProject, group, nameToSelect, true); - } - else { - dialog = new OptionsEditorDialog(actualProject, group, nameToSelect); + @Nullable + private static Configurable findPreselectedByDisplayName(final String preselectedConfigurableDisplayName, ConfigurableGroup[] groups) { + final List<Configurable> all = SearchUtil.expand(groups); + for (Configurable each : all) { + if (preselectedConfigurableDisplayName.equals(each.getDisplayName())) return each; } - dialog.show(); + return null; } public static void showSettingsDialog(@Nullable Project project, final String id2Select, final String filter) { ConfigurableGroup[] group = getConfigurableGroups(project, true); - Project actualProject = getProject(project); - group = filterEmptyGroups(group); final Configurable configurable2Select = findConfigurable2Select(id2Select, group); - final OptionsEditorDialog dialog; - if (Registry.is("ide.perProjectModality")) { - dialog = new OptionsEditorDialog(actualProject, group, configurable2Select, true); - } else { - dialog = new OptionsEditorDialog(actualProject, group, configurable2Select); - } + final DialogWrapper dialog = getDialog(project, group, configurable2Select); new UiNotifyConnector.Once(dialog.getContentPane(), new Activatable.Adapter() { @Override public void showNotify() { - final OptionsEditor editor = (OptionsEditor)dialog.getData(OptionsEditor.KEY.getName()); + final OptionsEditor editor = (OptionsEditor)((DataProvider)dialog).getData(OptionsEditor.KEY.getName()); LOG.assertTrue(editor != null); editor.select(configurable2Select, filter); } @@ -234,37 +228,53 @@ public class ShowSettingsUtilImpl extends ShowSettingsUtil { @Override public <T extends Configurable> T findProjectConfigurable(final Project project, final Class<T> confClass) { + //noinspection deprecation return ConfigurableExtensionPointUtil.findProjectConfigurable(project, confClass); } @Override public boolean editConfigurable(Project project, String dimensionServiceKey, @NotNull Configurable configurable) { - return editConfigurable(null, project, configurable, dimensionServiceKey, null); + return editConfigurable(project, dimensionServiceKey, configurable, isWorthToShowApplyButton(configurable)); + } + + private static boolean isWorthToShowApplyButton(@NotNull Configurable configurable) { + return configurable instanceof Place.Navigator || + configurable instanceof Composite || + configurable instanceof TabbedConfigurable; + } + + @Override + public boolean editConfigurable(Project project, String dimensionServiceKey, @NotNull Configurable configurable, boolean showApplyButton) { + return editConfigurable(null, project, configurable, dimensionServiceKey, null, showApplyButton); } @Override public boolean editConfigurable(Project project, Configurable configurable, Runnable advancedInitialization) { - return editConfigurable(null, project, configurable, createDimensionKey(configurable), advancedInitialization); + return editConfigurable(null, project, configurable, createDimensionKey(configurable), advancedInitialization, isWorthToShowApplyButton(configurable)); } @Override - public boolean editConfigurable(Component parent, Configurable configurable) { + public boolean editConfigurable(@Nullable Component parent, @NotNull Configurable configurable) { return editConfigurable(parent, configurable, null); } @Override - public boolean editConfigurable(final Component parent, final Configurable configurable, @Nullable final Runnable advancedInitialization) { - return editConfigurable(parent, null, configurable, createDimensionKey(configurable), advancedInitialization); + public boolean editConfigurable(@Nullable Component parent, @NotNull Configurable configurable, @Nullable Runnable advancedInitialization) { + return editConfigurable(parent, null, configurable, createDimensionKey(configurable), advancedInitialization, isWorthToShowApplyButton(configurable)); } - private static boolean editConfigurable(final @Nullable Component parent, @Nullable Project project, final Configurable configurable, final String dimensionKey, - @Nullable final Runnable advancedInitialization) { - SingleConfigurableEditor editor; - if (parent != null) { - editor = new SingleConfigurableEditor(parent, configurable, dimensionKey); + private static boolean editConfigurable(@Nullable Component parent, + @Nullable Project project, + @NotNull Configurable configurable, + String dimensionKey, + @Nullable final Runnable advancedInitialization, + boolean showApplyButton) { + final SingleConfigurableEditor editor; + if (parent == null) { + editor = new SingleConfigurableEditor(project, configurable, dimensionKey, showApplyButton); } else { - editor = new SingleConfigurableEditor(project, configurable, dimensionKey); + editor = new SingleConfigurableEditor(parent, configurable, dimensionKey, showApplyButton); } if (advancedInitialization != null) { new UiNotifyConnector.Once(editor.getContentPane(), new Activatable.Adapter() { @@ -278,15 +288,14 @@ public class ShowSettingsUtilImpl extends ShowSettingsUtil { return editor.isOK(); } - public static String createDimensionKey(Configurable configurable) { - String displayName = configurable.getDisplayName(); - displayName = displayName.replaceAll("\n", "_").replaceAll(" ", "_"); - return "#" + displayName; + @NotNull + public static String createDimensionKey(@NotNull Configurable configurable) { + return '#' + StringUtil.replaceChar(StringUtil.replaceChar(configurable.getDisplayName(), '\n', '_'), ' ', '_'); } @Override - public boolean editConfigurable(Component parent, String dimensionServiceKey,Configurable configurable) { - return editConfigurable(parent, null, configurable, dimensionServiceKey, null); + public boolean editConfigurable(Component parent, String dimensionServiceKey, Configurable configurable) { + return editConfigurable(parent, null, configurable, dimensionServiceKey, null, isWorthToShowApplyButton(configurable)); } public boolean isAlreadyShown() { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ViewSourceAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ViewSourceAction.java index a9461422f747..825db992b218 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ViewSourceAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ViewSourceAction.java @@ -17,9 +17,21 @@ package com.intellij.ide.actions; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; public class ViewSourceAction extends BaseNavigateToSourceAction { public ViewSourceAction() { super(false); } + + @Override + public void update(AnActionEvent e) { + if (CommonDataKeys.EDITOR.getData(e.getDataContext()) != null) { + e.getPresentation().setEnabledAndVisible(false); + } + else { + super.update(e); + } + } } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java b/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java index 6844ed5bab25..798247e16186 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java @@ -21,12 +21,14 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.border.Border; import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; public abstract class AbstractCustomizeWizardStep extends JPanel { + protected static final int SMALL_GAP = 10; protected static final int GAP = 20; protected abstract String getTitle(); @@ -40,6 +42,14 @@ public abstract class AbstractCustomizeWizardStep extends JPanel { return ColorUtil.mix(UIUtil.getListSelectionBackground(), UIUtil.getLabelBackground(), UIUtil.isUnderDarcula() ? .5 : .75); } + public static Border createSmallEmptyBorder() { + return BorderFactory.createEmptyBorder(SMALL_GAP, SMALL_GAP, SMALL_GAP, SMALL_GAP); + } + + public static BorderLayout createSmallBorderLayout() { + return new BorderLayout(SMALL_GAP, SMALL_GAP); + } + protected static JPanel createBigButtonPanel(LayoutManager layout, final JToggleButton anchorButton, final Runnable action) { final JPanel panel = new JPanel(layout) { @Override diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeDesktopEntryStep.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeDesktopEntryStep.java new file mode 100644 index 000000000000..57b68232b797 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeDesktopEntryStep.java @@ -0,0 +1,108 @@ +/* + * Copyright 2000-2014 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.ide.customize; + +import com.intellij.ide.actions.CreateDesktopEntryAction; +import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.openapi.util.EmptyRunnable; +import com.intellij.openapi.util.IconLoader; +import com.intellij.util.ui.GridBag; +import com.intellij.util.ui.UIUtil; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; + +/** + * @author Alexander Lobas + */ +public class CustomizeDesktopEntryStep extends AbstractCustomizeWizardStep { + private final JCheckBox myCreateEntryCheckBox = new JCheckBox(ActionsBundle.message("action.CreateDesktopEntry.description")); + private final JCheckBox myGlobalEntryCheckBox = new JCheckBox("For all users"); + + public CustomizeDesktopEntryStep(String iconPath) { + setLayout(new BorderLayout()); + + JPanel panel = createBigButtonPanel(createSmallBorderLayout(), myCreateEntryCheckBox, EmptyRunnable.INSTANCE); + panel.setBorder(createSmallEmptyBorder()); + + JPanel buttonPanel = new JPanel(new GridBagLayout()); + buttonPanel.setOpaque(false); + + GridBag gbc = + new GridBag().setDefaultAnchor(GridBagConstraints.WEST).setDefaultFill(GridBagConstraints.HORIZONTAL).setDefaultWeightX(1); + + myCreateEntryCheckBox.setOpaque(false); + buttonPanel.add(myCreateEntryCheckBox, gbc.nextLine()); + + myGlobalEntryCheckBox.setOpaque(false); + gbc.nextLine().insets.left = UIUtil.PANEL_REGULAR_INSETS.left; + buttonPanel.add(myGlobalEntryCheckBox, gbc); + + panel.add(buttonPanel, BorderLayout.NORTH); + + JLabel label = new JLabel(IconLoader.getIcon(iconPath)); + label.setVerticalAlignment(JLabel.TOP); + panel.add(label, BorderLayout.CENTER); + + add(panel, BorderLayout.CENTER); + + myCreateEntryCheckBox.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + myGlobalEntryCheckBox.setEnabled(myCreateEntryCheckBox.isSelected()); + myGlobalEntryCheckBox.setSelected(myCreateEntryCheckBox.isSelected() && !PathManager.getHomePath().startsWith("/home")); + } + }); + + myCreateEntryCheckBox.setSelected(true); + } + + public static boolean isAvailable() { + return CreateDesktopEntryAction.isAvailable(); + } + + @Override + public boolean beforeOkAction() { + if (myCreateEntryCheckBox.isSelected()) { + try { + CreateDesktopEntryAction.createDesktopEntry(null, new EmptyProgressIndicator(), myGlobalEntryCheckBox.isSelected()); + } + catch (Throwable e) { + // ignored + } + } + return true; + } + + @Override + protected String getTitle() { + return "Desktop Entry"; + } + + @Override + protected String getHTMLHeader() { + return "<html><body><h2>Create Desktop Entry</h2> </body></html>"; + } + + @Override + protected String getHTMLFooter() { + return "Desktop entry can be created later in Tools | Create Desktop Entry..."; + } +}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java index 786a5825e783..498391b8e971 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java @@ -18,7 +18,9 @@ package com.intellij.ide.customize; import com.intellij.CommonBundle; import com.intellij.icons.AllIcons; import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.plugins.PluginNode; +import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.progress.util.ProgressIndicatorBase; import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.updateSettings.impl.PluginDownloader; @@ -78,7 +80,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt final String pluginId = s.substring(j + 1); IdeaPluginDescriptor foundDescriptor = null; for (IdeaPluginDescriptor descriptor : pluginsFromRepository) { - if (descriptor.getPluginId().getIdString().equals(pluginId)) { + if (descriptor.getPluginId().getIdString().equals(pluginId) && !PluginManagerCore.isBrokenPlugin(descriptor)) { foundDescriptor = descriptor; isEmptyOrOffline = false; break; @@ -107,11 +109,11 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt JPanel progressPanel = new JPanel(new VerticalFlowLayout(true, false)); progressPanel.add(progressBar); final LinkLabel cancelLink = new LinkLabel("Cancel", AllIcons.Actions.Cancel); - JPanel linkWrapper = new JPanel(new FlowLayout(FlowLayout.CENTER)); + JPanel linkWrapper = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); linkWrapper.add(cancelLink); progressPanel.add(linkWrapper); - JPanel buttonPanel = new JPanel(new VerticalFlowLayout()); + JPanel buttonPanel = new JPanel(new VerticalFlowLayout(0, 0)); buttonPanel.add(installButton); buttonWrapper.add(buttonPanel, "button"); @@ -218,7 +220,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt }, null); gbc.insets.bottom = -5; groupPanel.add(titleLabel, gbc); - gbc.insets.bottom = 10; + gbc.insets.bottom = SMALL_GAP; groupPanel.add(topicLabel, gbc); groupPanel.add(descriptionLabel, gbc); gbc.weighty = 1; @@ -238,7 +240,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt protected Color getColor() { return ColorUtil.withAlpha(JBColor.foreground(), .2); } - }, BorderFactory.createEmptyBorder(GAP, GAP, 0, GAP))); + }, BorderFactory.createEmptyBorder(0, SMALL_GAP, 0, SMALL_GAP))); cursor++; } @@ -260,7 +262,10 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt @Override public String getHTMLFooter() { - return "New plugins can also be downloaded in " + CommonBundle.settingsTitle() + " | Plugins"; + return "New plugins can also be downloaded in " + + CommonBundle.settingsTitle() + + " | " + OptionsBundle.message("configurable.group.appearance.settings.display.name") + + " | " + "Plugins"; } public static class OfflineException extends Exception {}; diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java index be37f9d9d7f6..af3c193ca9d9 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java @@ -21,7 +21,7 @@ import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.JBCardLayout; -import org.jetbrains.annotations.NotNull; +import com.intellij.util.PlatformUtils; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -51,6 +51,7 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis public CustomizeIDEWizardDialog() { super(null, true, true); setTitle("Customize " + ApplicationNamesInfo.getInstance().getProductName()); + getPeer().setAppIcons(); initSteps(); mySkipButton.addActionListener(this); myBackButton.addActionListener(this); @@ -119,7 +120,7 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis result.add(myContentPanel, BorderLayout.CENTER); result.add(myFooterLabel, BorderLayout.SOUTH); result.setPreferredSize(new Dimension(700, 600)); - result.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + result.setBorder(AbstractCustomizeWizardStep.createSmallEmptyBorder()); return result; } @@ -131,8 +132,10 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis gbc.fill = GridBagConstraints.BOTH; gbc.gridx = 0; gbc.gridy = 0; - buttonPanel.add(mySkipButton, gbc); - gbc.gridx++; + if (!PlatformUtils.isCLion()) { + buttonPanel.add(mySkipButton, gbc); + gbc.gridx++; + } buttonPanel.add(myBackButton, gbc); gbc.gridx++; gbc.weightx = 1; @@ -219,15 +222,4 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis } myNavigationLabel.setText(navHTML.toString()); } - - - private static <T extends Component> void getChildren(@NotNull Component c, Class<? extends T> cls, List<T> accumulator) { - if (cls.isAssignableFrom(c.getClass())) accumulator.add((T)c); - if (c instanceof Container) { - Component[] components = ((Container)c).getComponents(); - for (Component component : components) { - getChildren(component, cls, accumulator); - } - } - } } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java index 8924c09780dd..de12269b5887 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java @@ -115,7 +115,7 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple gbc.weighty = 1; groupPanel.add(Box.createVerticalGlue(), gbc); gbc.weighty = 0; - JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 5)); + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, SMALL_GAP, SMALL_GAP / 2)); buttonsPanel.setOpaque(false); if (pluginGroups.getSets(group).size() == 1) { buttonsPanel.add(createLink(SWITCH_COMMAND + ":" + group, getGroupSwitchTextProvider(group))); @@ -139,7 +139,7 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple protected Color getColor() { return ColorUtil.withAlpha(JBColor.foreground(), .2); } - }, BorderFactory.createEmptyBorder(GAP / 2, GAP, GAP / 2, GAP))); + }, BorderFactory.createEmptyBorder(SMALL_GAP, GAP, SMALL_GAP, GAP))); cursor++; } } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java index b369e49f8bca..c7b29ef53827 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java @@ -22,6 +22,7 @@ import com.intellij.ide.ui.laf.darcula.DarculaLaf; import com.intellij.ide.ui.laf.darcula.DarculaLookAndFeelInfo; import com.intellij.idea.StartupUtil; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.util.IconUtil; @@ -46,7 +47,7 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { private Map<String, Icon> myLafNames = new LinkedHashMap<String, Icon>(); public CustomizeUIThemeStepPanel() { - setLayout(new BorderLayout(10, 10)); + setLayout(createSmallBorderLayout()); IconLoader.activate(); initLafs(); @@ -65,13 +66,13 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { radioButton.setSelected(true); myDefaultLafName = lafName; } - final JPanel panel = createBigButtonPanel(new BorderLayout(10, 10), radioButton, new Runnable() { + final JPanel panel = createBigButtonPanel(createSmallBorderLayout(), radioButton, new Runnable() { @Override public void run() { applyLaf(lafName, CustomizeUIThemeStepPanel.this); } }); - panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + panel.setBorder(createSmallEmptyBorder()); panel.add(radioButton, myColumnMode ? BorderLayout.WEST : BorderLayout.NORTH); final JLabel label = new JLabel(myColumnMode ? IconUtil.scale(IconUtil.cropIcon(icon, icon.getIconWidth() * 2 / 3, icon.getIconHeight() * 2 / 3), .5) : icon); label.setVerticalAlignment(SwingConstants.TOP); @@ -139,7 +140,10 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { @Override public String getHTMLFooter() { - return "UI theme can be changed later in " + CommonBundle.settingsTitle() + " | Appearance"; + return "UI theme can be changed later in " + + CommonBundle.settingsTitle() + + " | " + OptionsBundle.message("configurable.group.appearance.settings.display.name") + + " | " + "Appearance"; } private void applyLaf(String lafName, Component component) { diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java index 6b0b9814698b..de5d67b5e2d8 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java @@ -15,6 +15,8 @@ */ package com.intellij.ide.passwordSafe.impl.providers.masterKey; +import com.intellij.concurrency.AsyncFutureFactory; +import com.intellij.concurrency.AsyncFutureResult; import com.intellij.ide.passwordSafe.MasterPasswordUnavailableException; import com.intellij.ide.passwordSafe.PasswordSafeException; import com.intellij.ide.passwordSafe.impl.PasswordSafeTimed; @@ -23,14 +25,11 @@ import com.intellij.ide.passwordSafe.impl.providers.ByteArrayWrapper; import com.intellij.ide.passwordSafe.impl.providers.EncryptionUtil; import com.intellij.ide.passwordSafe.impl.providers.masterKey.windows.WindowsCryptUtils; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.ThrowableComputable; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,6 +37,7 @@ import org.jetbrains.annotations.Nullable; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutionException; /** * The password safe that stores information in configuration file encrypted by master password @@ -170,12 +170,6 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider { throw new MasterPasswordUnavailableException("The provider is not available in headless environment"); } - if (myDatabase.isEmpty()) { - if (!MasterPasswordDialog.resetMasterPasswordDialog(project, this, requestor).showAndGet()) { - throw new MasterPasswordUnavailableException("Master password is required to store passwords in the database."); - } - } - key = invokeAndWait(new ThrowableComputable<Object, PasswordSafeException>() { @Override public Object compute() throws PasswordSafeException { @@ -184,7 +178,14 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider { return key; } try { - MasterPasswordDialog.askPassword(project, MasterKeyPasswordSafe.this, requestor); + if (myDatabase.isEmpty()) { + if (!MasterPasswordDialog.resetMasterPasswordDialog(project, MasterKeyPasswordSafe.this, requestor).showAndGet()) { + throw new MasterPasswordUnavailableException("Master password is required to store passwords in the database."); + } + } + else { + MasterPasswordDialog.askPassword(project, MasterKeyPasswordSafe.this, requestor); + } } catch (PasswordSafeException e) { myKey.get().set(e); @@ -200,35 +201,41 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider { } private static final Object ourEDTLock = new Object(); - public <T, E extends Throwable> T invokeAndWait(@NotNull final ThrowableComputable<T, E> computable, @NotNull final Condition expired) throws E { + public <T, E extends Throwable> T invokeAndWait(@NotNull final ThrowableComputable<T, E> computable, @NotNull final Condition<?> expired) throws E { if (ApplicationManager.getApplication().isDispatchThread()) { return computable.compute(); } - final Ref<Throwable> exRef = Ref.create(); - final Ref<T> ref = Ref.create(); + + final AsyncFutureResult<Object> future = AsyncFutureFactory.getInstance().createAsyncFutureResult(); synchronized (ourEDTLock) { - if (expired.value(null)) { - throw new ProcessCanceledException(); - } - ApplicationManager.getApplication().invokeAndWait(new Runnable() { + IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(new ExpirableRunnable() { @Override - public void run() { - if (expired.value(null)) { - exRef.set(new ProcessCanceledException()); - return; - } + public boolean isExpired() { + boolean b = expired.value(null); + if (b) future.setException(new ProcessCanceledException()); + return b; + } + @Override + public void run() { try { - ref.set(computable.compute()); + future.set(computable.compute()); } catch (Throwable e) { - exRef.set(e); + future.setException(e); } } - }, ModalityState.any()); + }); + } + try { + return (T)future.get(); + } + catch (InterruptedException e) { + throw new ProcessCanceledException(e); + } + catch (ExecutionException e) { + throw (E) e.getCause(); } - if (!exRef.isNull()) throw (E)exRef.get(); - return ref.get(); } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/AvailablePluginsManagerMain.java b/platform/platform-impl/src/com/intellij/ide/plugins/AvailablePluginsManagerMain.java index 62609d9ba5fc..0ae246d41715 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/AvailablePluginsManagerMain.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/AvailablePluginsManagerMain.java @@ -29,7 +29,7 @@ import com.intellij.openapi.updateSettings.impl.UpdateSettings; import com.intellij.ui.DoubleClickListener; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.TableUtil; -import com.intellij.util.net.HTTPProxySettingsDialog; +import com.intellij.util.net.HttpConfigurable; import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NotNull; @@ -41,6 +41,7 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.TreeSet; /** @@ -50,7 +51,7 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { public static final String MANAGE_REPOSITORIES = "Manage repositories..."; public static final String N_A = "N/A"; - private PluginManagerMain installed; + private final PluginManagerMain installed; private final String myVendorFilter; public AvailablePluginsManagerMain(PluginManagerMain installed, PluginManagerUISettings uiSettings, String vendorFilter) { @@ -63,11 +64,11 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { manageRepositoriesBtn.setMnemonic('m'); manageRepositoriesBtn.addActionListener(new ActionListener() { @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(@NotNull ActionEvent e) { if (ShowSettingsUtil.getInstance().editConfigurable(myActionsPanel, new PluginHostsConfigurable())) { final ArrayList<String> pluginHosts = UpdateSettings.getInstance().myPluginHosts; if (!pluginHosts.contains(((AvailablePluginsTableModel)pluginsModel).getRepository())) { - ((AvailablePluginsTableModel)pluginsModel).setRepository(AvailablePluginsTableModel.ALL, myFilter.getFilter().toLowerCase()); + ((AvailablePluginsTableModel)pluginsModel).setRepository(AvailablePluginsTableModel.ALL, myFilter.getFilter().toLowerCase(Locale.ENGLISH)); } loadAvailablePlugins(); } @@ -78,11 +79,9 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { final JButton httpProxySettingsButton = new JButton(IdeBundle.message("button.http.proxy.settings")); httpProxySettingsButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - HTTPProxySettingsDialog settingsDialog = new HTTPProxySettingsDialog(); - settingsDialog.pack(); - settingsDialog.show(); - if (settingsDialog.isOK()) { + @Override + public void actionPerformed(@NotNull ActionEvent e) { + if (HttpConfigurable.editConfigurable(getMainPanel())) { loadAvailablePlugins(); } } @@ -114,7 +113,8 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { pluginTable.registerKeyboardAction( new ActionListener() { - public void actionPerformed(ActionEvent e) { + @Override + public void actionPerformed(@NotNull ActionEvent e) { installSelected(pluginTable); } }, @@ -235,7 +235,7 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { return new AnAction(availableCategory) { @Override public void actionPerformed(AnActionEvent e) { - final String filter = myFilter.getFilter().toLowerCase(); + final String filter = myFilter.getFilter().toLowerCase(Locale.ENGLISH); ((AvailablePluginsTableModel)pluginsModel).setCategory(availableCategory, filter); } }; @@ -277,7 +277,7 @@ public class AvailablePluginsManagerMain extends PluginManagerMain { return new AnAction(host) { @Override public void actionPerformed(AnActionEvent e) { - final String filter = myFilter.getFilter().toLowerCase(); + final String filter = myFilter.getFilter().toLowerCase(Locale.ENGLISH); ((AvailablePluginsTableModel)pluginsModel).setRepository(host, filter); TableUtil.ensureSelectionExists(getPluginTable()); } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java index a1ea238835cd..57329fb8fe9d 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java @@ -15,6 +15,7 @@ */ package com.intellij.ide.plugins; +import com.intellij.diagnostic.PluginException; import com.intellij.ide.ClassUtilCore; import com.intellij.ide.IdeBundle; import com.intellij.idea.IdeaApplication; @@ -100,31 +101,44 @@ public class PluginManager extends PluginManagerCore { public static void processException(Throwable t) { if (!IdeaApplication.isLoaded()) { - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") StartupAbortedException se = findCause(t); + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") StartupAbortedException se = findCause(t, StartupAbortedException.class); if (se == null) se = new StartupAbortedException(t); + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") PluginException pe = findCause(t, PluginException.class); + PluginId pluginId = pe != null ? pe.getPluginId() : null; - if (se.logError()) { + if (Logger.isInitialized() && !(t instanceof ProcessCanceledException)) { try { - if (Logger.isInitialized() && !(t instanceof ProcessCanceledException)) { - getLogger().error(t); - } + getLogger().error(t); } catch (Throwable ignore) { } + } + + if (pluginId != null && !CORE_PLUGIN_ID.equals(pluginId.getIdString())) { + disablePlugin(pluginId.getIdString()); + + StringWriter message = new StringWriter(); + message.append("Plugin '").append(pluginId.getIdString()).append("' failed to initialize and will be disabled. "); + message.append(" Please restart ").append(ApplicationNamesInfo.getInstance().getFullProductName()).append('.'); + message.append("\n\n"); + pe.getCause().printStackTrace(new PrintWriter(message)); + Main.showMessage("Plugin Error", message.toString(), false); + System.exit(Main.PLUGIN_ERROR); + } + else { Main.showMessage("Start Failed", t); + System.exit(se.exitCode()); } - - System.exit(se.exitCode()); } else if (!(t instanceof ProcessCanceledException)) { getLogger().error(t); } } - private static StartupAbortedException findCause(Throwable t) { + private static <T extends Throwable> T findCause(Throwable t, Class<T> clazz) { while (t != null) { - if (t instanceof StartupAbortedException) { - return (StartupAbortedException)t; + if (clazz.isInstance(t)) { + return clazz.cast(t); } t = t.getCause(); } @@ -229,18 +243,7 @@ public class PluginManager extends PluginManagerCore { } if (pluginId != null && !CORE_PLUGIN_ID.equals(pluginId.getIdString())) { - getLogger().warn(t); - - disablePlugin(pluginId.getIdString()); - - StringWriter message = new StringWriter(); - message.append("Plugin '").append(pluginId.getIdString()).append("' failed to initialize and will be disabled. "); - message.append(" Please restart ").append(ApplicationNamesInfo.getInstance().getFullProductName()).append('.'); - message.append("\n\n"); - t.printStackTrace(new PrintWriter(message)); - Main.showMessage("Plugin Error", message.toString(), false); - - throw new StartupAbortedException(t).exitCode(Main.PLUGIN_ERROR).logError(false); + throw new StartupAbortedException(new PluginException(t, pluginId)); } else { throw new StartupAbortedException("Fatal error initializing '" + componentClassName + "'", t); @@ -249,7 +252,6 @@ public class PluginManager extends PluginManagerCore { private static class StartupAbortedException extends RuntimeException { private int exitCode = Main.STARTUP_EXCEPTION; - private boolean logError = true; public StartupAbortedException(Throwable cause) { super(cause); @@ -267,14 +269,5 @@ public class PluginManager extends PluginManagerCore { this.exitCode = exitCode; return this; } - - public boolean logError() { - return logError; - } - - public StartupAbortedException logError(boolean logError) { - this.logError = logError; - return this; - } } } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form index 59197a00e70b..550e8e2482e1 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form @@ -74,7 +74,7 @@ </component> </children> </grid> - <grid id="d9fb" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="d9fb" layout-manager="GridLayoutManager" row-count="1" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> @@ -82,17 +82,6 @@ <properties/> <border type="none"/> <children> - <component id="5ea1e" class="javax.swing.JComboBox" binding="myFontSizeCombo" custom-create="true"> - <constraints> - <grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"> - <minimum-size width="140" height="-1"/> - <preferred-size width="140" height="-1"/> - </grid> - </constraints> - <properties> - <editable value="true"/> - </properties> - </component> <component id="76cd" class="javax.swing.JComboBox" binding="myFontCombo"> <constraints> <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/> @@ -107,15 +96,6 @@ <requestFocusEnabled value="true"/> </properties> </component> - <component id="4eb1b" class="javax.swing.JLabel" binding="myFontSizeLabel"> - <constraints> - <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/> - </constraints> - <properties> - <horizontalAlignment value="10"/> - <text resource-bundle="messages/IdeBundle" key="label.font.size"/> - </properties> - </component> <component id="23221" class="javax.swing.JLabel" binding="myFontNameLabel"> <constraints> <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/> @@ -127,11 +107,30 @@ </component> <hspacer id="53ae4"> <constraints> - <grid row="0" column="0" row-span="2" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"> + <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"> <minimum-size width="20" height="-1"/> </grid> </constraints> </hspacer> + <component id="4eb1b" class="javax.swing.JLabel" binding="myFontSizeLabel"> + <constraints> + <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <horizontalAlignment value="10"/> + <text resource-bundle="messages/IdeBundle" key="label.font.size"/> + </properties> + </component> + <component id="5ea1e" class="javax.swing.JComboBox" binding="myFontSizeCombo" custom-create="true"> + <constraints> + <grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="40" height="-1"/> + </grid> + </constraints> + <properties> + <editable value="true"/> + </properties> + </component> </children> </grid> <grid id="ce348" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> diff --git a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java index bbd52139bd51..7ebf2a57a998 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.ide.util; +import com.intellij.CommonBundle; import com.intellij.ide.BrowserUtil; import com.intellij.ide.IdeBundle; import com.intellij.ide.plugins.IdeaPluginDescriptor; @@ -97,6 +98,7 @@ public class TipUIUtil { String minor = ApplicationInfo.getInstance().getMinorVersion(); replaced = replaced.replace("&minorVersion;", minor); replaced = replaced.replace("&majorMinorVersion;", major + ("0".equals(minor) ? "" : ("." + minor))); + replaced = replaced.replace("&settingsPath;", CommonBundle.settingsActionPath()); if (UIUtil.isUnderDarcula()) { replaced = replaced.replace("css/tips.css", "css/tips_darcula.css"); } diff --git a/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java b/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java index 94b25a1b3c94..b32729dd7328 100644 --- a/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java +++ b/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java @@ -24,10 +24,7 @@ import com.intellij.ide.IdeRepaintManager; import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.internal.statistic.UsageTrigger; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ApplicationStarter; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.*; import com.intellij.openapi.application.ex.ApplicationEx; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.application.ex.ApplicationManagerEx; @@ -107,6 +104,12 @@ public class IdeaApplication { if (myStarter == null) { myStarter = getStarter(); } + + if (headless && myStarter instanceof ApplicationStarterEx && !((ApplicationStarterEx)myStarter).isHeadless()) { + Main.showMessage("Startup Error", "Application cannot start in headless mode", true); + System.exit(Main.STARTUP_IMPOSSIBLE); + } + myStarter.premain(args); } @@ -178,10 +181,15 @@ public class IdeaApplication { catch (ClassNotFoundException ignored) { } } - protected class IdeStarter implements ApplicationStarter { + protected class IdeStarter extends ApplicationStarterEx { private Splash mySplash; @Override + public boolean isHeadless() { + return false; + } + + @Override public String getCommandName() { return null; } diff --git a/platform/platform-impl/src/com/intellij/notification/impl/actions/ShowDelayedMessageInternalAction.java b/platform/platform-impl/src/com/intellij/notification/impl/actions/ShowDelayedMessageInternalAction.java new file mode 100644 index 000000000000..ad133f1c7574 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/notification/impl/actions/ShowDelayedMessageInternalAction.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2014 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.notification.impl.actions; + +import com.intellij.CommonBundle; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.ui.MessageDialogBuilder; + +import javax.swing.*; + +/** + * @author Denis Fokin + */ +public class ShowDelayedMessageInternalAction extends AnAction implements DumbAware{ + @Override + public void actionPerformed(AnActionEvent e) { + + new Thread() { + @Override + public void run() { + super.run(); + + //noinspection EmptyCatchBlock + try { + Thread.sleep(3000); + } + catch (InterruptedException e1) {} // does not matter for an internal action + + //noinspection SSBasedInspection + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + MessageDialogBuilder.yesNo("Nothing happens after that", "Some message goes here").yesText( + ApplicationBundle.message("command.exit")).noText( + CommonBundle.message("button.cancel")).show(); + } + }); + } + }.start(); + + } +} 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 a17370e73266..bf7c79695c94 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 @@ -24,6 +24,7 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.ex.DecodeDefaultsUtil; import com.intellij.openapi.components.ExportableApplicationComponent; import com.intellij.openapi.components.RoamingType; +import com.intellij.openapi.components.StoragePathMacros; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.options.BaseSchemeProcessor; import com.intellij.openapi.options.SchemesManager; @@ -63,7 +64,7 @@ public class QuickListsManager implements ExportableApplicationComponent, NamedJ public QuickListsManager(ActionManagerEx actionManagerEx, SchemesManagerFactory schemesManagerFactory) { myActionManager = actionManagerEx; mySchemesManager = schemesManagerFactory.createSchemesManager( - "$ROOT_CONFIG$/quicklists", + StoragePathMacros.ROOT_CONFIG + "/quicklists", new BaseSchemeProcessor<QuickList>(){ public QuickList readScheme(@NotNull final Document schemeContent) throws InvalidDataException, IOException, JDOMException { return loadListFromDocument(schemeContent); diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java index 723956ef9daa..c5a9b01c4d21 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java @@ -39,6 +39,7 @@ import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.keymap.ex.KeymapManagerEx; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.ProjectType; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; @@ -71,28 +72,6 @@ import java.util.*; import java.util.List; public final class ActionManagerImpl extends ActionManagerEx implements ApplicationComponent { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.actionSystem.impl.ActionManagerImpl"); - private static final int DEACTIVATED_TIMER_DELAY = 5000; - private static final int TIMER_DELAY = 500; - private static final int UPDATE_DELAY_AFTER_TYPING = 500; - - private final Object myLock = new Object(); - private final Map<String,Object> myId2Action = new THashMap<String, Object>(); - private final Map<PluginId, THashSet<String>> myPlugin2Id = new THashMap<PluginId, THashSet<String>>(); - private final TObjectIntHashMap<String> myId2Index = new TObjectIntHashMap<String>(); - private final Map<Object,String> myAction2Id = new THashMap<Object, String>(); - private final MultiMap<String,String> myId2GroupId = new MultiMap<String, String>(); - private final List<String> myNotRegisteredInternalActionIds = new ArrayList<String>(); - private MyTimer myTimer; - - private int myRegisteredActionsCount; - private final List<AnActionListener> myActionListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private String myLastPreformedActionId; - private final KeymapManager myKeymapManager; - private final DataManager myDataManager; - private String myPrevPerformedActionId; - private long myLastTimeEditorWasTypedIn = 0; - @NonNls public static final String ACTION_ELEMENT_NAME = "action"; @NonNls public static final String GROUP_ELEMENT_NAME = "group"; @NonNls public static final String ACTIONS_ELEMENT_NAME = "actions"; @@ -130,14 +109,32 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @NonNls public static final String USE_SHORTCUT_OF_ATTR_NAME = "use-shortcut-of"; @NonNls public static final String OVERRIDES_ATTR_NAME = "overrides"; @NonNls public static final String KEEP_CONTENT_ATTR_NAME = "keep-content"; - + @NonNls public static final String PROJECT_TYPE = "project-type"; + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.actionSystem.impl.ActionManagerImpl"); + private static final int DEACTIVATED_TIMER_DELAY = 5000; + private static final int TIMER_DELAY = 500; + private static final int UPDATE_DELAY_AFTER_TYPING = 500; + private final Object myLock = new Object(); + private final Map<String,AnAction> myId2Action = new THashMap<String, AnAction>(); + private final Map<PluginId, THashSet<String>> myPlugin2Id = new THashMap<PluginId, THashSet<String>>(); + private final TObjectIntHashMap<String> myId2Index = new TObjectIntHashMap<String>(); + private final Map<Object,String> myAction2Id = new THashMap<Object, String>(); + private final MultiMap<String,String> myId2GroupId = new MultiMap<String, String>(); + private final List<String> myNotRegisteredInternalActionIds = new ArrayList<String>(); + private final List<AnActionListener> myActionListeners = ContainerUtil.createLockFreeCopyOnWriteList(); + private final KeymapManager myKeymapManager; + private final DataManager myDataManager; private final List<ActionPopupMenuImpl> myPopups = new ArrayList<ActionPopupMenuImpl>(); - private final Map<AnAction, DataContext> myQueuedNotifications = new LinkedHashMap<AnAction, DataContext>(); private final Map<AnAction, AnActionEvent> myQueuedNotificationsEvents = new LinkedHashMap<AnAction, AnActionEvent>(); - + private MyTimer myTimer; + private int myRegisteredActionsCount; + private String myLastPreformedActionId; + private String myPrevPerformedActionId; + private long myLastTimeEditorWasTypedIn = 0; private Runnable myPreloadActionsRunnable; private boolean myTransparentOnlyUpdate; + private int myActionsPreloaded = 0; ActionManagerImpl(KeymapManager keymapManager, DataManager dataManager) { myKeymapManager = keymapManager; @@ -146,6 +143,257 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat registerPluginActions(); } + static AnAction convertStub(ActionStub stub) { + Object obj; + String className = stub.getClassName(); + try { + Class<?> aClass = Class.forName(className, true, stub.getLoader()); + obj = ReflectionUtil.newInstance(aClass); + } + catch (ClassNotFoundException e) { + PluginId pluginId = stub.getPluginId(); + if (pluginId != null) { + throw new PluginException("class with name \"" + className + "\" not found", e, pluginId); + } + else { + throw new IllegalStateException("class with name \"" + className + "\" not found"); + } + } + catch(UnsupportedClassVersionError e) { + PluginId pluginId = stub.getPluginId(); + if (pluginId != null) { + throw new PluginException(e, pluginId); + } + else { + throw new IllegalStateException(e); + } + } + catch (Exception e) { + PluginId pluginId = stub.getPluginId(); + if (pluginId != null) { + throw new PluginException("cannot create class \"" + className + "\"", e, pluginId); + } + else { + throw new IllegalStateException("cannot create class \"" + className + "\"", e); + } + } + + if (!(obj instanceof AnAction)) { + throw new IllegalStateException("class with name '" + className + "' must be an instance of '" + AnAction.class.getName()+"'; got "+obj); + } + + AnAction anAction = (AnAction)obj; + stub.initAction(anAction); + if (StringUtil.isNotEmpty(stub.getText())) { + anAction.getTemplatePresentation().setText(stub.getText()); + } + String iconPath = stub.getIconPath(); + if (iconPath != null) { + Class<? extends AnAction> actionClass = anAction.getClass(); + setIconFromClass(actionClass, actionClass.getClassLoader(), iconPath, anAction.getTemplatePresentation(), stub.getPluginId()); + } + return anAction; + } + + private static void processAbbreviationNode(Element e, String id) { + final String abbr = e.getAttributeValue(VALUE_ATTR_NAME); + if (!StringUtil.isEmpty(abbr)) { + final AbbreviationManagerImpl abbreviationManager = ((AbbreviationManagerImpl)AbbreviationManager.getInstance()); + abbreviationManager.register(abbr, id, true); + } + } + + @Nullable + private static ResourceBundle getActionsResourceBundle(ClassLoader loader, IdeaPluginDescriptor plugin) { + @NonNls final String resBundleName = plugin != null && !"com.intellij".equals(plugin.getPluginId().getIdString()) + ? plugin.getResourceBundleBaseName() : ACTIONS_BUNDLE; + ResourceBundle bundle = null; + if (resBundleName != null) { + bundle = AbstractBundle.getResourceBundle(resBundleName, loader); + } + return bundle; + } + + private static boolean isSecondary(Element element) { + return "true".equalsIgnoreCase(element.getAttributeValue(SECONDARY)); + } + + private static void setIcon(@Nullable final String iconPath, + @NotNull String className, + @NotNull ClassLoader loader, + @NotNull Presentation presentation, + final PluginId pluginId) { + if (iconPath == null) return; + + try { + final Class actionClass = Class.forName(className, true, loader); + setIconFromClass(actionClass, loader, iconPath, presentation, pluginId); + } + catch (ClassNotFoundException e) { + LOG.error(e); + reportActionError(pluginId, "class with name \"" + className + "\" not found"); + } + catch (NoClassDefFoundError e) { + LOG.error(e); + reportActionError(pluginId, "class with name \"" + className + "\" not found"); + } + } + + private static void setIconFromClass(@NotNull final Class actionClass, + @NotNull final ClassLoader classLoader, + @NotNull final String iconPath, + @NotNull Presentation presentation, + final PluginId pluginId) { + final IconLoader.LazyIcon lazyIcon = new IconLoader.LazyIcon() { + @Override + protected Icon compute() { + //try to find icon in idea class path + Icon icon = IconLoader.findIcon(iconPath, actionClass, true); + if (icon == null) { + icon = IconLoader.findIcon(iconPath, classLoader); + } + + if (icon == null) { + reportActionError(pluginId, "Icon cannot be found in '" + iconPath + "', action '" + actionClass + "'"); + } + + return icon; + } + + @Override + public String toString() { + return "LazyIcon@ActionManagerImpl (path: " + iconPath + ", action class: " + actionClass + ")"; + } + }; + + if (!Registry.is("ide.lazyIconLoading")) { + lazyIcon.load(); + } + + presentation.setIcon(lazyIcon); + } + + private static String loadDescriptionForElement(final Element element, final ResourceBundle bundle, final String id, String elementType) { + final String value = element.getAttributeValue(DESCRIPTION); + if (bundle != null) { + @NonNls final String key = elementType + "." + id + ".description"; + return CommonBundle.messageOrDefault(bundle, key, value == null ? "" : value); + } else { + return value; + } + } + + private static String loadTextForElement(final Element element, final ResourceBundle bundle, final String id, String elementType) { + final String value = element.getAttributeValue(TEXT_ATTR_NAME); + return CommonBundle.messageOrDefault(bundle, elementType + "." + id + "." + TEXT_ATTR_NAME, value == null ? "" : value); + } + + public static boolean checkRelativeToAction(final String relativeToActionId, + @NotNull final Anchor anchor, + @NotNull final String actionName, + @Nullable final PluginId pluginId) { + if ((Anchor.BEFORE == anchor || Anchor.AFTER == anchor) && relativeToActionId == null) { + reportActionError(pluginId, actionName + ": \"relative-to-action\" cannot be null if anchor is \"after\" or \"before\""); + return false; + } + return true; + } + + @Nullable + public static Anchor parseAnchor(final String anchorStr, + @Nullable final String actionName, + @Nullable final PluginId pluginId) { + if (anchorStr == null) { + return Anchor.LAST; + } + + if (FIRST.equalsIgnoreCase(anchorStr)) { + return Anchor.FIRST; + } + else if (LAST.equalsIgnoreCase(anchorStr)) { + return Anchor.LAST; + } + else if (BEFORE.equalsIgnoreCase(anchorStr)) { + return Anchor.BEFORE; + } + else if (AFTER.equalsIgnoreCase(anchorStr)) { + return Anchor.AFTER; + } + else { + reportActionError(pluginId, actionName + ": anchor should be one of the following constants: \"first\", \"last\", \"before\" or \"after\""); + return null; + } + } + + private static void processMouseShortcutNode(Element element, String actionId, PluginId pluginId) { + String keystrokeString = element.getAttributeValue(KEYSTROKE_ATTR_NAME); + if (keystrokeString == null || keystrokeString.trim().isEmpty()) { + reportActionError(pluginId, "\"keystroke\" attribute must be specified for action with id=" + actionId); + return; + } + MouseShortcut shortcut; + try { + shortcut = KeymapUtil.parseMouseShortcut(keystrokeString); + } + catch (Exception ex) { + reportActionError(pluginId, "\"keystroke\" attribute has invalid value for action with id=" + actionId); + return; + } + + String keymapName = element.getAttributeValue(KEYMAP_ATTR_NAME); + if (keymapName == null || keymapName.isEmpty()) { + reportActionError(pluginId, "attribute \"keymap\" should be defined"); + return; + } + Keymap keymap = KeymapManager.getInstance().getKeymap(keymapName); + if (keymap == null) { + reportActionError(pluginId, "keymap \"" + keymapName + "\" not found"); + return; + } + + final String removeOption = element.getAttributeValue(REMOVE_SHORTCUT_ATTR_NAME); + if (Boolean.valueOf(removeOption)) { + keymap.removeShortcut(actionId, shortcut); + } else { + keymap.addShortcut(actionId, shortcut); + } + } + + private static void assertActionIsGroupOrStub(final AnAction action) { + if (!(action instanceof ActionGroup || action instanceof ActionStub)) { + LOG.error("Action : " + action + "; class: " + action.getClass()); + } + } + + private static void reportActionError(final PluginId pluginId, @NonNls @NotNull String message) { + if (pluginId == null) { + LOG.error(message); + } + else { + LOG.error(new PluginException(message, null, pluginId)); + } + } + + @NonNls + private static String getPluginInfo(@Nullable PluginId id) { + if (id != null) { + final IdeaPluginDescriptor plugin = PluginManager.getPlugin(id); + if (plugin != null) { + String name = plugin.getName(); + if (name == null) { + name = id.getIdString(); + } + return " Plugin: " + name; + } + } + return ""; + } + + private static DataContext getContextBy(Component contextComponent) { + final DataManager dataManager = DataManager.getInstance(); + return contextComponent != null ? dataManager.getDataContext(contextComponent) : dataManager.getDataContext(); + } + @Override public void initComponent() {} @@ -213,7 +461,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return new ActionToolbarImpl(place, group, horizontal, decorateButtons, myDataManager, this, (KeymapManagerEx)myKeymapManager); } - private void registerPluginActions() { final IdeaPluginDescriptor[] plugins = PluginManagerCore.getPlugins(); for (IdeaPluginDescriptor plugin : plugins) { @@ -229,12 +476,28 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @Override public AnAction getAction(@NotNull String id) { - return getActionImpl(id, false); + return getActionImpl(id, false, null); } - private AnAction getActionImpl(String id, boolean canReturnStub) { + @Override + public AnAction getAction(@NonNls @NotNull String actionId, @Nullable ProjectType projectType) { + return getActionImpl(actionId, false, projectType); + } + + private AnAction getActionImpl(String id, boolean canReturnStub, ProjectType projectType) { synchronized (myLock) { - AnAction action = (AnAction)myId2Action.get(id); + AnAction action; + Object o = myId2Action.get(id); + if (o == null) { + return null; + } + if (o instanceof AnAction) { + action = (AnAction)o; + } + else { + //noinspection unchecked + action = ((Map<ProjectType, AnAction>)o).get(projectType); + } if (!canReturnStub && action instanceof ActionStub) { action = convert((ActionStub)action); } @@ -252,61 +515,14 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat LOG.assertTrue(myId2Action.containsKey(stub.getId())); - AnAction action = (AnAction)myId2Action.remove(stub.getId()); + AnAction action = myId2Action.remove(stub.getId()); LOG.assertTrue(action != null); LOG.assertTrue(action.equals(stub)); - Object obj; - String className = stub.getClassName(); - try { - Class<?> aClass = Class.forName(className, true, stub.getLoader()); - obj = ReflectionUtil.newInstance(aClass); - } - catch (ClassNotFoundException e) { - PluginId pluginId = stub.getPluginId(); - if (pluginId != null) { - throw new PluginException("class with name \"" + className + "\" not found", e, pluginId); - } - else { - throw new IllegalStateException("class with name \"" + className + "\" not found"); - } - } - catch(UnsupportedClassVersionError e) { - PluginId pluginId = stub.getPluginId(); - if (pluginId != null) { - throw new PluginException(e, pluginId); - } - else { - throw new IllegalStateException(e); - } - } - catch (Exception e) { - PluginId pluginId = stub.getPluginId(); - if (pluginId != null) { - throw new PluginException("cannot create class \"" + className + "\"", e, pluginId); - } - else { - throw new IllegalStateException("cannot create class \"" + className + "\"", e); - } - } - - if (!(obj instanceof AnAction)) { - throw new IllegalStateException("class with name '" + className + "' must be an instance of '" + AnAction.class.getName()+"'; got "+obj); - } + AnAction anAction = convertStub(stub); - AnAction anAction = (AnAction)obj; - stub.initAction(anAction); - if (StringUtil.isNotEmpty(stub.getText())) { - anAction.getTemplatePresentation().setText(stub.getText()); - } - String iconPath = stub.getIconPath(); - if (iconPath != null) { - Class<? extends AnAction> actionClass = anAction.getClass(); - setIconFromClass(actionClass, actionClass.getClassLoader(), iconPath, anAction.getTemplatePresentation(), stub.getPluginId()); - } - - myId2Action.put(stub.getId(), obj); - myAction2Id.put(obj, stub.getId()); + addToMap(stub.getId(), anAction, stub.getPluginId(), stub.getProjectType() == null ? null : new ProjectType(stub.getProjectType())); + myAction2Id.put(anAction, stub.getId()); return anAction; } @@ -334,7 +550,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @Override public boolean isGroup(@NotNull String actionId) { - return getActionImpl(actionId, true) instanceof ActionGroup; + return getActionImpl(actionId, true, null) instanceof ActionGroup; } @Override @@ -344,7 +560,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @Override public AnAction getActionOrStub(String id) { - return getActionImpl(id, true); + return getActionImpl(id, true, null); } /** @@ -386,7 +602,8 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return null; } - ActionStub stub = new ActionStub(className, id, text, loader, pluginId, iconPath); + String projectType = element.getAttributeValue(PROJECT_TYPE); + ActionStub stub = new ActionStub(className, id, text, loader, pluginId, iconPath, projectType); Presentation presentation = stub.getTemplatePresentation(); presentation.setText(text); @@ -441,99 +658,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private static void processAbbreviationNode(Element e, String id) { - final String abbr = e.getAttributeValue(VALUE_ATTR_NAME); - if (!StringUtil.isEmpty(abbr)) { - final AbbreviationManagerImpl abbreviationManager = ((AbbreviationManagerImpl)AbbreviationManager.getInstance()); - abbreviationManager.register(abbr, id, true); - } - } - - @Nullable - private static ResourceBundle getActionsResourceBundle(ClassLoader loader, IdeaPluginDescriptor plugin) { - @NonNls final String resBundleName = plugin != null && !"com.intellij".equals(plugin.getPluginId().getIdString()) - ? plugin.getResourceBundleBaseName() : ACTIONS_BUNDLE; - ResourceBundle bundle = null; - if (resBundleName != null) { - bundle = AbstractBundle.getResourceBundle(resBundleName, loader); - } - return bundle; - } - - private static boolean isSecondary(Element element) { - return "true".equalsIgnoreCase(element.getAttributeValue(SECONDARY)); - } - - private static void setIcon(@Nullable final String iconPath, - @NotNull String className, - @NotNull ClassLoader loader, - @NotNull Presentation presentation, - final PluginId pluginId) { - if (iconPath == null) return; - - try { - final Class actionClass = Class.forName(className, true, loader); - setIconFromClass(actionClass, loader, iconPath, presentation, pluginId); - } - catch (ClassNotFoundException e) { - LOG.error(e); - reportActionError(pluginId, "class with name \"" + className + "\" not found"); - } - catch (NoClassDefFoundError e) { - LOG.error(e); - reportActionError(pluginId, "class with name \"" + className + "\" not found"); - } - } - - private static void setIconFromClass(@NotNull final Class actionClass, - @NotNull final ClassLoader classLoader, - @NotNull final String iconPath, - @NotNull Presentation presentation, - final PluginId pluginId) { - final IconLoader.LazyIcon lazyIcon = new IconLoader.LazyIcon() { - @Override - protected Icon compute() { - //try to find icon in idea class path - Icon icon = IconLoader.findIcon(iconPath, actionClass, true); - if (icon == null) { - icon = IconLoader.findIcon(iconPath, classLoader); - } - - if (icon == null) { - reportActionError(pluginId, "Icon cannot be found in '" + iconPath + "', action '" + actionClass + "'"); - } - - return icon; - } - - @Override - public String toString() { - return "LazyIcon@ActionManagerImpl (path: " + iconPath + ", action class: " + actionClass + ")"; - } - }; - - if (!Registry.is("ide.lazyIconLoading")) { - lazyIcon.load(); - } - - presentation.setIcon(lazyIcon); - } - - private static String loadDescriptionForElement(final Element element, final ResourceBundle bundle, final String id, String elementType) { - final String value = element.getAttributeValue(DESCRIPTION); - if (bundle != null) { - @NonNls final String key = elementType + "." + id + ".description"; - return CommonBundle.messageOrDefault(bundle, key, value == null ? "" : value); - } else { - return value; - } - } - - private static String loadTextForElement(final Element element, final ResourceBundle bundle, final String id, String elementType) { - final String value = element.getAttributeValue(TEXT_ATTR_NAME); - return CommonBundle.messageOrDefault(bundle, elementType + "." + id + "." + TEXT_ATTR_NAME, value == null ? "" : value); - } - private AnAction processGroupElement(Element element, final ClassLoader loader, PluginId pluginId) { final IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); ResourceBundle bundle = getActionsResourceBundle(loader, plugin); @@ -723,43 +847,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat myId2GroupId.putValue(myAction2Id.get(action), myAction2Id.get(group)); } - public static boolean checkRelativeToAction(final String relativeToActionId, - @NotNull final Anchor anchor, - @NotNull final String actionName, - @Nullable final PluginId pluginId) { - if ((Anchor.BEFORE == anchor || Anchor.AFTER == anchor) && relativeToActionId == null) { - reportActionError(pluginId, actionName + ": \"relative-to-action\" cannot be null if anchor is \"after\" or \"before\""); - return false; - } - return true; - } - - @Nullable - public static Anchor parseAnchor(final String anchorStr, - @Nullable final String actionName, - @Nullable final PluginId pluginId) { - if (anchorStr == null) { - return Anchor.LAST; - } - - if (FIRST.equalsIgnoreCase(anchorStr)) { - return Anchor.FIRST; - } - else if (LAST.equalsIgnoreCase(anchorStr)) { - return Anchor.LAST; - } - else if (BEFORE.equalsIgnoreCase(anchorStr)) { - return Anchor.BEFORE; - } - else if (AFTER.equalsIgnoreCase(anchorStr)) { - return Anchor.AFTER; - } - else { - reportActionError(pluginId, actionName + ": anchor should be one of the following constants: \"first\", \"last\", \"before\" or \"after\""); - return null; - } - } - @Nullable public AnAction getParentGroup(final String groupId, @Nullable final String actionName, @@ -768,10 +855,10 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat reportActionError(pluginId, actionName + ": attribute \"group-id\" should be defined"); return null; } - AnAction parentGroup = getActionImpl(groupId, true); + AnAction parentGroup = getActionImpl(groupId, true, null); if (parentGroup == null) { reportActionError(pluginId, actionName + ": group with id \"" + groupId + "\" isn't registered; action will be added to the \"Other\" group"); - parentGroup = getActionImpl(IdeActions.GROUP_OTHER_MENU, true); + parentGroup = getActionImpl(IdeActions.GROUP_OTHER_MENU, true, null); } if (!(parentGroup instanceof DefaultActionGroup)) { reportActionError(pluginId, actionName + ": group with id \"" + groupId + "\" should be instance of " + DefaultActionGroup.class.getName() + @@ -850,40 +937,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private static void processMouseShortcutNode(Element element, String actionId, PluginId pluginId) { - String keystrokeString = element.getAttributeValue(KEYSTROKE_ATTR_NAME); - if (keystrokeString == null || keystrokeString.trim().isEmpty()) { - reportActionError(pluginId, "\"keystroke\" attribute must be specified for action with id=" + actionId); - return; - } - MouseShortcut shortcut; - try { - shortcut = KeymapUtil.parseMouseShortcut(keystrokeString); - } - catch (Exception ex) { - reportActionError(pluginId, "\"keystroke\" attribute has invalid value for action with id=" + actionId); - return; - } - - String keymapName = element.getAttributeValue(KEYMAP_ATTR_NAME); - if (keymapName == null || keymapName.isEmpty()) { - reportActionError(pluginId, "attribute \"keymap\" should be defined"); - return; - } - Keymap keymap = KeymapManager.getInstance().getKeymap(keymapName); - if (keymap == null) { - reportActionError(pluginId, "keymap \"" + keymapName + "\" not found"); - return; - } - - final String removeOption = element.getAttributeValue(REMOVE_SHORTCUT_ATTR_NAME); - if (Boolean.valueOf(removeOption)) { - keymap.removeShortcut(actionId, shortcut); - } else { - keymap.addShortcut(actionId, shortcut); - } - } - @Nullable private AnAction processReferenceElement(Element element, PluginId pluginId) { if (!REFERENCE_ELEMENT_NAME.equals(element.getName())) { @@ -902,7 +955,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return null; } - AnAction action = getActionImpl(ref, true); + AnAction action = getActionImpl(ref, true, null); if (action == null) { if (!myNotRegisteredInternalActionIds.contains(ref)) { @@ -936,27 +989,15 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private static void assertActionIsGroupOrStub(final AnAction action) { - if (!(action instanceof ActionGroup || action instanceof ActionStub)) { - LOG.error("Action : " + action + "; class: " + action.getClass()); - } - } - @Override public void registerAction(@NotNull String actionId, @NotNull AnAction action, @Nullable PluginId pluginId) { synchronized (myLock) { - if (myId2Action.containsKey(actionId)) { - reportActionError(pluginId, "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action + - "; Registered action is " + - myId2Action.get(actionId) + getPluginInfo(pluginId)); - return; - } + if (!addToMap(actionId, action, pluginId, null)) return; if (myAction2Id.containsKey(action)) { reportActionError(pluginId, "action was already registered for another ID. ID is " + myAction2Id.get(action) + getPluginInfo(pluginId)); return; } - myId2Action.put(actionId, action); myId2Index.put(actionId, myRegisteredActionsCount++); myAction2Id.put(action, actionId); if (pluginId != null && !(action instanceof ActionGroup)){ @@ -971,28 +1012,31 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private static void reportActionError(final PluginId pluginId, @NonNls @NotNull String message) { - if (pluginId == null) { - LOG.error(message); + public boolean addToMap(String actionId, AnAction action, PluginId pluginId, ProjectType projectType) { + if (myId2Action.containsKey(actionId)) { + // make sure id+projectType is unique + AnAction o = myId2Action.get(actionId); + ChameleonAction chameleonAction; + if (o instanceof ChameleonAction) { + chameleonAction = (ChameleonAction)o; + } + else { + chameleonAction = new ChameleonAction(o, projectType); + myId2Action.put(actionId, chameleonAction); + } + AnAction old = chameleonAction.addAction(action, projectType); + if (old != null) { + reportActionError(pluginId, + "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action + + "; Registered action is " + + myId2Action.get(actionId) + getPluginInfo(pluginId)); + return false; + } } else { - LOG.error(new PluginException(message, null, pluginId)); - } - } - - @NonNls - private static String getPluginInfo(@Nullable PluginId id) { - if (id != null) { - final IdeaPluginDescriptor plugin = PluginManager.getPlugin(id); - if (plugin != null) { - String name = plugin.getName(); - if (name == null) { - name = id.getIdString(); - } - return " Plugin: " + name; - } + myId2Action.put(actionId, action); } - return ""; + return true; } @Override @@ -1067,6 +1111,12 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } + //@Override + //public AnAction replaceAction(String actionId, @NotNull AnAction newAction) { + // synchronized (myLock) { + // return replaceAction(actionId, newAction, null); + // } + //} @Override public boolean isActionPopupStackEmpty() { @@ -1078,13 +1128,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return myTransparentOnlyUpdate; } - //@Override - //public AnAction replaceAction(String actionId, @NotNull AnAction newAction) { - // synchronized (myLock) { - // return replaceAction(actionId, newAction, null); - // } - //} - private AnAction replaceAction(@NotNull String actionId, @NotNull AnAction newAction, @Nullable PluginId pluginId) { AnAction oldAction = getActionOrStub(actionId); if (oldAction != null) { @@ -1208,8 +1251,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private int myActionsPreloaded = 0; - public void preloadActions() { if (myPreloadActionsRunnable == null) { myPreloadActionsRunnable = new Runnable() { @@ -1281,6 +1322,85 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } + @Override + public ActionCallback tryToExecute(@NotNull final AnAction action, @NotNull final InputEvent inputEvent, @Nullable final Component contextComponent, @Nullable final String place, + boolean now) { + + final Application app = ApplicationManager.getApplication(); + assert app.isDispatchThread(); + + final ActionCallback result = new ActionCallback(); + final Runnable doRunnable = new Runnable() { + @Override + public void run() { + tryToExecuteNow(action, inputEvent, contextComponent, place, result); + } + }; + + if (now) { + doRunnable.run(); + } else { + //noinspection SSBasedInspection + SwingUtilities.invokeLater(doRunnable); + } + + return result; + } + + private void tryToExecuteNow(final AnAction action, final InputEvent inputEvent, final Component contextComponent, final String place, final ActionCallback result) { + final Presentation presentation = action.getTemplatePresentation().clone(); + + IdeFocusManager.findInstanceByContext(getContextBy(contextComponent)).doWhenFocusSettlesDown(new Runnable() { + @Override + public void run() { + final DataContext context = getContextBy(contextComponent); + + AnActionEvent event = new AnActionEvent( + inputEvent, context, + place != null ? place : ActionPlaces.UNKNOWN, + presentation, ActionManagerImpl.this, + inputEvent.getModifiersEx() + ); + + ActionUtil.performDumbAwareUpdate(action, event, false); + if (!event.getPresentation().isEnabled()) { + result.setRejected(); + return; + } + + ActionUtil.lastUpdateAndCheckDumb(action, event, false); + if (!event.getPresentation().isEnabled()) { + result.setRejected(); + return; + } + + Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(context); + if (component != null && !component.isShowing()) { + result.setRejected(); + return; + } + + fireBeforeActionPerformed(action, context, event); + + UIUtil.addAwtListener(new AWTEventListener() { + @Override + public void eventDispatched(AWTEvent event) { + if (event.getID() == WindowEvent.WINDOW_OPENED ||event.getID() == WindowEvent.WINDOW_ACTIVATED) { + if (!result.isProcessed()) { + final WindowEvent we = (WindowEvent)event; + IdeFocusManager.findInstanceByComponent(we.getWindow()).doWhenFocusSettlesDown(result.createSetDoneRunnable()); + } + } + } + }, AWTEvent.WINDOW_EVENT_MASK, result); + + ActionUtil.performActionDumbAware(action, event); + result.setDone(); + queueActionPerformedEvent(action, context, event); + } + }); + } + private class MyTimer extends Timer implements ActionListener { private final List<TimerListener> myTimerListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private final List<TimerListener> myTransparentTimerListeners = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -1371,88 +1491,4 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } } - - @Override - public ActionCallback tryToExecute(@NotNull final AnAction action, @NotNull final InputEvent inputEvent, @Nullable final Component contextComponent, @Nullable final String place, - boolean now) { - - final Application app = ApplicationManager.getApplication(); - assert app.isDispatchThread(); - - final ActionCallback result = new ActionCallback(); - final Runnable doRunnable = new Runnable() { - @Override - public void run() { - tryToExecuteNow(action, inputEvent, contextComponent, place, result); - } - }; - - if (now) { - doRunnable.run(); - } else { - //noinspection SSBasedInspection - SwingUtilities.invokeLater(doRunnable); - } - - return result; - } - - private void tryToExecuteNow(final AnAction action, final InputEvent inputEvent, final Component contextComponent, final String place, final ActionCallback result) { - final Presentation presentation = action.getTemplatePresentation().clone(); - - IdeFocusManager.findInstanceByContext(getContextBy(contextComponent)).doWhenFocusSettlesDown(new Runnable() { - @Override - public void run() { - final DataContext context = getContextBy(contextComponent); - - AnActionEvent event = new AnActionEvent( - inputEvent, context, - place != null ? place : ActionPlaces.UNKNOWN, - presentation, ActionManagerImpl.this, - inputEvent.getModifiersEx() - ); - - ActionUtil.performDumbAwareUpdate(action, event, false); - if (!event.getPresentation().isEnabled()) { - result.setRejected(); - return; - } - - ActionUtil.lastUpdateAndCheckDumb(action, event, false); - if (!event.getPresentation().isEnabled()) { - result.setRejected(); - return; - } - - Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(context); - if (component != null && !component.isShowing()) { - result.setRejected(); - return; - } - - fireBeforeActionPerformed(action, context, event); - - UIUtil.addAwtListener(new AWTEventListener() { - @Override - public void eventDispatched(AWTEvent event) { - if (event.getID() == WindowEvent.WINDOW_OPENED ||event.getID() == WindowEvent.WINDOW_ACTIVATED) { - if (!result.isProcessed()) { - final WindowEvent we = (WindowEvent)event; - IdeFocusManager.findInstanceByComponent(we.getWindow()).doWhenFocusSettlesDown(result.createSetDoneRunnable()); - } - } - } - }, AWTEvent.WINDOW_EVENT_MASK, result); - - ActionUtil.performActionDumbAware(action, event); - result.setDone(); - queueActionPerformedEvent(action, context, event); - } - }); - } - - private static DataContext getContextBy(Component contextComponent) { - final DataManager dataManager = DataManager.getInstance(); - return contextComponent != null ? dataManager.getDataContext(contextComponent) : dataManager.getDataContext(); - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java new file mode 100644 index 000000000000..4b46dc191c0c --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2014 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.actionSystem.impl; + +import com.intellij.openapi.actionSystem.ActionStub; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectType; +import com.intellij.openapi.project.ProjectTypeService; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Dmitry Avdeev + */ +public class ChameleonAction extends AnAction { + + private final Map<ProjectType, AnAction> myActions = new HashMap<ProjectType, AnAction>(); + + public ChameleonAction(@NotNull AnAction first, ProjectType projectType) { + addAction(first, projectType); + } + + public AnAction addAction(AnAction action, ProjectType projectType) { + if (action instanceof ActionStub) { + String type = ((ActionStub)action).getProjectType(); + action = ActionManagerImpl.convertStub((ActionStub)action); + projectType = type == null ? null : new ProjectType(type); + } + return myActions.put(projectType, action); + } + + @Override + public void actionPerformed(AnActionEvent e) { + getAction(e).actionPerformed(e); + } + + @Override + public void update(AnActionEvent e) { + super.update(e); + } + + private AnAction getAction(AnActionEvent e) { + Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); + ProjectType projectType = ProjectTypeService.getProjectType(project); + AnAction action = myActions.get(projectType); + if (action == null) action = myActions.get(null); + if (action == null) action = myActions.values().iterator().next(); + return action; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/config/ActionBean.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/config/ActionBean.java index 01aa0ff4c49e..4de847213103 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/config/ActionBean.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/config/ActionBean.java @@ -40,7 +40,6 @@ public class ActionBean { @Attribute(ActionManagerImpl.ICON_ATTR_NAME) public String icon; - @Attribute(ActionManagerImpl.POPUP_ATTR_NAME) public String isPopup; @@ -58,4 +57,7 @@ public class ActionBean { @Attribute(ActionManagerImpl.OVERRIDES_ATTR_NAME) public boolean overrides; + + @Attribute(ActionManagerImpl.PROJECT_TYPE) + public String projectType; } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 46fae517170d..e3a36a2e59ab 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -336,25 +336,26 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App } private boolean disposeSelf(final boolean checkCanCloseProject) { - final CommandProcessor commandProcessor = CommandProcessor.getInstance(); - final boolean[] canClose = {true}; - for (final Project project : ProjectManagerEx.getInstanceEx().getOpenProjects()) { - try { - commandProcessor.executeCommand(project, new Runnable() { - @Override - public void run() { - final ProjectManagerImpl manager = (ProjectManagerImpl)ProjectManagerEx.getInstanceEx(); - if (!manager.closeProject(project, true, true, checkCanCloseProject)) { - canClose[0] = false; + final ProjectManagerImpl manager = (ProjectManagerImpl)ProjectManagerEx.getInstanceEx(); + if (manager != null) { + final boolean[] canClose = {true}; + for (final Project project : manager.getOpenProjects()) { + try { + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + @Override + public void run() { + if (!manager.closeProject(project, true, true, checkCanCloseProject)) { + canClose[0] = false; + } } - } - }, ApplicationBundle.message("command.exit"), null); - } - catch (Throwable e) { - LOG.error(e); - } - if (!canClose[0]) { - return false; + }, ApplicationBundle.message("command.exit"), null); + } + catch (Throwable e) { + LOG.error(e); + } + if (!canClose[0]) { + return false; + } } } runWriteAction(new Runnable() { @@ -529,9 +530,13 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App } @Override - public void load(String path) throws IOException, InvalidDataException { - getStateStore().setOptionsPath(path); - getStateStore().setConfigPath(PathManager.getConfigPath()); + public void load(@Nullable String optionsPath) throws IOException { + load(PathManager.getConfigPath(), optionsPath); + } + + public void load(@NotNull String configPath, @Nullable String optionsPath) throws IOException { + getStateStore().setOptionsPath(optionsPath); + getStateStore().setConfigPath(configPath); myIsFiringLoadingEvent = true; try { diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java index 41942960ab04..a00533d3244b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.NamedJDOMExternalizable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Collection; @@ -36,13 +37,14 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto private static final String XML_EXTENSION = ".xml"; private static final String DEFAULT_STORAGE_SPEC = StoragePathMacros.APP_CONFIG + "/" + PathManager.DEFAULT_OPTIONS_FILE_NAME + XML_EXTENSION; private static final String OPTIONS_MACRO = "OPTIONS"; - private static final String CONFIG_MACRO = "ROOT_CONFIG"; private static final String ROOT_ELEMENT_NAME = "application"; private final ApplicationImpl myApplication; private final StateStorageManager myStateStorageManager; private final DefaultsStateStorage myDefaultsStateStorage; + private String myConfigPath; + // created from PicoContainer @SuppressWarnings({"UnusedDeclaration"}) public ApplicationStoreImpl(final ApplicationImpl application, PathMacroManager pathMacroManager) { @@ -53,23 +55,24 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto return new FileBasedStorage.FileStorageData(ROOT_ELEMENT_NAME); } + @Nullable @Override protected String getOldStorageSpec(Object component, final String componentName, final StateStorageOperation operation) { - final String fileName; - if (component instanceof NamedJDOMExternalizable) { - fileName = StoragePathMacros.APP_CONFIG + "/" + ((NamedJDOMExternalizable)component).getExternalFileName() + XML_EXTENSION; + return StoragePathMacros.APP_CONFIG + "/" + ((NamedJDOMExternalizable)component).getExternalFileName() + XML_EXTENSION; } else { - fileName = DEFAULT_STORAGE_SPEC; + return DEFAULT_STORAGE_SPEC; } - - return fileName; } @Override protected String getVersionsFilePath() { - return PathManager.getConfigPath() + "/options/appComponentVersions.xml"; + String configPath = myConfigPath; + if (configPath == null) { + configPath = PathManager.getConfigPath(); + } + return configPath + "/options/appComponentVersions.xml"; } @Override @@ -97,7 +100,8 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto @Override public void setConfigPath(@NotNull final String configPath) { - myStateStorageManager.addMacro(CONFIG_MACRO, configPath); + myStateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.ROOT_CONFIG), configPath); + myConfigPath = configPath; } @Override @@ -150,6 +154,7 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto return myStateStorageManager; } + @Nullable @Override protected StateStorage getDefaultsStorage() { return myDefaultsStateStorage; diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java index 01cc0fb987b9..25dd8a05cab3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java @@ -16,7 +16,6 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.openapi.components.*; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.impl.ProjectManagerImpl; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -28,8 +27,6 @@ import java.util.ArrayList; import java.util.Set; abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.BaseFileConfigurableStoreImpl"); - @NonNls protected static final String VERSION_OPTION = "version"; @NonNls public static final String ATTRIBUTE_NAME = "name"; private final ComponentManager myComponentManager; @@ -119,6 +116,7 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { return (BaseStorageData) getMainStorage().getStorageData(false); } + @Nullable @Override protected StateStorage getDefaultsStorage() { return myDefaultsStateStorage; diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java index 575717f455f7..fc2017b57a4a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java @@ -41,8 +41,7 @@ import java.util.*; @SuppressWarnings({"deprecation"}) public abstract class ComponentStoreImpl implements IComponentStore { - - private static final Logger LOG = Logger.getInstance("#com.intellij.components.ComponentStoreImpl"); + private static final Logger LOG = Logger.getInstance(ComponentStoreImpl.class); private final Map<String, Object> myComponents = Collections.synchronizedMap(new THashMap<String, Object>()); private final List<SettingsSavingComponent> mySettingsSavingComponents = Collections.synchronizedList(new ArrayList<SettingsSavingComponent>()); @Nullable private SaveSessionImpl mySession; @@ -53,9 +52,8 @@ public abstract class ComponentStoreImpl implements IComponentStore { return getStateStorageManager().getStateStorage(storageSpec); } - protected StateStorage getDefaultsStorage() { - throw new UnsupportedOperationException("Method getDefaultsStorage is not supported in " + getClass()); - } + @Nullable + protected abstract StateStorage getDefaultsStorage(); @Override public void initComponent(@NotNull final Object component, final boolean service) { @@ -143,12 +141,10 @@ public abstract class ComponentStoreImpl implements IComponentStore { private <T> void commitPersistentComponent(@NotNull final PersistentStateComponent<T> persistentStateComponent, @NotNull StateStorageManager.ExternalizationSession session) { - Storage[] storageSpecs = getComponentStorageSpecs(persistentStateComponent, StateStorageOperation.WRITE); - T state = persistentStateComponent.getState(); if (state != null) { - session - .setState(storageSpecs, persistentStateComponent, getComponentName(persistentStateComponent), state); + Storage[] storageSpecs = getComponentStorageSpecs(persistentStateComponent, StateStorageOperation.WRITE); + session.setState(storageSpecs, persistentStateComponent, getComponentName(persistentStateComponent), state); } } @@ -276,13 +272,12 @@ public abstract class ComponentStoreImpl implements IComponentStore { @NotNull private static <T> Class<T> getComponentStateClass(@NotNull final PersistentStateComponent<T> persistentStateComponent) { final Class persistentStateComponentClass = PersistentStateComponent.class; + Class componentClass = persistentStateComponent.getClass(); nextSuperClass: while (true) { - final Class[] interfaces = componentClass.getInterfaces(); - - for (Class anInterface : interfaces) { + for (Class anInterface : componentClass.getInterfaces()) { if (anInterface.equals(persistentStateComponentClass)) { break nextSuperClass; } @@ -292,7 +287,7 @@ public abstract class ComponentStoreImpl implements IComponentStore { } final Type type = ReflectionUtil.resolveVariable(persistentStateComponentClass.getTypeParameters()[0], componentClass); - + assert type != null; //noinspection unchecked return (Class<T>)ReflectionUtil.getRawType(type); } @@ -319,14 +314,12 @@ public abstract class ComponentStoreImpl implements IComponentStore { protected <T> Storage[] getComponentStorageSpecs(@NotNull final PersistentStateComponent<T> persistentStateComponent, final StateStorageOperation operation) throws StateStorageException { final State stateSpec = getStateSpec(persistentStateComponent); - final Storage[] storages = stateSpec.storages(); - - if (storages.length == 1) return storages; - + if (storages.length == 1) { + return storages; + } assert storages.length > 0; - final Class<StorageAnnotationsDefaultValues.NullStateStorageChooser> defaultClass = StorageAnnotationsDefaultValues.NullStateStorageChooser.class; @@ -340,14 +333,11 @@ public abstract class ComponentStoreImpl implements IComponentStore { } else { try { - //noinspection unchecked - final StateStorageChooser<PersistentStateComponent<T>> storageChooser = storageChooserClass.newInstance(); + @SuppressWarnings("unchecked") + StateStorageChooser<PersistentStateComponent<T>> storageChooser = ReflectionUtil.newInstance(storageChooserClass); return storageChooser.selectStorages(storages, persistentStateComponent, operation); } - catch (InstantiationException e) { - throw new StateStorageException(e); - } - catch (IllegalAccessException e) { + catch (RuntimeException e) { throw new StateStorageException(e); } } @@ -404,7 +394,7 @@ public abstract class ComponentStoreImpl implements IComponentStore { } catch (StateStorageException e) { LOG.info(e); - throw new IOException(e.getMessage()); + throw new IOException(e.getMessage(), e); } return this; diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java index 48247b2de866..6f0c3b8122cf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java @@ -159,8 +159,8 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { } @Override - public String expandMacros(final String file) { - throw new UnsupportedOperationException("Method expandMacroses not implemented in " + getClass()); + public String expandMacros(@NotNull String file) { + throw new UnsupportedOperationException("Method expandMacros not implemented in " + getClass()); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java index 574823767d9b..2120030db2c1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java @@ -55,7 +55,7 @@ final class OldStreamProviderAdapter extends StreamProvider implements CurrentUs } @Override - public void deleteFile(@NotNull String fileSpec, @NotNull RoamingType roamingType) { + public void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType) { if (myRoamingType == roamingType) { myProvider.deleteFile(fileSpec, roamingType); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java index f6a4e11e5aa5..0eed50a65131 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java @@ -20,6 +20,7 @@ import com.intellij.openapi.components.*; import com.intellij.openapi.project.impl.ProjectImpl; import org.jdom.Element; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; import java.util.Map; @@ -47,6 +48,7 @@ class ProjectStateStorageManager extends StateStorageManagerImpl { return new ProjectStoreImpl.IprStorageData(ROOT_TAG_NAME, myProject); } + @Nullable @Override protected String getOldStorageSpec(Object component, final String componentName, final StateStorageOperation operation) throws StateStorageException { diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java index 0e2828e988a3..68d882d2b204 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java @@ -58,7 +58,7 @@ public interface StateStorageManager { StateStorage getOldStorage(Object component, String componentName, StateStorageOperation operation) throws StateStorageException; @Nullable - String expandMacros(String file); + String expandMacros(@NotNull String file); @Deprecated void registerStreamProvider(@SuppressWarnings("deprecation") StreamProvider streamProvider, final RoamingType type); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java index 1b7da8fc2144..431c9a97820c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java @@ -331,7 +331,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Override @Nullable - public synchronized String expandMacros(final String file) { + public synchronized String expandMacros(@NotNull String file) { final Matcher matcher = MACRO_PATTERN.matcher(file); while (matcher.find()) { String m = matcher.group(1); @@ -427,6 +427,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di return getFileStateStorage(getOldStorageSpec(component, componentName, operation)); } + @Nullable protected abstract String getOldStorageSpec(Object component, final String componentName, final StateStorageOperation operation) throws StateStorageException; @@ -615,11 +616,11 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } @Override - public void deleteFile(@NotNull String fileSpec, @NotNull RoamingType roamingType) { + public void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType) { for (StreamProvider streamProvider : myStreamProviders) { try { if (streamProvider.isEnabled() && streamProvider.isApplicable(fileSpec, roamingType)) { - streamProvider.deleteFile(fileSpec, roamingType); + streamProvider.delete(fileSpec, roamingType); } } catch (Exception e) { diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java index 73aea37d6444..bd732705446a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java @@ -113,19 +113,25 @@ public class StorageUtil { @Nullable static VirtualFile save(@NotNull IFile file, Parent element, Object requestor) throws StateStorageException { try { - VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); - Couple<String> pair = loadFile(vFile); - String text = JDOMUtil.writeParent(element, pair.second); - + String lineSeparator; + String oldText; if (file.exists()) { - if (text.equals(pair.first)) { - return null; - } + VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); + Couple<String> pair = loadFile(vFile); + lineSeparator = pair.second; + oldText = pair.first; } else { + oldText = null; + lineSeparator = SystemProperties.getLineSeparator(); file.createParentDirs(); } + String text = JDOMUtil.writeParent(element, lineSeparator); + if (text.equals(oldText)) { + return null; + } + // mark this action as modifying the file which daemon analyzer should ignore AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); try { @@ -245,6 +251,7 @@ public class StorageUtil { } } + @NotNull public static BufferExposingByteArrayOutputStream documentToBytes(@NotNull Document document, boolean useSystemLineSeparator) throws IOException { BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(512); OutputStreamWriter writer = new OutputStreamWriter(out, CharsetToolkit.UTF8_CHARSET); @@ -271,9 +278,9 @@ public class StorageUtil { } } - public static void deleteContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull RoamingType type) { + public static void delete(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull RoamingType type) { if (provider.isApplicable(fileSpec, type)) { - provider.deleteFile(fileSpec, type); + provider.delete(fileSpec, type); } } 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 index cf77ee377cf1..ec57d8c63601 100644 --- 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 @@ -47,5 +47,8 @@ public abstract class StreamProvider { return Collections.emptyList(); } - public abstract void deleteFile(@NotNull String fileSpec, @NotNull RoamingType roamingType); + /** + * 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/diff/ApplicationStarterBase.java b/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java new file mode 100644 index 000000000000..b508686ade00 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java @@ -0,0 +1,187 @@ +/* + * Copyright 2000-2014 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.diff; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ApplicationStarterEx; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.io.StreamUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Arrays; + +/** + * @author Konstantin Bulenkov + */ +@SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace"}) +public abstract class ApplicationStarterBase extends ApplicationStarterEx { + private final String myCommandName; + private final int[] myArgsCount; + + protected ApplicationStarterBase(String commandName, int... possibleArgumentsCount) { + myCommandName = commandName; + myArgsCount = possibleArgumentsCount; + } + + @Override + public String getCommandName() { + return myCommandName; + } + + @Override + public boolean isHeadless() { + return false; + } + + @Override + public void processExternalCommandLine(String[] args) { + if (!checkArguments(args)) { + Messages.showMessageDialog(getUsageMessage(), StringUtil.toTitleCase(getCommandName()), Messages.getInformationIcon()); + return; + } + try { + processCommand(args); + } + catch (Exception e) { + Messages.showMessageDialog(String.format("Error showing %s: %s", getCommandName(), e.getMessage()), + StringUtil.toTitleCase(getCommandName()), + Messages.getErrorIcon()); + } + finally { + saveAll(); + } + } + + private static void saveAll() { + FileDocumentManager.getInstance().saveAllDocuments(); + ApplicationManager.getApplication().saveSettings(); + } + + private boolean checkArguments(String[] args) { + return Arrays.binarySearch(myArgsCount, args.length - 1) != -1 && getCommandName().equals(args[0]); + } + + public abstract String getUsageMessage(); + + protected abstract void processCommand(String[] args) throws Exception; + + @Override + public void premain(String[] args) { + if (!checkArguments(args)) { + System.err.println(getUsageMessage()); + System.exit(1); + } + } + + @Override + public void main(String[] args) { + try { + processCommand(args); + } + catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + catch (Throwable t) { + t.printStackTrace(); + System.exit(2); + } + finally { + saveAll(); + } + + System.exit(0); + } + + public static VirtualFile findOrCreateFile(String path) throws IOException { + final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(path)); + if (file == null) { + boolean result = new File(path).createNewFile(); + if (result) { + return findFile(path); + } + else { + throw new FileNotFoundException("Can't create file " + path); + } + } + return file; + } + + /** + * Get direct from file because IDEA cache files(see #IDEA-81067) + */ + public static String getText(VirtualFile file) throws IOException { + FileInputStream inputStream = new FileInputStream(file.getPath()); + try { + return StreamUtil.readText(inputStream); + } + finally { + inputStream.close(); + } + } + + public static boolean haveDirs(VirtualFile... files) { + for (VirtualFile file : files) { + if (file.isDirectory()) { + return true; + } + } + return false; + } + + public static boolean areJars(VirtualFile file1, VirtualFile file2) { + return JarFileSystem.PROTOCOL.equalsIgnoreCase(file1.getExtension()) && JarFileSystem.PROTOCOL.equalsIgnoreCase(file2.getExtension()); + } + + public static boolean areDirs(VirtualFile file1, VirtualFile file2) { + return file1.isDirectory() && file2.isDirectory(); + } + + public static class OperationFailedException extends IOException { + public OperationFailedException(@NotNull String message) { + super(message); + } + } + + @NotNull + public static VirtualFile findFile(final String path) throws OperationFailedException { + File ioFile = new File(path); + if (!ioFile.exists()) { + final String dir = PathManager.getOriginalWorkingDir(); + ioFile = new File(dir + File.separator + path); + } + final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile); + if (file == null) { + throw new OperationFailedException("Can't find file " + path); + } + return file; + } + + @Override + public boolean canProcessExternalCommandLine() { + return true; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java b/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java new file mode 100644 index 000000000000..742a405f350e --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2014 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.diff; + +import com.intellij.ide.diff.DiffElement; +import com.intellij.ide.diff.DirDiffSettings; +import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.fileTypes.UnknownFileType; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.vfs.VirtualFile; + +/** + * @author max + * @author Konstantin Bulenkov + */ +@SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace"}) +public class DiffApplication extends ApplicationStarterBase { + public DiffApplication() { + super("diff", 2); + } + + public String getUsageMessage() { + final String scriptName = ApplicationNamesInfo.getInstance().getScriptName(); + return DiffBundle.message("diff.application.usage.parameters.and.description", scriptName); + } + + public void processCommand(String[] args) throws OperationFailedException { + final String path1 = args[1]; + final String path2 = args[2]; + final VirtualFile file1 = findFile(path1); + final VirtualFile file2 = findFile(path2); + final boolean areDirs = areDirs(file1, file2); + final boolean areJars = areJars(file1, file2); + if (areDirs || areJars) { + final DirDiffManager diffManager = DirDiffManager.getInstance(ProjectManager.getInstance().getDefaultProject()); + final DiffElement d1 = diffManager.createDiffElement(file1); + final DiffElement d2 = diffManager.createDiffElement(file2); + if (d1 == null) { + throw new OperationFailedException(DiffBundle.message("cannot.create.diff.error", path1)); + } + if (d2 == null) { + throw new OperationFailedException(DiffBundle.message("cannot.create.diff.error", path1)); + } + else if (!diffManager.canShow(d1, d2)) { + throw new OperationFailedException(DiffBundle.message("cannot.compare.error", path1, path2)); + } + + final DirDiffSettings settings = new DirDiffSettings(); + settings.showInFrame = false; + diffManager.showDiff(d1, d2, settings, null); + } + else { + file1.refresh(false, false); + file2.refresh(false, false); + + if (file1.getFileType() == UnknownFileType.INSTANCE) { + throw new OperationFailedException(DiffBundle.message("unknown.file.type.error", path1)); + } + else if (file2.getFileType() == UnknownFileType.INSTANCE) { + throw new OperationFailedException(DiffBundle.message("unknown.file.type.error", path2)); + } + + SimpleDiffRequest request = SimpleDiffRequest.compareFiles(file1, file2, ProjectManager.getInstance().getDefaultProject()); + request.addHint(DiffTool.HINT_SHOW_MODAL_DIALOG); + DiffManager.getInstance().getIdeaDiffTool().show(request); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java b/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java new file mode 100644 index 000000000000..0c088929ea49 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2014 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.diff; + +import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.vfs.VirtualFile; + +/** + * @author Konstantin Bulenkov + */ +public class MergeApplication extends ApplicationStarterBase { + public MergeApplication() { + super("merge", 3 ,4); + } + + @Override + public String getUsageMessage() { + final String script = ApplicationNamesInfo.getInstance().getScriptName(); + return String.format("Usage:\n\t%s merge <file1> <file2> <original>\n\t%s merge <file1> <file2> <original> <output>", script, script); + } + + @Override + protected void processCommand(String[] args) throws Exception { + final VirtualFile left = findFile(args[1]); + final VirtualFile right = findFile(args[2]); + final VirtualFile middle = findFile(args[3]); + final VirtualFile result = findOrCreateFile(args.length == 4 ? args[3] : args[4]); + + MergeRequest request = DiffRequestFactory.getInstance() + .createMergeRequest(getText(left), getText(right), getText(middle), result, + ProjectManager.getInstance().getDefaultProject(), + ActionButtonPresentation.APPLY, + ActionButtonPresentation.CANCEL_WITH_PROMPT); + request.addHint(DiffTool.HINT_SHOW_MODAL_DIALOG); + request.setWindowTitle("Merge"); + request.setVersionTitles(new String[]{left.getPresentableUrl(), result.getPresentableUrl(), middle.getPresentableUrl()}); + DiffManager.getInstance().getDiffTool().show(request); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/ex/DiffPanelOptions.java b/platform/platform-impl/src/com/intellij/openapi/diff/ex/DiffPanelOptions.java index ec7a316357e2..3ad1a58a6732 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/ex/DiffPanelOptions.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/ex/DiffPanelOptions.java @@ -93,7 +93,7 @@ public class DiffPanelOptions { while (window instanceof DialogWrapperDialog) { DialogWrapperDialog dlg = (DialogWrapperDialog)window; window = window.getParent(); - dlg.getDialogWrapper().close(DialogWrapper.CLOSE_EXIT_CODE); + dlg.getDialogWrapper().doCancelAction(); } return true; } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffUtil.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffUtil.java index 2a5a01b43ca5..a67a6ad5af71 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.diff.impl; +import com.intellij.codeStyle.CodeStyleFacade; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.diff.DiffContent; import com.intellij.openapi.diff.DiffContentUtil; @@ -84,11 +85,22 @@ public class DiffUtil { } public static EditorEx createEditor(Document document, Project project, boolean isViewer) { + return createEditor(document, project, isViewer, null); + } + + public static EditorEx createEditor(Document document, Project project, boolean isViewer, @Nullable FileType fileType) { EditorFactory factory = EditorFactory.getInstance(); EditorEx editor = (EditorEx)(isViewer ? factory.createViewer(document, project) : factory.createEditor(document, project)); editor.putUserData(DiffManagerImpl.EDITOR_IS_DIFF_KEY, Boolean.TRUE); editor.setSoftWrapAppliancePlace(SoftWrapAppliancePlaces.VCS_DIFF); editor.getGutterComponentEx().revalidateMarkup(); + + if (fileType != null && project != null && !project.isDisposed()) { + CodeStyleFacade codeStyleFacade = CodeStyleFacade.getInstance(project); + editor.getSettings().setTabSize(codeStyleFacade.getTabSize(fileType)); + editor.getSettings().setUseTabCharacter(codeStyleFacade.useTabCharacter(fileType)); + } + return editor; } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/BaseExternalTool.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/BaseExternalTool.java index 9ae17ec50df0..d4fbf23aa6c4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/BaseExternalTool.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/BaseExternalTool.java @@ -46,7 +46,7 @@ abstract class BaseExternalTool implements DiffTool { myToolProperty = toolProperty; } - public final boolean canShow(DiffRequest request) { + public final boolean canShow(@NotNull DiffRequest request) { if (!isEnabled() || StringUtil.isEmpty(getToolPath())) return false; return isAvailable(request); } @@ -56,10 +56,10 @@ abstract class BaseExternalTool implements DiffTool { return null; } - public abstract boolean isAvailable(DiffRequest request); + public abstract boolean isAvailable(@NotNull DiffRequest request); @Nullable - protected ContentExternalizer externalize(final DiffRequest request, final int index) { + protected ContentExternalizer externalize(@NotNull DiffRequest request, final int index) { final VirtualFile file = getLocalFile(request.getContents()[index].getFile()); if (LocalFileExternalizer.canExternalizeAsFile(file)) { @@ -81,7 +81,8 @@ abstract class BaseExternalTool implements DiffTool { return myEnableProperty.value(getProperties()); } - protected List<String> getParameters(DiffRequest request) throws Exception { + @NotNull + protected List<String> getParameters(@NotNull DiffRequest request) throws Exception { final String p1 = convertToPath(request, 0); final String p2 = convertToPath(request, 1); final List<String> params = new ArrayList<String>(); @@ -91,12 +92,8 @@ abstract class BaseExternalTool implements DiffTool { } public void show(DiffRequest request) { - for (DiffContent diffContent : request.getContents()) { - Document document = diffContent.getDocument(); - if (document != null) { - FileDocumentManager.getInstance().saveDocument(document); - } - } + saveContents(request); + GeneralCommandLine commandLine = new GeneralCommandLine(); commandLine.setExePath(getToolPath()); try { @@ -109,6 +106,15 @@ abstract class BaseExternalTool implements DiffTool { } } + protected void saveContents(DiffRequest request) { + for (DiffContent diffContent : request.getContents()) { + Document document = diffContent.getDocument(); + if (document != null) { + FileDocumentManager.getInstance().saveDocument(document); + } + } + } + @Nullable protected String convertToPath(DiffRequest request, int index) throws Exception { final ContentExternalizer externalize = externalize(request, index); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtCompareFiles.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtCompareFiles.java index 0594ab09c0c5..6f661acf9463 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtCompareFiles.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtCompareFiles.java @@ -19,6 +19,7 @@ import com.intellij.openapi.diff.DiffContent; import com.intellij.openapi.diff.DiffRequest; import com.intellij.openapi.diff.impl.DiffUtil; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; /** * @author Konstantin Bulenkov @@ -30,7 +31,7 @@ class ExtCompareFiles extends BaseExternalTool { super(DiffManagerImpl.ENABLE_FILES, DiffManagerImpl.FILES_TOOL); } - public boolean isAvailable(DiffRequest request) { + public boolean isAvailable(@NotNull DiffRequest request) { final DiffContent[] contents = request.getContents(); for (DiffContent content : contents) { final VirtualFile file = getLocalFile(content.getFile()); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtCompareFolders.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtCompareFolders.java index 14c0a9607293..6260176d3e26 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtCompareFolders.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtCompareFolders.java @@ -18,6 +18,7 @@ package com.intellij.openapi.diff.impl.external; import com.intellij.openapi.diff.DiffContent; import com.intellij.openapi.diff.DiffRequest; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -31,7 +32,7 @@ class ExtCompareFolders extends BaseExternalTool { } @Override - public boolean isAvailable(DiffRequest request) { + public boolean isAvailable(@NotNull DiffRequest request) { final DiffContent[] contents = request.getContents(); if (contents.length != 2) return false; if (externalize(request, 0) == null) return false; @@ -40,7 +41,7 @@ class ExtCompareFolders extends BaseExternalTool { } @Nullable - protected ContentExternalizer externalize(DiffRequest request, int index) { + protected ContentExternalizer externalize(@NotNull DiffRequest request, int index) { final VirtualFile file = request.getContents()[index].getFile(); if (!isLocalDirectory(file)) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtMergeFiles.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtMergeFiles.java index 82313a5c9c35..cfee691a1f1f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtMergeFiles.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/ExtMergeFiles.java @@ -15,10 +15,21 @@ */ package com.intellij.openapi.diff.impl.external; +import com.intellij.execution.ExecutionException; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.util.ExecutionErrorDialog; +import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.diff.DiffContent; import com.intellij.openapi.diff.DiffRequest; import com.intellij.openapi.diff.impl.mergeTool.MergeRequestImpl; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.TimeoutUtil; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -34,7 +45,8 @@ public class ExtMergeFiles extends BaseExternalTool { } @Override - public boolean isAvailable(DiffRequest request) { + public boolean isAvailable(@NotNull DiffRequest request) { + if (!(request instanceof MergeRequestImpl)) return false; DiffContent[] contents = request.getContents(); if (contents.length != 3) return false; if (externalize(request, 0) == null) return false; @@ -45,7 +57,8 @@ public class ExtMergeFiles extends BaseExternalTool { } @Override - protected List<String> getParameters(DiffRequest request) throws Exception { + @NotNull + protected List<String> getParameters(@NotNull DiffRequest request) throws Exception { final List<String> params = new ArrayList<String>(); String result = ((MergeRequestImpl)request).getResultContent().getFile().getPath(); String left = externalize(request, 0).getContentFile().getPath(); @@ -60,4 +73,41 @@ public class ExtMergeFiles extends BaseExternalTool { } return params; } + + @Override + public void show(@NotNull DiffRequest request) { + saveContents(request); + + int result = DialogWrapper.CANCEL_EXIT_CODE; + + GeneralCommandLine commandLine = new GeneralCommandLine(); + commandLine.setExePath(getToolPath()); + try { + commandLine.addParameters(getParameters(request)); + commandLine.createProcess(); + + ProgressManager.getInstance().run(new Task.Modal(request.getProject(), "Launching external tool", false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + indicator.setIndeterminate(true); + TimeoutUtil.sleep(1000); + } + }); + + if (Messages.YES == Messages.showYesNoDialog(request.getProject(), + "Press \"Mark as Resolved\" when you finish resolving conflicts in the external tool", + "Merge In External Tool", "Mark as Resolved", "Revert", null)) { + result = DialogWrapper.OK_EXIT_CODE; + } + ((MergeRequestImpl)request).getResultContent().getFile().refresh(false, false); + // We can actually check exit code of external tool, but some of them could work with tabs -> do not close at all + } + catch (Exception e) { + ExecutionErrorDialog + .show(new ExecutionException(e.getMessage()), DiffBundle.message("cant.launch.diff.tool.error.message"), request.getProject()); + } + finally { + ((MergeRequestImpl)request).setResult(result); + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/highlighting/EditorPlaceHolder.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/highlighting/EditorPlaceHolder.java index 0632b56d0f0e..dabaf5f1f030 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/highlighting/EditorPlaceHolder.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/highlighting/EditorPlaceHolder.java @@ -93,7 +93,7 @@ class EditorPlaceHolder extends DiffMarkup implements DiffVersionComponent { } else { document = new DocumentImpl("Can not show", true); final EditorFactory editorFactory = EditorFactory.getInstance(); - myEditor = DiffUtil.createEditor(document, getProject(), true); + myEditor = DiffUtil.createEditor(document, getProject(), true, content.getContentType()); addDisposable(new Disposable() { public void dispose() { editorFactory.releaseEditor(myEditor); @@ -105,7 +105,7 @@ class EditorPlaceHolder extends DiffMarkup implements DiffVersionComponent { } else { final EditorFactory editorFactory = EditorFactory.getInstance(); - myEditor = DiffUtil.createEditor(document, getProject(), false); + myEditor = DiffUtil.createEditor(document, getProject(), false, content.getContentType()); addDisposable(new Disposable() { public void dispose() { editorFactory.releaseEditor(myEditor); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/SyncScrollSupport.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/SyncScrollSupport.java index f6cd904c9503..3bc3e90b81b3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/SyncScrollSupport.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/SyncScrollSupport.java @@ -136,16 +136,13 @@ public class SyncScrollSupport implements Disposable { Editor slaveEditor = sidesContainer.getEditor(masterSide.otherSide()); if (slaveEditor == null) return; - ScrollingModel scrollingModel = slaveEditor.getScrollingModel(); - scrollingModel.disableAnimation(); - scrollingModel.scrollHorizontally(newScrollOffset); - scrollingModel.enableAnimation(); + doScrollHorizontally(slaveEditor.getScrollingModel(), newScrollOffset); } private static void syncVerticalScroll(@NotNull ScrollingContext context, @NotNull Rectangle newRectangle, @NotNull Rectangle oldRectangle) { - if (newRectangle.y == oldRectangle.y && newRectangle.height == oldRectangle.height) return; + if (newRectangle.y == oldRectangle.y) return; EditingSides sidesContainer = context.getSidesContainer(); FragmentSide masterSide = context.getMasterSide(); FragmentSide masterDiffSide = context.getMasterDiffSide(); @@ -155,42 +152,55 @@ public class SyncScrollSupport implements Disposable { if (master == null || slave == null) return; + int masterVerticalScrollOffset = master.getScrollingModel().getVerticalScrollOffset(); + int slaveVerticalScrollOffset = slave.getScrollingModel().getVerticalScrollOffset(); + Rectangle viewRect = master.getScrollingModel().getVisibleArea(); int middleY = viewRect.height / 3; - int masterVerticalScrollOffset = getScrollOffset(master); - int slaveVerticalScrollOffset = getScrollOffset(slave); + if (master.getDocument().getTextLength() == 0) return; LogicalPosition masterPos = master.xyToLogicalPosition(new Point(viewRect.x, masterVerticalScrollOffset + middleY)); int masterCenterLine = masterPos.line; - if (masterCenterLine > master.getDocument().getLineCount()) { - masterCenterLine = master.getDocument().getLineCount(); + int scrollToLine = sidesContainer.getLineBlocks().transform(masterDiffSide, masterCenterLine); + + int offset; + if (scrollToLine < 0) { + offset = slaveVerticalScrollOffset + newRectangle.y - oldRectangle.y; } - int scrollToLine = sidesContainer.getLineBlocks().transform(masterDiffSide, masterCenterLine) + 1; - int actualLine = scrollToLine - 1; + else { + int correction = (masterVerticalScrollOffset + middleY) % master.getLineHeight(); + Point point = slave.logicalPositionToXY(new LogicalPosition(scrollToLine, masterPos.column)); + offset = point.y - middleY + correction; + } + + int deltaHeaderOffset = getHeaderOffset(slave) - getHeaderOffset(master); + doScrollVertically(slave.getScrollingModel(), offset + deltaHeaderOffset); + } + private static int getHeaderOffset(@NotNull final Editor editor) { + final JComponent header = editor.getHeaderComponent(); + return header == null ? 0 : header.getHeight(); + } - slave.getScrollingModel().disableAnimation(); + private static void doScrollVertically(@NotNull ScrollingModel model, int offset) { + model.disableAnimation(); try { - if (scrollToLine <= 0) { - int offset = newRectangle.y - oldRectangle.y; - slave.getScrollingModel().scrollVertically(slaveVerticalScrollOffset + offset); - } - else { - int correction = (masterVerticalScrollOffset + middleY) % master.getLineHeight(); - Point point = slave.logicalPositionToXY(new LogicalPosition(actualLine, masterPos.column)); - slave.getScrollingModel().scrollVertically(point.y - middleY + correction); - } - } finally { - slave.getScrollingModel().enableAnimation(); + model.scrollVertically(offset); + } + finally { + model.enableAnimation(); } } - private static int getScrollOffset(@NotNull final Editor editor) { - final JComponent header = editor.getHeaderComponent(); - int headerOffset = header == null ? 0 : header.getHeight(); - - return editor.getScrollingModel().getVerticalScrollOffset() - headerOffset; + private static void doScrollHorizontally(@NotNull ScrollingModel model, int offset) { + model.disableAnimation(); + try { + model.scrollHorizontally(offset); + } + finally { + model.enableAnimation(); + } } public static void scrollEditor(@NotNull Editor editor, int logicalLine) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java index 452d7e144356..cf3c196a9d78 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java @@ -26,6 +26,7 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ExportableComponent; import com.intellij.openapi.components.NamedComponent; import com.intellij.openapi.components.RoamingType; +import com.intellij.openapi.components.StoragePathMacros; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.colors.EditorColorsListener; @@ -67,7 +68,7 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Name private final DefaultColorSchemesManager myDefaultColorSchemesManager; private final SchemesManager<EditorColorsScheme, EditorColorsSchemeImpl> mySchemesManager; @NonNls private static final String NAME_ATTR = "name"; - private static final String FILE_SPEC = "$ROOT_CONFIG$/colors"; + private static final String FILE_SPEC = StoragePathMacros.ROOT_CONFIG + "/colors"; private static final String FILE_EXT = ".icls"; public EditorColorsManagerImpl(DefaultColorSchemesManager defaultColorSchemesManager, SchemesManagerFactory schemesManagerFactory) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java index 216429557cea..7d9973fed2d3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java @@ -407,7 +407,7 @@ public final class EditorUtil { IterationState state = new IterationState(editorImpl, start, end, false); int fontType = state.getMergedAttributes().getFontType(); int column = currentColumn[0]; - int spaceSize = getSpaceWidth(fontType, editorImpl); + int plainSpaceSize = getSpaceWidth(Font.PLAIN, editorImpl); for (; column < columnNumber && offset < end; offset++) { if (offset >= state.getEndOffset()) { state.advance(); @@ -417,7 +417,7 @@ public final class EditorUtil { char c = text.charAt(offset); if (c == '\t') { final int newX = nextTabStop(x, editorImpl); - final int columns = columnsNumber(newX - x, spaceSize); + final int columns = columnsNumber(newX - x, plainSpaceSize); if (debugBuffer != null) { debugBuffer.append(String.format( "Processing tabulation at the offset %d. Current X: %d, new X: %d, current column: %d, new column: %d%n", @@ -556,11 +556,11 @@ public final class EditorUtil { return nextTabStop(x, getSpaceWidth(Font.PLAIN, editor), tabSize); } - public static int nextTabStop(int x, int spaceWidth, int tabSize) { + public static int nextTabStop(int x, int plainSpaceWidth, int tabSize) { if (tabSize <= 0) { - return x + spaceWidth; + return x + plainSpaceWidth; } - tabSize *= spaceWidth; + tabSize *= plainSpaceWidth; int nTabs = x / tabSize; return (nTabs + 1) * tabSize; @@ -617,15 +617,15 @@ public final class EditorUtil { * @param c target char * @param x <code>'x'</code> coordinate of the line where given char is represented that indicates char end location * @param prevX <code>'x'</code> coordinate of the line where given char is represented that indicates char start location - * @param spaceSize <code>'space'</code> symbol width + * @param plainSpaceSize <code>'space'</code> symbol width (in plain font style) * @return number of columns necessary for representation of the given char on a screen. */ - public static int columnsNumber(char c, int x, int prevX, int spaceSize) { + public static int columnsNumber(char c, int x, int prevX, int plainSpaceSize) { if (c != '\t') { return 1; } - int result = (x - prevX) / spaceSize; - if ((x - prevX) % spaceSize > 0) { + int result = (x - prevX) / plainSpaceSize; + if ((x - prevX) % plainSpaceSize > 0) { result++; } return result; @@ -635,12 +635,12 @@ public final class EditorUtil { * Allows to answer how many visual columns are occupied by the given width. * * @param width target width - * @param spaceSize width of the single space symbol within the target editor + * @param plainSpaceSize width of the single space symbol within the target editor (in plain font style) * @return number of visual columns are occupied by the given width */ - public static int columnsNumber(int width, int spaceSize) { - int result = width / spaceSize; - if (width % spaceSize > 0) { + public static int columnsNumber(int width, int plainSpaceSize) { + int result = width / plainSpaceSize; + if (width % plainSpaceSize > 0) { result++; } return result; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java index c769f92fff78..e2693b2c835f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java @@ -1156,7 +1156,13 @@ public class CaretImpl extends UserDataHolderBase implements Caret { @Override public void setSelection(int startOffset, int endOffset) { - doSetSelection(myEditor.offsetToVisualPosition(startOffset), startOffset, myEditor.offsetToVisualPosition(endOffset), endOffset, false); + setSelection(startOffset, endOffset, true); + } + + @Override + public void setSelection(int startOffset, int endOffset, boolean updateSystemSelection) { + doSetSelection(myEditor.offsetToVisualPosition(startOffset), startOffset, myEditor.offsetToVisualPosition(endOffset), endOffset, false, + updateSystemSelection); } @Override @@ -1173,16 +1179,22 @@ public class CaretImpl extends UserDataHolderBase implements Caret { @Override public void setSelection(@Nullable VisualPosition startPosition, int startOffset, @Nullable VisualPosition endPosition, int endOffset) { + setSelection(startPosition, startOffset, endPosition, endOffset, true); + } + + @Override + public void setSelection(@Nullable VisualPosition startPosition, int startOffset, @Nullable VisualPosition endPosition, int endOffset, boolean updateSystemSelection) { VisualPosition startPositionToUse = startPosition == null ? myEditor.offsetToVisualPosition(startOffset) : startPosition; VisualPosition endPositionToUse = endPosition == null ? myEditor.offsetToVisualPosition(endOffset) : endPosition; - doSetSelection(startPositionToUse, startOffset, endPositionToUse, endOffset, true); + doSetSelection(startPositionToUse, startOffset, endPositionToUse, endOffset, true, updateSystemSelection); } private void doSetSelection(@NotNull final VisualPosition startPosition, final int _startOffset, @NotNull final VisualPosition endPosition, final int _endOffset, - final boolean visualPositionAware) + final boolean visualPositionAware, + final boolean updateSystemSelection) { myEditor.getCaretModel().doWithCaretMerging(new Runnable() { public void run() { @@ -1278,7 +1290,9 @@ public class CaretImpl extends UserDataHolderBase implements Caret { myEditor.getSelectionModel().fireSelectionChanged(oldSelectionStart, oldSelectionEnd, startOffset, endOffset); - updateSystemSelection(); + if (updateSystemSelection) { + updateSystemSelection(); + } } }); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java index 8d6608e63dd9..15a907e826e4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java @@ -443,6 +443,11 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener, @Override public void setCaretsAndSelections(@NotNull final List<CaretState> caretStates) { + setCaretsAndSelections(caretStates, true); + } + + @Override + public void setCaretsAndSelections(@NotNull final List<CaretState> caretStates, final boolean updateSystemSelection) { myEditor.assertIsDispatchThread(); if (caretStates.isEmpty()) { throw new IllegalArgumentException("At least one caret should exist"); @@ -474,9 +479,11 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener, caret.moveToLogicalPosition(caretState.getCaretPosition()); } if (caretState != null && caretState.getSelectionStart() != null && caretState.getSelectionEnd() != null) { - caret.setSelection(myEditor.logicalToVisualPosition(caretState.getSelectionStart()), myEditor.logicalPositionToOffset(caretState.getSelectionStart()), - myEditor.logicalToVisualPosition(caretState.getSelectionEnd()), myEditor.logicalPositionToOffset( - caretState.getSelectionEnd())); + caret.setSelection(myEditor.logicalToVisualPosition(caretState.getSelectionStart()), + myEditor.logicalPositionToOffset(caretState.getSelectionStart()), + myEditor.logicalToVisualPosition(caretState.getSelectionEnd()), + myEditor.logicalPositionToOffset(caretState.getSelectionEnd()), + updateSystemSelection); } } int caretsToRemove = myCarets.size() - caretStates.size(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ContextMenuImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ContextMenuImpl.java index 28a7c2204265..cbe6da5379cf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ContextMenuImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ContextMenuImpl.java @@ -46,16 +46,15 @@ import java.awt.event.ActionListener; public class ContextMenuImpl extends JPanel implements Disposable { @NonNls public static final String ACTION_GROUP = "EditorContextBarMenu"; - - private ActionGroup myActionGroup; private final JComponent myComponent; + private final JLayeredPane myLayeredPane; + private ActionGroup myActionGroup; private boolean myVisible = false; private boolean myShow = false; private int myCurrentOpacity; private Timer myTimer; private EditorImpl myEditor; private boolean myDisposed; - private final JLayeredPane myLayeredPane; private ActionToolbar myActionToolbar; public ContextMenuImpl(JLayeredPane layeredPane, @NotNull final JScrollPane container, @NotNull final EditorImpl editor) { @@ -81,7 +80,7 @@ public class ContextMenuImpl extends JPanel implements Disposable { } }); - AnAction action = actionManager.getAction("EditorContextBarMenu"); + AnAction action = actionManager.getAction(ACTION_GROUP); if (action == null) { action = new DefaultActionGroup(); actionManager.registerAction(ACTION_GROUP, action); @@ -107,6 +106,15 @@ public class ContextMenuImpl extends JPanel implements Disposable { return activationArea.contains(p.x, p.y - viewPosition.y); } + public static boolean mayShowToolbar(@Nullable final Document document) { + if (document == null) { + return false; + } + + final VirtualFile file = FileDocumentManager.getInstance().getFile(document); + return file != null && file.isValid() && (file.isInLocalFileSystem() || file instanceof HttpVirtualFile); + } + private void toggleContextToolbar(final boolean show) { final Component toolbar = myComponent.getComponent(0); final int count = ((Container)toolbar).getComponentCount(); @@ -201,15 +209,6 @@ public class ContextMenuImpl extends JPanel implements Disposable { } } - public static boolean mayShowToolbar(@Nullable final Document document) { - if (document == null) { - return false; - } - - final VirtualFile file = FileDocumentManager.getInstance().getFile(document); - return file != null && file.isValid() && (file.isInLocalFileSystem() || file instanceof HttpVirtualFile); - } - private void scheduleHide() { if (myTimer != null && myTimer.isRunning()) { myTimer.stop(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java index ec0cef4e76d5..d0313e04cf9f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java @@ -203,14 +203,17 @@ public class EditorFactoryImpl extends EditorFactory implements ApplicationCompo @Override public void releaseEditor(@NotNull Editor editor) { try { - ((EditorImpl)editor).release(); + myEditorFactoryEventDispatcher.getMulticaster().editorReleased(new EditorFactoryEvent(this, editor)); } finally { - myEditors.remove(editor); - myEditorFactoryEventDispatcher.getMulticaster().editorReleased(new EditorFactoryEvent(this, editor)); - - if (LOG.isDebugEnabled()) { - LOG.debug("number of Editor's:" + myEditors.size()); + try { + ((EditorImpl)editor).release(); + } + finally { + myEditors.remove(editor); + if (LOG.isDebugEnabled()) { + LOG.debug("number of Editor's:" + myEditors.size()); + } } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index 39706f016adc..f773f842f323 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -1236,7 +1236,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse GutterIconRenderer renderer = getGutterRenderer(e); AnAction clickAction = null; - if (renderer != null) { + if (renderer != null && e.getButton() < 4) { clickAction = (InputEvent.BUTTON2_MASK & e.getModifiers()) > 0 ? renderer.getMiddleButtonClickAction() : renderer.getClickAction(); @@ -1447,8 +1447,8 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse public void process(int x, int y, GutterMark renderer) { final int ex = convertX((int)p.getX()); Icon icon = renderer.getIcon(); - if (x <= ex && ex <= x + icon.getIconWidth() && - y <= p.getY() && p.getY() <= y + icon.getIconHeight()) { + // Do not check y to extend the area where users could click + if (x <= ex && ex <= x + icon.getIconWidth()) { result[0] = renderer; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 6160ea58dcdf..51f6ec4a8cdb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -743,12 +743,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myGutterComponent.repaint(0, y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - y); } // make sure carets won't appear at invalid positions (e.g. on Tab width change) - getCaretModel().runForEachCaret(new CaretAction() { - @Override - public void perform(Caret caret) { - caret.moveToOffset(caret.getOffset()); - } - }); + for (Caret caret : getCaretModel().getAllCarets()) { + caret.moveToOffset(caret.getOffset()); + } } private void initTabPainter() { @@ -1153,8 +1150,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi int textLength = myDocument.getTextLength(); LogicalPosition logicalPosition = visualToLogicalPosition(new VisualPosition(line, 0)); int offset = logicalPositionToOffset(logicalPosition); + int plainSpaceSize = EditorUtil.getSpaceWidth(Font.PLAIN, this); - if (offset >= textLength) return new VisualPosition(line, EditorUtil.columnsNumber(p.x, EditorUtil.getSpaceWidth(Font.PLAIN, this))); + if (offset >= textLength) return new VisualPosition(line, EditorUtil.columnsNumber(p.x, plainSpaceSize)); // There is a possible case that starting logical line is split by soft-wraps and it's part after the split should be drawn. // We mark that we're under such circumstances then. @@ -1172,12 +1170,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi + "to offset %d (end offset). State: %s", p, line, line, 0, logicalPosition, offset, line + 1, 0, endLogicalPosition, endOffset, dumpState() )); - return new VisualPosition(line, EditorUtil.columnsNumber(p.x, EditorUtil.getSpaceWidth(Font.PLAIN, this))); + return new VisualPosition(line, EditorUtil.columnsNumber(p.x, plainSpaceSize)); } IterationState state = new IterationState(this, offset, endOffset, false); int fontType = state.getMergedAttributes().getFontType(); - int spaceSize = EditorUtil.getSpaceWidth(fontType, this); int x = 0; int charWidth; @@ -1221,7 +1218,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (x >= px) { break outer; } - column += EditorUtil.columnsNumber(c, x, prevX, spaceSize); + column += EditorUtil.columnsNumber(c, x, prevX, plainSpaceSize); } // Process 'after soft wrap' sign. @@ -1261,7 +1258,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (x >= px) { break; } - column += EditorUtil.columnsNumber(c, x, prevX, spaceSize); + column += EditorUtil.columnsNumber(c, x, prevX, plainSpaceSize); offset++; } @@ -1272,16 +1269,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } if (charWidth < 0) { - charWidth = spaceSize; + charWidth = plainSpaceSize; } if (x >= px && c == '\t' && !onSoftWrapDrawing) { if (mySettings.isCaretInsideTabs()) { - column += (px - prevX) / spaceSize; - if ((px - prevX) % spaceSize > spaceSize / 2) column++; + column += (px - prevX) / plainSpaceSize; + if ((px - prevX) % plainSpaceSize > plainSpaceSize / 2) column++; } else if ((x - px) * 2 < x - prevX) { - column += EditorUtil.columnsNumber(c, x, prevX, spaceSize); + column += EditorUtil.columnsNumber(c, x, prevX, plainSpaceSize); } } else { @@ -1290,8 +1287,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } else { int diff = px - x; - column += diff / spaceSize; - if (diff % spaceSize * 2 >= spaceSize) { + column += diff / plainSpaceSize; + if (diff % plainSpaceSize * 2 >= plainSpaceSize) { column++; } } @@ -1549,7 +1546,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi // so, we can't just use 'startOffset + targetColumn' as a max end offset. IterationState state = new IterationState(this, startOffset, calcEndOffset(startOffset, targetColumn), false); int fontType = state.getMergedAttributes().getFontType(); - int spaceSize = EditorUtil.getSpaceWidth(fontType, this); + int plainSpaceSize = EditorUtil.getSpaceWidth(Font.PLAIN, this); int column = 0; outer: @@ -1588,8 +1585,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (c == '\t') { int prevX = x; x = EditorUtil.nextTabStop(x, this); - int columnDiff = (x - prevX) / spaceSize; - if ((x - prevX) % spaceSize > 0) { + int columnDiff = (x - prevX) / plainSpaceSize; + if ((x - prevX) % plainSpaceSize > 0) { // There is a possible case that tabulation symbol takes more than one visual column to represent and it's shown at // soft-wrapped line. Soft wrap sign width may be not divisible by space size, hence, part of tabulation symbol represented // as a separate visual column may take less space than space width. @@ -2895,7 +2892,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi + ", soft wraps: " + (mySoftWrapModel.isSoftWrappingEnabled() ? "on" : "off") + ", soft wraps data: " + getSoftWrapModel().dumpState() + "\n\nfolding data: " + getFoldingModel().dumpState() - + (myDocument instanceof DocumentImpl ? "\n\ndocument info: " + ((DocumentImpl)myDocument).dumpState() : ""); + + (myDocument instanceof DocumentImpl ? "\n\ndocument info: " + ((DocumentImpl)myDocument).dumpState() : "") + + "\nfont preferences: " + myScheme.getFontPreferences(); } private class CachedFontContent { @@ -4824,22 +4822,21 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi int getDecScrollButtonHeight() { ScrollBarUI barUI = getUI(); Insets insets = getInsets(); + int top = Math.max(0, insets.top); if (barUI instanceof ButtonlessScrollBarUI) { - return insets.top + ((ButtonlessScrollBarUI)barUI).getDecrementButtonHeight(); + return top + ((ButtonlessScrollBarUI)barUI).getDecrementButtonHeight(); } - else if (barUI instanceof BasicScrollBarUI) { + if (barUI instanceof BasicScrollBarUI) { try { JButton decrButtonValue = (JButton)decrButtonField.get(barUI); LOG.assertTrue(decrButtonValue != null); - return insets.top + decrButtonValue.getHeight(); + return top + decrButtonValue.getHeight(); } catch (Exception exc) { throw new IllegalStateException(exc); } } - else { - return insets.top + 15; - } + return top + 15; } /** @@ -6623,7 +6620,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi int fontType = state.getMergedAttributes().getFontType(); int column = 0; int x = 0; - int spaceSize = EditorUtil.getSpaceWidth(fontType, this); + int plainSpaceSize = EditorUtil.getSpaceWidth(Font.PLAIN, this); for (int i = start; i < offset; i++) { if (i >= state.getEndOffset()) { state.advance(); @@ -6639,7 +6636,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (c == '\t') { int prevX = x; x = EditorUtil.nextTabStop(x, this); - column += EditorUtil.columnsNumber(c, x, prevX, spaceSize); + column += EditorUtil.columnsNumber(c, x, prevX, plainSpaceSize); } else { x += EditorUtil.charWidth(c, fontType, this); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index 20c08ec4ec99..032573521b56 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -42,6 +42,7 @@ import com.intellij.openapi.editor.ex.*; import com.intellij.openapi.editor.markup.ErrorStripeRenderer; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.fileEditor.impl.EditorWindowHolder; +import com.intellij.openapi.ui.GraphicsConfig; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.util.Disposer; @@ -127,9 +128,10 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark void recalcEditorDimensions() { EditorImpl.MyScrollBar scrollBar = myEditor.getVerticalScrollBar(); - int scrollBarHeight = scrollBar.getSize().height; + int scrollBarHeight = Math.max(0, scrollBar.getSize().height); myEditorScrollbarTop = scrollBar.getDecScrollButtonHeight()/* + 1*/; + assert myEditorScrollbarTop>=0; int editorScrollbarBottom = scrollBar.getIncScrollButtonHeight(); myEditorTargetHeight = scrollBarHeight - myEditorScrollbarTop - editorScrollbarBottom; myEditorSourceHeight = myEditor.getPreferredHeight(); @@ -758,20 +760,32 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark Color color, boolean drawTopDecoration, boolean drawBottomDecoration) { + GraphicsConfig config = GraphicsUtil.setupAAPainting(g); int x = isMirrored() ? 3 : 5; int paintWidth = width; boolean flatStyle = Registry.is("ide.new.markup.markers"); if (thinErrorStripeMark) { paintWidth /= 2; - paintWidth += flatStyle ? 0 : 1; + paintWidth += flatStyle ? -2 : 1; x = isMirrored() ? width + 2 : 0; + if (yEnd - yStart < 6) { + yStart -= 1; + yEnd += yEnd-yStart - 1; + } } if (color == null) return; - Color darker = UIUtil.isUnderDarcula()? color : ColorUtil.shift(color, 0.75); + Color darker = color; + if (!UIUtil.isUnderDarcula()) { + float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); + hsb[2] = Math.min(1f, hsb[2] * 0.85f); + //noinspection UseJBColor + darker = new Color(Color.HSBtoRGB(hsb[0], hsb[1], hsb[2])); + } if (flatStyle) { g.setColor(darker); - g.fillRect(x, yStart, paintWidth, yEnd - yStart + 1); + g.fillRoundRect(x, yStart, paintWidth, yEnd - yStart, 3,3); + config.restore(); return; } @@ -794,6 +808,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } //right decoration UIUtil.drawLine(g, x + paintWidth - 2, yStart, x + paintWidth - 2, yEnd/* - 1*/); + config.restore(); } // mouse events @@ -1056,29 +1071,30 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark int startLineNumber = end == -1 ? 0 : offsetToLine(start, document); int startY; int lineCount; - if (myEditorSourceHeight < myEditorTargetHeight) { + int editorTargetHeight = Math.max(0, myEditorTargetHeight); + if (myEditorSourceHeight < editorTargetHeight) { lineCount = 0; startY = myEditorScrollbarTop + startLineNumber * myEditor.getLineHeight(); } else { lineCount = myEditorSourceHeight / myEditor.getLineHeight(); - startY = myEditorScrollbarTop + (int)((float)startLineNumber / lineCount * myEditorTargetHeight); + startY = myEditorScrollbarTop + (int)((float)startLineNumber / lineCount * editorTargetHeight); } int endY; int endLineNumber = offsetToLine(end, document); if (end == -1 || start == -1) { - endY = Math.min(myEditorSourceHeight, myEditorTargetHeight); + endY = Math.min(myEditorSourceHeight, editorTargetHeight); } else if (start == end || offsetToLine(start, document) == endLineNumber) { endY = startY; // both offsets are on the same line, no need to recalc Y position } else { - if (myEditorSourceHeight < myEditorTargetHeight) { + if (myEditorSourceHeight < editorTargetHeight) { endY = myEditorScrollbarTop + endLineNumber * myEditor.getLineHeight(); } else { - endY = myEditorScrollbarTop + (int)((float)endLineNumber / lineCount * myEditorTargetHeight); + endY = myEditorScrollbarTop + (int)((float)endLineNumber / lineCount * editorTargetHeight); } } if (endY < startY) endY = startY; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java index 3a819d88a370..881dc4100204 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java @@ -270,8 +270,9 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { EditorPosition position = new EditorPosition(logical, start, myEditor); position.x = point.x; int spaceWidth = EditorUtil.getSpaceWidth(myContext.fontType, myEditor); + int plainSpaceWidth = EditorUtil.getSpaceWidth(Font.PLAIN, myEditor); - myContext.logicalLineData.update(logical.line, spaceWidth, myEditor); + myContext.logicalLineData.update(logical.line, spaceWidth, plainSpaceWidth); myContext.currentPosition = position; myContext.lineStartPosition = position.clone(); @@ -363,8 +364,7 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { revertListeners(softWrap.getStart(), myContext.currentPosition.visualLine); for (int j = foldRegion.getStartOffset() - 1; j >= softWrap.getStart(); j--) { int pixelsDiff = myOffset2widthInPixels.data[j - myOffset2widthInPixels.anchor]; - int tmpFontType = myOffset2fontType.get(j); - int columnsDiff = calculateWidthInColumns(myContext.text.charAt(j), pixelsDiff, myContext.getSpaceWidth(tmpFontType)); + int columnsDiff = calculateWidthInColumns(myContext.text.charAt(j), pixelsDiff, myContext.getPlainSpaceWidth()); myContext.currentPosition.offset--; myContext.currentPosition.logicalColumn -= columnsDiff; myContext.currentPosition.visualColumn -= columnsDiff; @@ -582,8 +582,7 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { revertListeners(actualSoftWrapOffset, myContext.currentPosition.visualLine); for (int j = offset - 1; j >= actualSoftWrapOffset; j--) { int pixelsDiff = myOffset2widthInPixels.data[j - myOffset2widthInPixels.anchor]; - int tmpFontType = myOffset2fontType.get(j); - int columnsDiff = calculateWidthInColumns(myContext.text.charAt(j), pixelsDiff, myContext.getSpaceWidth(tmpFontType)); + int columnsDiff = calculateWidthInColumns(myContext.text.charAt(j), pixelsDiff, myContext.getPlainSpaceWidth()); myContext.currentPosition.offset--; myContext.currentPosition.logicalColumn -= columnsDiff; myContext.currentPosition.visualColumn -= columnsDiff; @@ -630,12 +629,12 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { return Math.max(start, end); } - private static int calculateWidthInColumns(char c, int widthInPixels, int spaceWithInPixels) { + private static int calculateWidthInColumns(char c, int widthInPixels, int plainSpaceWithInPixels) { if (c != '\t') { return 1; } - int result = widthInPixels / spaceWithInPixels; - if (widthInPixels % spaceWithInPixels > 0) { + int result = widthInPixels / plainSpaceWithInPixels; + if (widthInPixels % plainSpaceWithInPixels > 0) { result++; } return result; @@ -869,7 +868,7 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { } } updateLastTopLeftCornerOffset(); - return result; + return true; } private void updateLastTopLeftCornerOffset() { @@ -1035,7 +1034,7 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { public int endLineOffset; public int nonWhiteSpaceSymbolOffset; - public void update(int logicalLine, int spaceWidth, Editor editor) { + public void update(int logicalLine, int spaceWidth, int plainSpaceWidth) { Document document = myEditor.getDocument(); int startLineOffset; if (logicalLine >= document.getLineCount()) { @@ -1055,8 +1054,8 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { switch (c) { case ' ': indentInColumns += 1; indentInPixels += spaceWidth; break; case '\t': - int x = EditorUtil.nextTabStop(indentInPixels, editor); - indentInColumns += calculateWidthInColumns(c, x - indentInPixels, spaceWidth); + int x = EditorUtil.nextTabStop(indentInPixels, myEditor); + indentInColumns += calculateWidthInColumns(c, x - indentInPixels, plainSpaceWidth); indentInPixels = x; break; default: nonWhiteSpaceSymbolOffset = i; return; @@ -1270,8 +1269,12 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { public int getSpaceWidth() { return getSpaceWidth(fontType); } - - public int getSpaceWidth(@JdkConstants.FontStyle int fontType) { + + public int getPlainSpaceWidth() { + return getSpaceWidth(Font.PLAIN); + } + + private int getSpaceWidth(@JdkConstants.FontStyle int fontType) { int result = fontType2spaceWidth.get(fontType); if (result <= 0) { result = EditorUtil.getSpaceWidth(fontType, myEditor); @@ -1280,7 +1283,7 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { assert result > 0; return result; } - + /** * Asks current context to update its state assuming that it begins to point to the line next to its current position. */ @@ -1293,7 +1296,7 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { lastFoldEndPosition = null; lastFold = null; lineStartPosition.from(currentPosition); - logicalLineData.update(currentPosition.logicalLine, getSpaceWidth(), myEditor); + logicalLineData.update(currentPosition.logicalLine, getSpaceWidth(), getPlainSpaceWidth()); fontType = myOffset2fontType.get(currentPosition.offset); myOffset2fontType.clear(); @@ -1330,7 +1333,7 @@ public class SoftWrapApplianceManager implements DocumentListener, Dumpable { myOffset2widthInPixels.data[currentPosition.offset - myOffset2widthInPixels.anchor] = widthInPixels; myOffset2widthInPixels.end++; - int widthInColumns = calculateWidthInColumns(c, widthInPixels, myContext.getSpaceWidth()); + int widthInColumns = calculateWidthInColumns(c, widthInPixels, myContext.getPlainSpaceWidth()); if (c == '\t') { notifyListenersOnVisualLineStart(myContext.lineStartPosition); notifyListenersOnTabulation(widthInColumns); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java index 92ef12c46558..48ba57e150cf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java @@ -151,6 +151,12 @@ public class TextComponentCaret extends UserDataHolderBase implements Caret { } @Override + public void setSelection(int startOffset, int endOffset, boolean updateSystemSelection) { + // updating system selection is not supported currently for TextComponentEditor + setSelection(startOffset, endOffset); + } + + @Override public void setSelection(int startOffset, @Nullable VisualPosition endPosition, int endOffset) { getSelectionModel().setSelection(startOffset, endPosition, endOffset); } @@ -161,6 +167,13 @@ public class TextComponentCaret extends UserDataHolderBase implements Caret { } @Override + public void setSelection(@Nullable VisualPosition startPosition, int startOffset, @Nullable VisualPosition endPosition, int endOffset, + boolean updateSystemSelection) { + // updating system selection is not supported currently for TextComponentEditor + setSelection(startPosition, startOffset, endPosition, endOffset); + } + + @Override public void removeSelection() { getSelectionModel().removeSelection(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaretModel.java b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaretModel.java index ac415f5f9409..341c8ace3598 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaretModel.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaretModel.java @@ -187,6 +187,11 @@ public class TextComponentCaretModel implements CaretModel { throw new UnsupportedOperationException("Multiple carets are not supported"); } + @Override + public void setCaretsAndSelections(@NotNull List<CaretState> caretStates, boolean updateSystemSelection) { + throw new UnsupportedOperationException("Multiple carets are not supported"); + } + @NotNull @Override public List<CaretState> getCaretsAndSelections() { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index b1f9ec0c2b93..b9e38809faab 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -148,7 +148,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener } - private boolean showEmptyText() { + protected boolean showEmptyText() { return myCurrentWindow == null || myCurrentWindow.getFiles().length == 0; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java index cce6de27545d..ec2559fc06ba 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java @@ -550,14 +550,18 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Virt if (document != null) { // a file is linked to a document - chances are it is an "unknown text file" now if (isBinaryWithoutDecompiler(file)) { - myDocuments.remove(file); - file.putUserData(HARD_REF_TO_DOCUMENT_KEY, null); - document.putUserData(FILE_KEY, null); + unbindFileFromDocument(file, document); } } } } + private void unbindFileFromDocument(@NotNull VirtualFile file, @NotNull Document document) { + myDocuments.remove(file); + file.putUserData(HARD_REF_TO_DOCUMENT_KEY, null); + document.putUserData(FILE_KEY, null); + } + private static boolean isBinaryWithDecompiler(@NotNull VirtualFile file) { final FileType ft = file.getFileType(); return ft.isBinary() && BinaryFileTypeDecompilers.INSTANCE.forFileType(ft) != null; @@ -609,6 +613,13 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Virt return; } + if (file.getLength() > FileUtilRt.LARGE_FOR_CONTENT_LOADING) { + unbindFileFromDocument(file, document); + myUnsavedDocuments.remove(document); + myMultiCaster.fileWithNoDocumentChanged(file); + return; + } + final Project project = ProjectLocator.getInstance().guessProjectForFile(file); CommandProcessor.getInstance().executeCommand(project, new Runnable() { @Override @@ -640,7 +651,7 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Virt public boolean process(final VirtualFile file, final Document document) { String message = UIBundle.message("file.cache.conflict.message.text", file.getPresentableUrl()); - final DialogBuilder builder = new DialogBuilder((Project)null); + final DialogBuilder builder = new DialogBuilder(); builder.setCenterPanel(new JLabel(message, Messages.getQuestionIcon(), SwingConstants.CENTER)); builder.addOkAction().setText(UIBundle.message("file.cache.conflict.load.fs.changes.button")); builder.addCancelAction().setText(UIBundle.message("file.cache.conflict.keep.memory.changes.button")); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index bab6d2b2fa4f..65adf52041b9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -58,6 +58,7 @@ import com.intellij.openapi.vcs.FileStatusListener; import com.intellij.openapi.vcs.FileStatusManager; import com.intellij.openapi.vfs.*; import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; @@ -90,6 +91,7 @@ import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.*; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * @author Anton Katilin @@ -106,10 +108,12 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public static final String FILE_EDITOR_MANAGER = "FileEditorManager"; private volatile JPanel myPanels; + private PreviewPanel myPreviewPanel; private EditorsSplitters mySplitters; private final Project myProject; private final List<Pair<VirtualFile, EditorWindow>> mySelectionHistory = new ArrayList<Pair<VirtualFile, EditorWindow>>(); private WeakReference<EditorComposite> myLastSelectedComposite = new WeakReference<EditorComposite>(null); + private final AtomicBoolean myPreviewBlocker = new AtomicBoolean(false); private final MergingUpdateQueue myQueue = new MergingUpdateQueue("FileEditorManagerUpdateQueue", 50, true, null); @@ -174,7 +178,11 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } public Set<EditorsSplitters> getAllSplitters() { - HashSet<EditorsSplitters> all = new HashSet<EditorsSplitters>(); + HashSet<EditorsSplitters> all = new LinkedHashSet<EditorsSplitters>(); + if (Registry.is("editor.use.preview")) { + initUI(); + all.add(myPreviewPanel.getWindow().getOwner()); + } all.add(getMainSplitters()); Set<DockContainer> dockContainers = myDockManager.getContainers(); for (DockContainer each : dockContainers) { @@ -247,6 +255,11 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } } } + if (myPreviewPanel == null && Registry.is("editor.use.preview")) { + synchronized (myInitLock) { + myPreviewPanel = new PreviewPanel(myProject, this, myDockManager); + } + } } private static class MyBorder implements Border { @@ -595,7 +608,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @NotNull public Pair<FileEditor[], FileEditorProvider[]> openFileWithProviders(@NotNull final VirtualFile file, final boolean focusEditor, - boolean searchForSplitter) { + final boolean searchForSplitter) { if (!file.isValid()) { throw new IllegalArgumentException("file is not valid: " + file); } @@ -628,6 +641,13 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec wndToOpenIn = getSplitters().getCurrentWindow(); } + if (wndToOpenIn == null || !wndToOpenIn.isFileOpen(file)) { + EditorWindow previewWindow = getPreviewWindow(file, focusEditor, searchForSplitter); + if (previewWindow != null) { + wndToOpenIn = previewWindow; + } + } + EditorsSplitters splitters = getSplitters(); if (wndToOpenIn == null) { @@ -638,6 +658,31 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return openFileImpl2(wndToOpenIn, file, focusEditor); } + @Nullable + private EditorWindow getPreviewWindow(@NotNull VirtualFile virtualFile, final boolean focusEditor, final boolean searchForSplitter) { + EditorWindow wndToOpenIn = null; + if (Registry.is("editor.use.preview") && !myPreviewBlocker.get()) { + wndToOpenIn = myPreviewPanel.getWindow(); + if (virtualFile.equals(myPreviewPanel.getCurrentFile())) return wndToOpenIn; + final VirtualFile modifiedFile = myPreviewPanel.closeCurrentFile(); + ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).activate(null, false); + if (modifiedFile != null) { + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + @Override + public void run() { + myPreviewBlocker.set(true); + try { + openFileWithProviders(modifiedFile, focusEditor, searchForSplitter); + } finally { + myPreviewBlocker.set(false); + } + } + }, "", null); + } + } + return wndToOpenIn; + } + public Pair<FileEditor[], FileEditorProvider[]> openFileInNewWindow(@NotNull VirtualFile file) { return ((DockManagerImpl)DockManager.getInstance(getProject())).createNewDockContainerFor(file, this); } @@ -1610,7 +1655,10 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return null; } - public void runChange(FileEditorManagerChange change, EditorsSplitters splitters) { + /** + * @param splitters - taken getAllSplitters() value if parameter is null + */ + public void runChange(@NotNull FileEditorManagerChange change, @Nullable EditorsSplitters splitters) { Set<EditorsSplitters> target = new HashSet<EditorsSplitters>(); if (splitters == null) { target.addAll(getAllSplitters()); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java new file mode 100644 index 000000000000..a9634d5a335a --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java @@ -0,0 +1,244 @@ +/* + * Copyright 2000-2014 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.fileEditor.impl; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.ui.UISettings; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.event.DocumentListener; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.FileEditorManagerListener; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindowAnchor; +import com.intellij.openapi.wm.ToolWindowId; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.impl.ToolWindowImpl; +import com.intellij.ui.JBColor; +import com.intellij.ui.docking.DockManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; + +class PreviewPanel extends JPanel implements DocumentListener, FileEditorManagerListener.Before { + private static final int HISTORY_LIMIT = 10; + + private final Project myProject; + private final FileEditorManagerImpl myManager; + private final DockManager myDockManager; + private EditorWindow myWindow; + private boolean myInitialized = false; + private EditorsSplitters myEditorsSplitters; + private ArrayList<VirtualFile> myHistory = new ArrayList<VirtualFile>(); + private VirtualFile myModifiedFile = null; + private ToolWindowImpl myToolWindow; + private VirtualFile myAwaitingForOpen = null; + + public PreviewPanel(Project project, FileEditorManagerImpl manager, DockManager dockManager) { + myProject = project; + myManager = manager; + myDockManager = dockManager; + setOpaque(true); + setBackground(JBColor.DARK_GRAY); + } + + private void initToolWindowIfNeed() { + if (myInitialized) return; + + myToolWindow = (ToolWindowImpl)ToolWindowManager.getInstance(myProject) + .registerToolWindow(ToolWindowId.PREVIEW, this, ToolWindowAnchor.RIGHT, myProject, false); + myToolWindow.setIcon(AllIcons.Actions.PreviewDetails); + + myEditorsSplitters = new EditorsSplitters(myManager, myDockManager, false) { + @Override + public void updateFileName(VirtualFile updatedFile) { + super.updateFileName(updatedFile); + if (updatedFile != null && updatedFile.equals(getCurrentFile())) { + updateWindowTitle(updatedFile); + } + } + + @Override + protected void afterFileOpen(VirtualFile file) { + if (file.equals(myAwaitingForOpen)) { + updateWindowTitle(file); + Document document = FileDocumentManager.getInstance().getDocument(file); + if (document != null) { + myModifiedFile = null; + document.addDocumentListener(PreviewPanel.this, myProject); + } + } + myAwaitingForOpen = null; + } + + @Override + public void setTabsPlacement(int tabPlacement) { + super.setTabsPlacement(UISettings.TABS_NONE); + } + + @Override + protected boolean showEmptyText() { + return false; + } + }; + + myProject.getMessageBus().connect().subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER, this); + myEditorsSplitters.createCurrentWindow(); + + myWindow = myEditorsSplitters.getCurrentWindow(); + myWindow.setTabsPlacement(UISettings.TABS_NONE); + + setLayout(new GridLayout(1, 1)); + add(myEditorsSplitters); + + myToolWindow.setTitleActions(new MoveToEditorTabsAction(), new CloseFileAction()); + + myInitialized = true; + } + + private void updateWindowTitle(VirtualFile file) { + if (myToolWindow == null) return; + if (file == null) { + myToolWindow.setTitle(": (empty)"); + } + else { + myToolWindow.setTitle(": " + + StringUtil.getShortened(EditorTabbedContainer.calcTabTitle(myProject, file), + UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT)); + } + } + + @Override + public void beforeFileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { + myAwaitingForOpen = file; + } + + @Override + public void beforeFileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { + if (file.equals(getCurrentFile())) { + updateWindowTitle(null); + Document document = FileDocumentManager.getInstance().getDocument(file); + if (document != null) { + document.removeDocumentListener(this); + } + } + } + + + @Override + public void beforeDocumentChange(DocumentEvent event) { + + } + + @Override + public void documentChanged(DocumentEvent event) { + VirtualFile file = FileDocumentManager.getInstance().getFile(event.getDocument()); + if (file != null) { + myModifiedFile = file; + } + } + + EditorWindow getWindow() { + initToolWindowIfNeed(); + return myWindow; + } + + @Nullable + VirtualFile getCurrentFile() { + VirtualFile[] files = myWindow.getFiles(); + return files.length == 1 ? files[0] : null; + } + + private class MoveToEditorTabsAction extends AnAction { + public MoveToEditorTabsAction() { + super(null, "Move to main tabs", AllIcons.Duplicates.SendToTheLeftGrayed); + } + + @Override + public void actionPerformed(AnActionEvent e) { + VirtualFile virtualFile = getCurrentFile(); + if (virtualFile == null) { + return; + } + + myManager.openFileWithProviders(virtualFile, false, myManager.getCurrentWindow()); + closeCurrentFile(); + } + + @Override + public void update(AnActionEvent e) { + super.update(e); + VirtualFile currentFile = getCurrentFile(); + e.getPresentation().setEnabled(currentFile != null); + if (currentFile == null) return; + + if (isModified(currentFile)) { + e.getPresentation().setIcon(AllIcons.Duplicates.SendToTheLeft); + } + else { + e.getPresentation().setIcon(AllIcons.Duplicates.SendToTheLeftGrayed); + } + } + } + + private boolean isModified(@NotNull VirtualFile file) { + return file.equals(myModifiedFile); + } + + //returns last open file if it has "modified" status + @Nullable + VirtualFile closeCurrentFile() { + VirtualFile virtualFile = getCurrentFile(); + if (virtualFile == null) return null; + if (!myHistory.contains(virtualFile)) { + myHistory.add(virtualFile); + while (myHistory.size() > HISTORY_LIMIT) { + myHistory.remove(0); + } + } + myWindow.closeFile(virtualFile); + this.revalidate(); + this.repaint(); + return isModified(virtualFile) ? virtualFile : null; + } + + private class CloseFileAction extends AnAction { + public CloseFileAction() { + super(null, "Close", AllIcons.Actions.Close); + } + + @Override + public void actionPerformed(AnActionEvent e) { + if (getCurrentFile() == null) return; + closeCurrentFile(); + ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).hide(null); + } + + @Override + public void update(AnActionEvent e) { + super.update(e); + e.getPresentation().setEnabled(getCurrentFile() != null); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java index b24927b70726..076d29e64bbe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -32,7 +32,7 @@ import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; import com.intellij.openapi.vfs.impl.http.RemoteFileInfo; import com.intellij.openapi.vfs.impl.http.RemoteFileState; import com.intellij.ui.AppUIUtil; -import com.intellij.util.net.HTTPProxySettingsDialog; +import com.intellij.util.net.HttpConfigurable; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; @@ -86,22 +86,22 @@ public class RemoteFilePanel { remoteFileInfo.addDownloadingListener(myDownloadingListener); myCancelButton.addActionListener(new ActionListener() { @Override - public void actionPerformed(final ActionEvent e) { + public void actionPerformed(@NotNull final ActionEvent e) { remoteFileInfo.cancelDownloading(); } }); myTryAgainButton.addActionListener(new ActionListener() { @Override - public void actionPerformed(final ActionEvent e) { + public void actionPerformed(@NotNull final ActionEvent e) { showCard(DOWNLOADING_CARD); remoteFileInfo.restartDownloading(); } }); myChangeProxySettingsButton.addActionListener(new ActionListener() { @Override - public void actionPerformed(final ActionEvent e) { - new HTTPProxySettingsDialog().show(); + public void actionPerformed(@NotNull ActionEvent e) { + HttpConfigurable.editConfigurable(myMainPanel); } }); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorProvider.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorProvider.java index 690e7c7d2820..14f0c95ec31b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorProvider.java @@ -287,7 +287,7 @@ public class TextEditorProvider implements FileEditorProvider, DumbAware { new LogicalPosition(caretState.SELECTION_START_LINE, caretState.SELECTION_START_COLUMN), new LogicalPosition(caretState.SELECTION_END_LINE, caretState.SELECTION_END_COLUMN))); } - caretModel.setCaretsAndSelections(states); + caretModel.setCaretsAndSelections(states, false); } else { LogicalPosition pos = new LogicalPosition(state.CARETS[0].LINE, state.CARETS[0].COLUMN); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java index 198ed907a8d9..837626b53246 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.impl.TransferToPooledThreadQueue; import com.intellij.openapi.components.ExportableApplicationComponent; import com.intellij.openapi.components.RoamingType; +import com.intellij.openapi.components.StoragePathMacros; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; @@ -120,7 +121,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements NamedJDOME private static final String[] FILE_TYPES_WITH_PREDEFINED_EXTENSIONS = {"JSP", "JSPX", "DTD", "HTML", "Properties", "XHTML"}; private final SchemesManager<FileType, AbstractFileType> mySchemesManager; @NonNls - private static final String FILE_SPEC = "$ROOT_CONFIG$/filetypes"; + private static final String FILE_SPEC = StoragePathMacros.ROOT_CONFIG + "/filetypes"; private final ConcurrentBitSet autoDetectWasRun = new ConcurrentBitSet(); private final ConcurrentBitSet autoDetectedAsText = new ConcurrentBitSet(); private final ConcurrentBitSet autoDetectedAsBinary = new ConcurrentBitSet(); diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java index 7c65a805ee6c..7c30e4faedec 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java @@ -67,7 +67,7 @@ public class KeymapManagerImpl extends KeymapManagerEx implements PersistentStat KeymapManagerImpl(DefaultKeymap defaultKeymap, SchemesManagerFactory factory) { mySchemesManager = factory.createSchemesManager( - "$ROOT_CONFIG$/keymaps", + StoragePathMacros.ROOT_CONFIG + "/keymaps", new BaseSchemeProcessor<KeymapImpl>() { @Override public KeymapImpl readScheme(@NotNull final Document schemeContent) throws InvalidDataException, IOException, JDOMException { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/CompositeConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/CompositeConfigurable.java index 0b9e5ade8d29..aaba0fdbaf6b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/CompositeConfigurable.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/CompositeConfigurable.java @@ -20,18 +20,21 @@ import java.util.List; public abstract class CompositeConfigurable<T extends UnnamedConfigurable> extends BaseConfigurable { private List<T> myConfigurables; + @Override public void reset() { for (T configurable : getConfigurables()) { configurable.reset(); } } + @Override public void apply() throws ConfigurationException { for (T configurable : getConfigurables()) { configurable.apply(); } } + @Override public boolean isModified() { for (T configurable : getConfigurables()) { if (configurable.isModified()) { @@ -41,6 +44,7 @@ public abstract class CompositeConfigurable<T extends UnnamedConfigurable> exten return false; } + @Override public void disposeUIResources() { if (myConfigurables != null) { for (final T myConfigurable : myConfigurables) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java b/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java deleted file mode 100644 index d7c7dca2f19b..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2000-2014 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.options; - -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; - -public abstract class ConfigurableBase<UI extends ConfigurableUi<S>, S> implements SearchableConfigurable, Configurable.NoScroll { - private final String id; - private final String displayName; - private final String helpTopic; - - private UI ui; - - protected ConfigurableBase(@NotNull String id, @NotNull String displayName, @Nullable String helpTopic) { - this.id = id; - this.displayName = displayName; - this.helpTopic = helpTopic; - } - - @NotNull - @Override - public final String getId() { - return id; - } - - @Nls - @Override - public final String getDisplayName() { - return displayName; - } - - @Nullable - @Override - public final String getHelpTopic() { - return helpTopic; - } - - @Nullable - @Override - public Runnable enableSearch(String option) { - return null; - } - - @NotNull - protected abstract S getSettings(); - - @Override - public void reset() { - if (ui != null) { - ui.reset(getSettings()); - } - } - - @Nullable - @Override - public final JComponent createComponent() { - if (ui == null) { - ui = createUi(); - } - return ui.getComponent(); - } - - protected abstract UI createUi(); - - @Override - public final boolean isModified() { - return ui != null && ui.isModified(getSettings()); - } - - @Override - public final void apply() { - if (ui != null) { - ui.apply(getSettings()); - } - } - - @Override - public void disposeUIResources() { - ui = null; - } -}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java b/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java deleted file mode 100644 index 0ecb19a7ce8e..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2000-2014 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.options; - -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; - -public interface ConfigurableUi<S> { - void reset(@NotNull S settings); - - boolean isModified(@NotNull S settings); - - void apply(@NotNull S settings); - - @NotNull - JComponent getComponent(); -}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java index 323394ad7c94..449a2180e3c1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java @@ -21,6 +21,7 @@ import com.intellij.openapi.application.impl.ApplicationImpl; import com.intellij.openapi.components.RoamingType; import com.intellij.openapi.components.ServiceBean; import com.intellij.openapi.components.SettingsSavingComponent; +import com.intellij.openapi.components.impl.stores.IApplicationStore; import com.intellij.openapi.components.impl.stores.StreamProvider; import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.containers.ContainerUtil; @@ -32,21 +33,22 @@ import java.util.Collections; import java.util.List; public class SchemesManagerFactoryImpl extends SchemesManagerFactory implements SettingsSavingComponent { - - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.options.SchemesManagerFactoryImpl"); + private static final Logger LOG = Logger.getInstance(SchemesManagerFactoryImpl.class); private final List<SchemesManagerImpl> myRegisteredManagers = ContainerUtil.createLockFreeCopyOnWriteList(); @Override - public <T extends Scheme, E extends ExternalizableScheme> SchemesManager<T, E> createSchemesManager(final String fileSpec, - final SchemeProcessor<E> processor, - final RoamingType roamingType) { + public <T extends Scheme, E extends ExternalizableScheme> SchemesManager<T, E> createSchemesManager(@NotNull String fileSpec, + @NotNull SchemeProcessor<E> processor, + @NotNull RoamingType roamingType) { final Application application = ApplicationManager.getApplication(); - if (!(application instanceof ApplicationImpl)) return null; - String baseDirPath = ((ApplicationImpl)application).getStateStore().getStateStorageManager().expandMacros(fileSpec); - + if (!(application instanceof ApplicationImpl)) { + return null; + } + IApplicationStore applicationStore = ((ApplicationImpl)application).getStateStore(); + String baseDirPath = applicationStore.getStateStorageManager().expandMacros(fileSpec); if (baseDirPath != null) { - StreamProvider provider = ((ApplicationImpl)ApplicationManager.getApplication()).getStateStore().getStateStorageManager().getStreamProvider(); + StreamProvider provider = applicationStore.getStateStorageManager().getStreamProvider(); SchemesManagerImpl<T, E> manager = new SchemesManagerImpl<T, E>(fileSpec, processor, roamingType, provider, new File(baseDirPath)); myRegisteredManagers.add(manager); return manager; diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java index 1c7e768c7d83..4ff3ebba1ee9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java @@ -34,6 +34,7 @@ import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.util.Alarm; +import com.intellij.util.SmartList; import com.intellij.util.UniqueFileNamesProvider; import com.intellij.util.containers.HashSet; import com.intellij.util.text.UniqueNameGenerator; @@ -51,7 +52,7 @@ import java.io.IOException; import java.util.*; public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme> extends AbstractSchemesManager<T, E> { - private static final Logger LOG = Logger.getInstance("#" + SchemesManagerFactoryImpl.class.getName()); + private static final Logger LOG = Logger.getInstance(SchemesManagerFactoryImpl.class); @NonNls private static final String DEFAULT_EXT = ".xml"; @@ -292,12 +293,13 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } } + @NotNull private Collection<E> readSchemesFromProviders() { - Collection<E> result = new ArrayList<E>(); if (myProvider == null || !myProvider.isEnabled()) { - return result; + return Collections.emptyList(); } + Collection<E> result = new SmartList<E>(); for (String subPath : myProvider.listSubFiles(myFileSpec, myRoamingType)) { if (!subPath.equals(DELETED_XML)) { try { @@ -305,6 +307,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme if (subDocument != null) { E scheme = readScheme(subDocument); boolean fileRenamed = false; + assert scheme != null; T existing = findSchemeByName(scheme.getName()); if (existing != null && existing instanceof ExternalizableScheme) { String currentFileName = ((ExternalizableScheme)existing).getExternalInfo().getCurrentFileName(); @@ -315,7 +318,6 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } } String fileName = checkFileNameIsFree(subPath, scheme.getName()); - if (!fileRenamed && !fileName.equals(subPath)) { deleteServerFiles(subPath); } @@ -325,7 +327,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } } catch (Exception e) { - LOG.info("Cannot load data from IDEAServer: " + e.getLocalizedMessage()); + LOG.info("Cannot load data from stream provider: " + e.getLocalizedMessage()); } } } @@ -347,21 +349,22 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme }); } - private String checkFileNameIsFree(final String subPath, final String schemeName) { + @NotNull + private String checkFileNameIsFree(@NotNull String subPath, @NotNull String schemeName) { for (Scheme scheme : mySchemes) { if (scheme instanceof ExternalizableScheme) { - ExternalInfo externalInfo = ((ExternalizableScheme)scheme).getExternalInfo(); - String name = externalInfo.getCurrentFileName(); - if (name != null) { - String fileName = name + mySchemeExtension; - if (fileName.equals(subPath) && !Comparing.equal(schemeName, scheme.getName())) { - return createUniqueFileName(collectAllFileNames(), UniqueFileNamesProvider.convertName(schemeName)); + String name = ((ExternalizableScheme)scheme).getExternalInfo().getCurrentFileName(); + if (name != null && + !schemeName.equals(scheme.getName()) && + subPath.length() == (name.length() + mySchemeExtension.length()) && + subPath.startsWith(name) && + subPath.endsWith(mySchemeExtension)) { + return UniqueNameGenerator.generateUniqueName(UniqueFileNamesProvider.convertName(schemeName), collectAllFileNames()); /*VirtualFile oldFile = myVFSBaseDir.findChild(subPath); if (oldFile != null) { oldFile.copy(this, myVFSBaseDir, uniqueFileName + EXT); } externalInfo.setCurrentFileName(uniqueFileName);*/ - } } } } @@ -369,8 +372,9 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return subPath; } + @NotNull private Collection<String> collectAllFileNames() { - HashSet<String> result = new HashSet<String>(); + Set<String> result = new THashSet<String>(); for (T scheme : mySchemes) { if (scheme instanceof ExternalizableScheme) { ExternalInfo externalInfo = ((ExternalizableScheme)scheme).getExternalInfo(); @@ -382,10 +386,6 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return result; } - private static String createUniqueFileName(final Collection<String> strings, final String schemeName) { - return UniqueNameGenerator.generateUniqueName(schemeName, strings); - } - private void loadScheme(final E scheme, boolean forceAdd, final String name) { if (scheme != null && (!myDeletedNames.contains(scheme.getName()) || forceAdd)) { T existing = findSchemeByName(scheme.getName()); @@ -469,13 +469,6 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } final E scheme = readScheme(document); if (scheme != null) { - if (scheme.getName() == null) { - String suggestedName = FileUtil.getNameWithoutExtension(file.getName()); - if (!"_".equals(suggestedName)) { - scheme.setName(suggestedName); - } - } - loadScheme(scheme, forceAdd, file.getName()); result.add(scheme); } @@ -653,8 +646,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme result = new SharedSchemeData(original, name, user, description); } else { - Document original = subDocument; - result = new SharedSchemeData(original, name, null, null); + result = new SharedSchemeData(subDocument, name, null, null); } return result; } @@ -673,8 +665,8 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return false; } - private String getFileFullPath(final String subPath) { - return myFileSpec + "/" + subPath; + private String getFileFullPath(@NotNull String subPath) { + return myFileSpec + '/' + subPath; } @Override @@ -843,9 +835,9 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme deleteServerFiles(fileName); } - private void deleteServerFiles(final String fileName) { + private void deleteServerFiles(@NotNull String path) { if (myProvider != null && myProvider.isEnabled()) { - StorageUtil.deleteContent(myProvider, getFileFullPath(fileName), myRoamingType); + StorageUtil.delete(myProvider, getFileFullPath(path), myRoamingType); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java deleted file mode 100644 index 9812165932a2..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2000-2014 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.options; - -import com.intellij.openapi.util.Getter; -import com.intellij.util.ReflectionUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class SimpleConfigurable<UI extends ConfigurableUi<S>, S> extends ConfigurableBase<UI, S> { - private final Class<UI> uiClass; - private final Getter<S> settingsGetter; - - private SimpleConfigurable(@NotNull String id, @NotNull String displayName, @Nullable String helpTopic, @NotNull Class<UI> uiClass, @NotNull Getter<S> settingsGetter) { - super(id, displayName, helpTopic); - - this.uiClass = uiClass; - this.settingsGetter = settingsGetter; - } - - public static <UI extends ConfigurableUi<S>, S> SimpleConfigurable<UI, S> create(@NotNull String id, @NotNull String displayName, @Nullable String helpTopic, @NotNull Class<UI> uiClass, @NotNull Getter<S> settingsGetter) { - return new SimpleConfigurable<UI, S>(id, displayName, helpTopic, uiClass, settingsGetter); - } - - public static <UI extends ConfigurableUi<S>, S> SimpleConfigurable<UI, S> create(@NotNull String id, @NotNull String displayName, @NotNull Class<UI> uiClass, @NotNull Getter<S> settingsGetter) { - return create(id, displayName, id, uiClass, settingsGetter); - } - - @NotNull - @Override - protected S getSettings() { - return settingsGetter.get(); - } - - @Override - protected UI createUi() { - return ReflectionUtil.newInstance(uiClass); - } -}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java index 5f8dc0e890df..3b67a3478368 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java @@ -165,10 +165,6 @@ public class ConfigurableWrapper implements SearchableConfigurable { return myEp; } - public String getGroupId() { - return myEp.groupId; - } - public String getParentId() { return myEp.parentId; } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/GlassPanel.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/GlassPanel.java index 96cc9e451185..c80a222ffd96 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/GlassPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/GlassPanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -17,7 +17,10 @@ package com.intellij.openapi.options.ex; import com.intellij.ide.ui.search.SearchUtil; +import com.intellij.openapi.ui.GraphicsConfig; +import com.intellij.ui.ColorUtil; import com.intellij.ui.components.JBTabbedPane; +import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; @@ -27,6 +30,7 @@ import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.image.Kernel; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -64,17 +68,19 @@ public class GlassPanel extends JComponent { final Point leftPoint = SwingUtilities.convertPoint(myPanel, new Point(visibleRect.x, visibleRect.y), surfaceComponent); Area innerPanel = new Area(new Rectangle2D.Double(leftPoint.x, leftPoint.y, visibleRect.width, visibleRect.height)); Area mask = new Area(screen); - + ArrayList<JComponent> components = new ArrayList<JComponent>(); for (JComponent lightComponent : myLightComponents) { - final Area area = getComponentArea(surfaceComponent, lightComponent); + final Area area = getComponentArea(surfaceComponent, lightComponent, 1); if (area == null) continue; + components.add(lightComponent); if (lightComponent instanceof JLabel) { final JLabel label = (JLabel)lightComponent; final Component labelFor = label.getLabelFor(); if (labelFor instanceof JComponent) { - final Area labelForArea = getComponentArea(surfaceComponent, (JComponent)labelFor); + final Area labelForArea = getComponentArea(surfaceComponent, (JComponent)labelFor, 1); if (labelForArea != null) { + components.add((JComponent)labelFor); area.add(labelForArea); } } @@ -86,19 +92,32 @@ public class GlassPanel extends JComponent { Graphics2D g2 = (Graphics2D)g; - Color shieldColor = new Color(0.0f, 0.0f, 0.0f, 0.15f); + Color shieldColor = new Color(0.0f, 0.0f, 0.0f, 0.20f); Color boundsColor = Color.gray; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(shieldColor); g2.fill(mask); - g2.setColor(boundsColor); - g2.draw(mask); + g2.setColor(ColorUtil.toAlpha(Color.orange, 25)); + GraphicsConfig config = GraphicsUtil.setupAAPainting(g2); + for (int i = 2; i > 0; i--) { + g2.setStroke(new BasicStroke(i)); + Area arrr = new Area(); + for (JComponent component : components) { + Area area = getComponentArea(surfaceComponent, component, i-1); + if (area != null) { + arrr.add(area); + } + } + g2.draw(arrr); + } + + config.restore(); } } @Nullable - private Area getComponentArea(final JComponent surfaceComponent, final JComponent lightComponent) { + private Area getComponentArea(final JComponent surfaceComponent, final JComponent lightComponent, int offset) { if (!lightComponent.isShowing()) return null; final Point panelPoint = SwingUtilities.convertPoint(lightComponent, new Point(0, 0), surfaceComponent); @@ -115,12 +134,17 @@ public class GlassPanel extends JComponent { int hInset = isWithBorder ? 7 : isLabelFromTabbedPane ? 20 : 7; int vInset = isWithBorder ? 1 : isLabelFromTabbedPane ? 10 : 5; - final Area area = new Area(new RoundRectangle2D.Double(x - hInset + insetsToIgnore.left, - y - vInset + insetsToIgnore.top, - lightComponent.getWidth() + hInset * 2 - insetsToIgnore.right - insetsToIgnore.left, - lightComponent.getHeight() + vInset * 2 - insetsToIgnore.top - insetsToIgnore.bottom, - 6, 6)); - return area; + hInset += offset; + vInset += offset; + int xCoord = x - hInset + insetsToIgnore.left; + int yCoord = y - vInset + insetsToIgnore.top; + int width = lightComponent.getWidth() + hInset * 2 - insetsToIgnore.right - insetsToIgnore.left; + int height = lightComponent.getHeight() + vInset * 2 - insetsToIgnore.top - insetsToIgnore.bottom; + return new Area(new RoundRectangle2D.Double(xCoord, + yCoord, + width, + height, + Math.min(height, 30), Math.min(height, 30))); } protected static Kernel getBlurKernel(int blurSize) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java index f1411cf3551d..e3404ec017cd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java @@ -17,15 +17,21 @@ package com.intellij.openapi.options.ex; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; +import javax.swing.JComponent; -public final class MixedConfigurableGroup implements ConfigurableGroup { +public final class MixedConfigurableGroup implements SearchableConfigurable, ConfigurableGroup { private final String myGroupId; private Configurable[] myConfigurables; @@ -34,6 +40,7 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { myConfigurables = (configurables != null) ? configurables.toArray(new Configurable[configurables.size()]) : new Configurable[0]; + Arrays.sort(myConfigurables, COMPARATOR); } private MixedConfigurableGroup(String groupId, HashMap<String, ArrayList<Configurable>> configurables) { @@ -41,6 +48,45 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { } @Override + public JComponent createComponent() { + return null; + } + + @Override + public boolean isModified() { + return false; + } + + @Override + public void apply() throws ConfigurationException { + } + + @Override + public void reset() { + } + + @Override + public void disposeUIResources() { + myConfigurables = null; + } + + @Override + public Runnable enableSearch(String option) { + return null; + } + + @NotNull + @Override + public String getId() { + return "configurable.group." + myGroupId; + } + + @Override + public String getHelpTopic() { + return "configurable.group." + myGroupId + ".help.topic"; + } + + @Override public String getDisplayName() { return OptionsBundle.message("configurable.group." + myGroupId + ".settings.display.name"); } @@ -60,7 +106,7 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { for (Configurable configurable : configurables) { String groupId = null; if (configurable instanceof ConfigurableWrapper) { - groupId = ((ConfigurableWrapper)configurable).getGroupId(); + groupId = ((ConfigurableWrapper)configurable).getExtensionPoint().groupId; } ArrayList<Configurable> list = map.get(groupId); if (list == null) { @@ -70,7 +116,7 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { } ArrayList<Configurable> buildList = map.get("build"); if (buildList != null) { - NodeConfigurable buildTools = new NodeConfigurable("build.tools"); + NodeConfigurable buildTools = new NodeConfigurable("build.tools", 1000); buildTools.add(find("MavenSettings", buildList.iterator())); buildTools.add(find("reference.settingsdialog.project.gradle", buildList.iterator())); buildTools.add(find("reference.settingsdialog.project.gant", buildList.iterator())); @@ -106,4 +152,27 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { } return null; } + + public static int getGroupWeight(Configurable configurable) { + if (configurable instanceof NodeConfigurable) { + return ((NodeConfigurable)configurable).getGroupWeight(); + } + if (configurable instanceof ConfigurableWrapper) { + return ((ConfigurableWrapper)configurable).getExtensionPoint().groupWeight; + } + return 0; + } + + private static final Comparator<Configurable> COMPARATOR = new Comparator<Configurable>() { + @Override + public int compare(Configurable configurable1, Configurable configurable2) { + if (configurable1 == null || configurable2 == null) { + return configurable2 != null ? -1 : configurable1 != null ? 1 : 0; + } + int weight1 = getGroupWeight(configurable1); + int weight2 = getGroupWeight(configurable2); + return weight1 > weight2 ? -1 : weight1 < weight2 ? 1 : StringUtil.naturalCompare(configurable1.getDisplayName(), + configurable2.getDisplayName()); + } + }; } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java index 6571c7a63746..609943d02943 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java @@ -27,9 +27,15 @@ import java.util.ArrayList; public final class NodeConfigurable extends SearchableConfigurable.Parent.Abstract { private final ArrayList<Configurable> myConfigurables = new ArrayList<Configurable>(); private final String myId; + private final int myWeight; - public NodeConfigurable(@NotNull String id) { + public NodeConfigurable(@NotNull String id, int weight) { myId = id; + myWeight = weight; + } + + public int getGroupWeight() { + return myWeight; } public void add(Configurable configurable) { @@ -48,13 +54,13 @@ public final class NodeConfigurable extends SearchableConfigurable.Parent.Abstra @NotNull @Override public String getId() { - return myId; + return "node.configurable." + myId; } @Nullable @Override public String getHelpTopic() { - return myId; + return "node.configurable." + myId + ".help.topic"; } @Nls diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java index 80144d230eaf..def0dd16a2d2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java @@ -46,7 +46,7 @@ public class SingleConfigurableEditor extends DialogWrapper { private Component myParentComponent; private Configurable myConfigurable; private JComponent myCenterPanel; - private String myDimensionKey; + private final String myDimensionKey; private final boolean myShowApplyButton; private boolean myChangesWereApplied; @@ -134,6 +134,7 @@ public class SingleConfigurableEditor extends DialogWrapper { return displayName.replaceAll("\n", " "); } + @Override protected String getDimensionServiceKey() { if (myDimensionKey == null) { return super.getDimensionServiceKey(); @@ -143,6 +144,7 @@ public class SingleConfigurableEditor extends DialogWrapper { } } + @Override @NotNull protected Action[] createActions() { List<Action> actions = new ArrayList<Action>(); @@ -157,6 +159,7 @@ public class SingleConfigurableEditor extends DialogWrapper { return actions.toArray(new Action[actions.size()]); } + @Override protected void doHelpAction() { HelpManager.getInstance().invokeHelp(myConfigurable.getHelpTopic()); } @@ -169,6 +172,7 @@ public class SingleConfigurableEditor extends DialogWrapper { super.doCancelAction(); } + @Override protected void doOKAction() { try { if (myConfigurable.isModified()) myConfigurable.apply(); @@ -201,10 +205,11 @@ public class SingleConfigurableEditor extends DialogWrapper { public ApplyAction() { super(CommonBundle.getApplyButtonText()); final Runnable updateRequest = new Runnable() { + @Override public void run() { - if (!SingleConfigurableEditor.this.isShowing()) return; + if (!isShowing()) return; try { - ApplyAction.this.setEnabled(myConfigurable != null && myConfigurable.isModified()); + setEnabled(myConfigurable != null && myConfigurable.isModified()); } catch (IndexNotReadyException ignored) { } @@ -214,6 +219,7 @@ public class SingleConfigurableEditor extends DialogWrapper { // invokeLater necessary to make sure dialog is already shown so we calculate modality state correctly. SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { addUpdateRequest(updateRequest); } @@ -224,6 +230,7 @@ public class SingleConfigurableEditor extends DialogWrapper { myUpdateAlarm.addRequest(updateRequest, 500, ModalityState.stateForComponent(getWindow())); } + @Override public void actionPerformed(ActionEvent event) { if (myPerformAction) return; try { @@ -248,11 +255,13 @@ public class SingleConfigurableEditor extends DialogWrapper { } } + @Override protected JComponent createCenterPanel() { myCenterPanel = myConfigurable.createComponent(); return myCenterPanel; } + @Override public JComponent getPreferredFocusedComponent() { if (myConfigurable instanceof BaseConfigurable) { JComponent preferred = ((BaseConfigurable)myConfigurable).getPreferredFocusedComponent(); @@ -261,6 +270,7 @@ public class SingleConfigurableEditor extends DialogWrapper { return IdeFocusTraversalPolicy.getPreferredFocusedComponent(myCenterPanel); } + @Override public void dispose() { super.dispose(); myConfigurable.disposeUIResources(); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java new file mode 100644 index 000000000000..c38bf6ff2033 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java @@ -0,0 +1,330 @@ +/* + * Copyright 2000-2014 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.options.newEditor; + +import com.intellij.CommonBundle; +import com.intellij.ide.ui.search.SearchUtil; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.help.HelpManager; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.Gray; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.JBColor; +import com.intellij.ui.border.CustomLineBorder; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Konstantin Bulenkov + */ +public class IdeSettingsDialog extends DialogWrapper implements DataProvider { + private Project myProject; + private ConfigurableGroup[] myGroups; + private Configurable myPreselected; + private OptionsEditor myEditor; + + private ApplyAction myApplyAction; + public static final String DIMENSION_KEY = "OptionsEditor"; + @NonNls static final String LAST_SELECTED_CONFIGURABLE = "options.lastSelected"; + + /** + * This constructor should be eliminated after the new modality approach + * will have been checked. See a {@code Registry} key ide.perProjectModality + * + * @deprecated + */ + public IdeSettingsDialog(Project project, ConfigurableGroup[] groups, + @Nullable Configurable preselectedConfigurable, boolean applicationModalIfPossible) { + super(project, true, applicationModalIfPossible); + init(project, groups, preselectedConfigurable != null ? preselectedConfigurable : findLastSavedConfigurable(groups, project)); + } + + /** + * This constructor should be eliminated after the new modality approach + * will have been checked. See a {@code Registry} key ide.perProjectModality + * + * @deprecated + */ + public IdeSettingsDialog(Project project, ConfigurableGroup[] groups, + @NotNull String preselectedConfigurableDisplayName, boolean applicationModalIfPossible) { + super(project, true, applicationModalIfPossible); + init(project, groups, getPreselectedByDisplayName(groups, preselectedConfigurableDisplayName, project)); + } + + public IdeSettingsDialog(Project project, ConfigurableGroup[] groups, @Nullable Configurable preselectedConfigurable) { + super(project, true); + init(project, groups, preselectedConfigurable != null ? preselectedConfigurable : findLastSavedConfigurable(groups, project)); + } + + public IdeSettingsDialog(Project project, ConfigurableGroup[] groups, @NotNull String preselectedConfigurableDisplayName) { + super(project, true); + init(project, groups, getPreselectedByDisplayName(groups, preselectedConfigurableDisplayName, project)); + } + + @Nullable + @Override + protected Border createContentPaneBorder() { + return IdeBorderFactory.createEmptyBorder(0); + } + + private void init(final Project project, final ConfigurableGroup[] groups, @Nullable final Configurable preselected) { + myProject = project; + myGroups = groups; + myPreselected = preselected; + + setTitle(CommonBundle.settingsTitle()); + + init(); + } + + @Nullable + private static Configurable getPreselectedByDisplayName(final ConfigurableGroup[] groups, final String preselectedConfigurableDisplayName, + final Project project) { + Configurable result = findPreselectedByDisplayName(preselectedConfigurableDisplayName, groups); + + return result == null ? findLastSavedConfigurable(groups, project) : result; + } + + @Override + public boolean isTypeAheadEnabled() { + return true; + } + + @Nullable + @Override + protected JComponent createSouthPanel() { + final JComponent panel = super.createSouthPanel(); + CustomLineBorder line = new CustomLineBorder(new JBColor(Gray._153.withAlpha(128), Gray._100.withAlpha(128)), 1, 0, 0, 0); + panel.setBorder(new CompoundBorder(line, new EmptyBorder(8, 12, 8, 12))); + return panel; + } + + protected JComponent createCenterPanel() { + myEditor = new OptionsEditor(myProject, myGroups, myPreselected); + myEditor.getContext().addColleague(new OptionsEditorColleague.Adapter() { + @Override + public ActionCallback onModifiedAdded(final Configurable configurable) { + updateStatus(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback onModifiedRemoved(final Configurable configurable) { + updateStatus(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback onErrorsChanged() { + updateStatus(); + return new ActionCallback.Done(); + } + }); + Disposer.register(myDisposable, myEditor); + return myEditor; + } + + public boolean updateStatus() { + myApplyAction.setEnabled(myEditor.canApply()); + + final Map<Configurable, ConfigurationException> errors = myEditor.getContext().getErrors(); + if (errors.size() == 0) { + setErrorText(null); + } + else { + String text = "Changes were not applied because of an error"; + + final String errorMessage = getErrorMessage(errors); + if (errorMessage != null) { + text += "<br>" + errorMessage; + } + + setErrorText(text); + } + + return errors.size() == 0; + } + + @Nullable + private static String getErrorMessage(final Map<Configurable, ConfigurationException> errors) { + final Collection<ConfigurationException> values = errors.values(); + final ConfigurationException[] exceptions = values.toArray(new ConfigurationException[values.size()]); + if (exceptions.length > 0) { + return exceptions[0].getMessage(); + } + return null; + } + + @Override + protected String getDimensionServiceKey() { + return DIMENSION_KEY; + } + + @Override + protected void doOKAction() { + myEditor.flushModifications(); + + if (myEditor.canApply()) { + myEditor.apply(); + if (!updateStatus()) return; + } + + saveCurrentConfigurable(); + + ApplicationManager.getApplication().saveAll(); + + super.doOKAction(); + } + + + private void saveCurrentConfigurable() { + final Configurable current = myEditor.getContext().getCurrentConfigurable(); + if (current == null) return; + + final PropertiesComponent props = PropertiesComponent.getInstance(myProject); + + if (current instanceof SearchableConfigurable) { + props.setValue(LAST_SELECTED_CONFIGURABLE, ((SearchableConfigurable)current).getId()); + } + else { + props.setValue(LAST_SELECTED_CONFIGURABLE, current.getClass().getName()); + } + } + + @Nullable + private static Configurable findLastSavedConfigurable(ConfigurableGroup[] groups, final Project project) { + final String id = PropertiesComponent.getInstance(project).getValue(LAST_SELECTED_CONFIGURABLE); + if (id == null) return null; + + return findConfigurableInGroups(id, groups); + } + + @Nullable + private static Configurable findConfigurableInGroups(String id, Configurable.Composite... groups) { + // avoid unnecessary group expand: check top-level configurables in all groups before looking at children + for (Configurable.Composite group : groups) { + final Configurable[] configurables = group.getConfigurables(); + for (Configurable c : configurables) { + if (c instanceof SearchableConfigurable && id.equals(((SearchableConfigurable)c).getId())) { + return c; + } + else if (id.equals(c.getClass().getName())) { + return c; + } + } + } + for (Configurable.Composite group : groups) { + final Configurable[] configurables = group.getConfigurables(); + for (Configurable c : configurables) { + if (c instanceof Configurable.Composite) { + Configurable result = findConfigurableInGroups(id, (Configurable.Composite)c); + if (result != null) { + return result; + } + } + } + } + return null; + } + + @Nullable + private static Configurable findPreselectedByDisplayName(final String preselectedConfigurableDisplayName, ConfigurableGroup[] groups) { + final List<Configurable> all = SearchUtil.expand(groups); + for (Configurable each : all) { + if (preselectedConfigurableDisplayName.equals(each.getDisplayName())) return each; + } + return null; + } + + @Override + public void doCancelAction(final AWTEvent source) { + if (source instanceof KeyEvent || source instanceof ActionEvent) { + if (myEditor.getContext().isHoldingFilter()) { + myEditor.clearFilter(); + return; + } + } + + super.doCancelAction(source); + } + + @Override + public void doCancelAction() { + saveCurrentConfigurable(); + super.doCancelAction(); + } + + @NotNull + @Override + protected Action[] createActions() { + myApplyAction = new ApplyAction(); + return new Action[]{getOKAction(), getCancelAction(), myApplyAction, getHelpAction()}; + } + + @Override + protected void doHelpAction() { + final String topic = myEditor.getHelpTopic(); + if (topic != null) { + HelpManager.getInstance().invokeHelp(topic); + } + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myEditor.getPreferredFocusedComponent(); + } + + public Object getData(@NonNls String dataId) { + if (OptionsEditor.KEY.is(dataId)) { + return myEditor; + } + return null; + } + + private class ApplyAction extends AbstractAction { + public ApplyAction() { + super(CommonBundle.getApplyButtonText()); + setEnabled(false); + } + + public void actionPerformed(final ActionEvent e) { + myEditor.apply(); + myEditor.revalidate(); + myEditor.repaint(); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java index 271539f038a0..c2119596a06e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java @@ -15,9 +15,12 @@ */ package com.intellij.openapi.options.newEditor; -import com.intellij.ide.ui.search.ConfigurableHit; +import com.intellij.AbstractBundle; +import com.intellij.CommonBundle; +import com.intellij.icons.AllIcons; +import com.intellij.ide.ui.laf.darcula.ui.DarculaTextBorder; +import com.intellij.ide.ui.laf.darcula.ui.DarculaTextFieldUI; import com.intellij.ide.ui.search.SearchUtil; -import com.intellij.ide.ui.search.SearchableOptionsRegistrar; import com.intellij.ide.util.PropertiesComponent; import com.intellij.internal.statistic.UsageTrigger; import com.intellij.internal.statistic.beans.ConvertUsagesUtil; @@ -36,18 +39,18 @@ import com.intellij.openapi.ui.*; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.EdtRunnable; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeGlassPaneUtil; -import com.intellij.ui.DocumentAdapter; import com.intellij.ui.LightColors; +import com.intellij.ui.OnePixelSplitter; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.SearchTextField; +import com.intellij.ui.components.labels.LinkLabel; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.components.panels.Wrapper; import com.intellij.ui.navigation.History; import com.intellij.ui.navigation.Place; -import com.intellij.ui.speedSearch.ElementFilter; import com.intellij.ui.treeStructure.SimpleNode; import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; import com.intellij.util.ui.UIUtil; @@ -61,7 +64,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.event.DocumentEvent; +import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; @@ -77,23 +80,18 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat @NonNls private static final String MAIN_SPLITTER_PROPORTION = "options.splitter.main.proportions"; @NonNls private static final String DETAILS_SPLITTER_PROPORTION = "options.splitter.details.proportions"; - @NonNls private static final String SEARCH_VISIBLE = "options.searchVisible"; - @NonNls private static final String NOT_A_NEW_COMPONENT = "component.was.already.instantiated"; - private final Project myProject; - - private final OptionsEditorContext myContext; - private final History myHistory = new History(this); private final OptionsTree myTree; private final SettingsTreeView myTreeView; - private final MySearchField mySearch; + private final SearchTextField mySearch; private final Splitter myMainSplitter; //[back/forward] JComponent myToolbarComponent; - private final DetailsComponent myOwnDetails = new DetailsComponent().setEmptyContentText("Select configuration element in the tree to edit its settings"); + private final DetailsComponent myOwnDetails = + new DetailsComponent().setEmptyContentText("Select configuration element in the tree to edit its settings"); private final ContentWrapper myContentWrapper = new ContentWrapper(); @@ -101,30 +99,54 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat private final Map<Configurable, ActionCallback> myConfigurable2LoadCallback = new HashMap<Configurable, ActionCallback>(); private final MergingUpdateQueue myModificationChecker; - private final ConfigurableGroup[] myGroups; private final SpotlightPainter mySpotlightPainter = new SpotlightPainter(); private final MergingUpdateQueue mySpotlightUpdate; private final LoadingDecorator myLoadingDecorator; - private final Filter myFilter; + private final SettingsFilter myFilter; private final Wrapper mySearchWrapper = new Wrapper(); private final JPanel myLeftSide; - private boolean myFilterDocumentWasChanged; //[back/forward] private ActionToolbar myToolbar; private Window myWindow; private final PropertiesComponent myProperties; private volatile boolean myDisposed; + private final KeyListener myTreeKeyListener = new KeyListener() { + @Override + public void keyPressed(KeyEvent event) { + keyTyped(event); + } + + @Override + public void keyReleased(KeyEvent event) { + keyTyped(event); + } + + @Override + public void keyTyped(KeyEvent event) { + Object source = event.getSource(); + if (source instanceof JTree) { + JTree tree = (JTree)source; + if (tree.getInputMap().get(KeyStroke.getKeyStrokeForEvent(event)) == null) { + myFilter.myDocumentWasChanged = false; + try { + mySearch.keyEventToTextField(event); + } + finally { + if (myFilter.myDocumentWasChanged && !isFilterFieldVisible()) { + setFilterFieldVisible(true, false, false); + } + } + } + } + } + }; + public OptionsEditor(Project project, ConfigurableGroup[] groups, Configurable preselectedConfigurable) { - myProject = project; - myGroups = groups; myProperties = PropertiesComponent.getInstance(project); - myFilter = new Filter(); - myContext = new OptionsEditorContext(myFilter); - mySearch = new MySearchField() { @Override protected void onTextKeyEvent(final KeyEvent e) { @@ -137,67 +159,53 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } }; - mySearch.getTextEditor().addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - boolean hasText = mySearch.getText().length() > 0; - if (!myContext.isHoldingFilter() && hasText) { - myFilter.reenable(); - } - - if (!isSearchFieldFocused() && hasText) { - mySearch.selectText(); - } - } - }); - - final KeyListener listener = new KeyListener() { + myFilter = new SettingsFilter(project, groups, mySearch) { @Override - public void keyTyped(KeyEvent event) { - myFilterDocumentWasChanged = false; - try { - mySearch.keyEventToTextField(event); - } - finally { - if (myFilterDocumentWasChanged && !isFilterFieldVisible()) { - setFilterFieldVisible(true, false, false); - } + Configurable getConfigurable(SimpleNode node) { + if (node instanceof OptionsTree.EditorNode) { + return ((OptionsTree.EditorNode)node).getConfigurable(); } + return SettingsTreeView.getConfigurable(node); } @Override - public void keyPressed(KeyEvent event) { - keyTyped(event); + SimpleNode findNode(Configurable configurable) { + return myTreeView != null + ? myTreeView.findNode(configurable) + : myTree.findNodeFor(configurable); } @Override - public void keyReleased(KeyEvent event) { - keyTyped(event); + void updateSpotlight(boolean now) { + if (!now) { + mySpotlightUpdate.queue(new Update(this) { + @Override + public void run() { + if (!mySpotlightPainter.updateForCurrentConfigurable()) { + updateSpotlight(false); + } + } + }); + } + else if (!mySpotlightPainter.updateForCurrentConfigurable()) { + updateSpotlight(false); + } } }; - if (Registry.is("ide.file.settings.tree.new")) { - myTreeView = new SettingsTreeView(listener, getContext(), groups); + + if (Registry.is("ide.new.settings.dialog")) { + myTreeView = new SettingsTreeView(myFilter, groups); + myTreeView.myTree.addKeyListener(myTreeKeyListener); myTree = null; } else { myTreeView = null; - myTree = new OptionsTree(myProject, groups, getContext()) { - @Override - protected void onTreeKeyEvent(final KeyEvent e) { - listener.keyTyped(e); - } - }; + myTree = new OptionsTree(myFilter, groups); + myTree.addKeyListener(myTreeKeyListener); } getContext().addColleague(myTreeView != null ? myTreeView : myTree); Disposer.register(this, myTreeView != null ? myTreeView : myTree); - mySearch.addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(DocumentEvent e) { - myFilter.update(e.getType(), true, false); - } - }); - /* [back/forward] final DefaultActionGroup toolbarActions = new DefaultActionGroup(); @@ -246,7 +254,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat setLayout(new BorderLayout()); - myMainSplitter = new Splitter(false); + myMainSplitter = Registry.is("ide.new.settings.dialog") ? new OnePixelSplitter(false) : new Splitter(false); myMainSplitter.setFirstComponent(myLeftSide); myLoadingDecorator = new LoadingDecorator(myOwnDetails.getComponent(), this, 150); @@ -264,13 +272,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat mySpotlightUpdate = new MergingUpdateQueue("OptionsSpotlight", 200, false, this, this, this); if (preselectedConfigurable != null) { - if (myTreeView != null) { - myTreeView.select(preselectedConfigurable); - } - else { - myTree.select(preselectedConfigurable); - } - } else { + selectInTree(preselectedConfigurable); + } + else { if (myTreeView != null) { myTreeView.selectFirst(); } @@ -322,6 +326,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat }); } + private ActionCallback selectInTree(Configurable configurable) { + return myTreeView != null + ? myTreeView.select(configurable) + : myTree.select(configurable); + } + /** @see #select(com.intellij.openapi.options.Configurable) */ @Deprecated public ActionCallback select(Class<? extends Configurable> configurableClass) { @@ -354,18 +364,17 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } public ActionCallback select(Configurable configurable) { - if (StringUtil.isEmpty(mySearch.getText())) { + if (myFilter.getFilterText().isEmpty()) { return select(configurable, ""); - } else { - return myFilter.refilterFor(mySearch.getText(), true, true); + } + else { + return myFilter.update(true, true); } } public ActionCallback select(Configurable configurable, final String text) { - myFilter.refilterFor(text, false, true); - return myTreeView != null - ? myTreeView.select(configurable) - : myTree.select(configurable); + myFilter.update(text, false, true); + return selectInTree(configurable); } private float readProportion(final float defaultValue, final String propertyName) { @@ -390,7 +399,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat if (configurable == null) { myOwnDetails.setContent(null); - updateSpotlight(true); + myFilter.updateSpotlight(true); checkModified(oldConfigurable); result.setDone(); @@ -417,7 +426,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat if (myTreeView != null) { myOwnDetails.forProject(myTreeView.findConfigurableProject(configurable)); } - else if (Registry.is("ide.file.settings.order.new")) { + else if (Registry.is("ide.new.settings.dialog")) { myOwnDetails.forProject(myTree.getConfigurableProject(configurable)); } @@ -430,7 +439,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myLoadingDecorator.stopLoading(); - updateSpotlight(false); + myFilter.updateSpotlight(false); checkModified(oldConfigurable); checkModified(configurable); @@ -480,7 +489,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat ((ApplicationEx)app).runEdtSafeAction(new Runnable() { @Override public void run() { - if (myProject.isDisposed()) { + if (myFilter.myProject.isDisposed()) { result.setRejected(); } else { @@ -537,26 +546,6 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat return result; } - - private void updateSpotlight(boolean now) { - if (now) { - final boolean success = mySpotlightPainter.updateForCurrentConfigurable(); - if (!success) { - updateSpotlight(false); - } - } else { - mySpotlightUpdate.queue(new Update(this) { - @Override - public void run() { - final boolean success = mySpotlightPainter.updateForCurrentConfigurable(); - if (!success) { - updateSpotlight(false); - } - } - }); - } - } - private String[] getBannerText(Configurable configurable) { if (myTreeView != null) { return myTreeView.getPathNames(configurable); @@ -712,7 +701,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat @Override public boolean isEnabled() { - return myContext.isModified(myConfigurable) || getContext().getErrors().containsKey(myConfigurable); + return myFilter.myContext.isModified(myConfigurable) || getContext().getErrors().containsKey(myConfigurable); } } @@ -849,12 +838,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat getContext().fireErrorsChanged(errors, null); if (!errors.isEmpty()) { - if (myTreeView != null) { - myTreeView.select(errors.keySet().iterator().next()); - } - else { - myTree.select(errors.keySet().iterator().next()); - } + selectInTree(errors.keySet().iterator().next()); } } @@ -868,7 +852,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } public JComponent getPreferredFocusedComponent() { - return mySearch;//myTree.getTree(); + return myTreeView != null ? myTreeView.myTree : mySearch;//myTree.getTree(); } @Override @@ -876,145 +860,6 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat return new Dimension(1200, 768); } - private class Filter extends ElementFilter.Active.Impl<SimpleNode> { - - SearchableOptionsRegistrar myIndex = SearchableOptionsRegistrar.getInstance(); - Set<Configurable> myFiltered = null; - ConfigurableHit myHits; - - boolean myUpdateEnabled = true; - private Configurable myLastSelected; - - @Override - public boolean shouldBeShowing(final SimpleNode value) { - if (myFiltered == null) return true; - - if (value instanceof OptionsTree.EditorNode) { - final OptionsTree.EditorNode node = (OptionsTree.EditorNode)value; - return myFiltered.contains(node.getConfigurable()) || isChildOfNameHit(node); - } - - return SettingsTreeView.isFiltered(myFiltered, myHits, value); - } - - private boolean isChildOfNameHit(OptionsTree.EditorNode node) { - if (myHits != null) { - OptionsTree.Base eachParent = node; - while (eachParent != null) { - if (eachParent instanceof OptionsTree.EditorNode) { - final OptionsTree.EditorNode eachEditorNode = (OptionsTree.EditorNode)eachParent; - if (myHits.getNameFullHits().contains(eachEditorNode.myConfigurable)) return true; - } - eachParent = (OptionsTree.Base)eachParent.getParent(); - } - - return false; - } - - return false; - } - - public ActionCallback refilterFor(String text, boolean adjustSelection, final boolean now) { - try { - myUpdateEnabled = false; - mySearch.setText(text); - } - finally { - myUpdateEnabled = true; - } - - return update(DocumentEvent.EventType.CHANGE, adjustSelection, now); - } - - public void clearTemporary() { - myContext.setHoldingFilter(false); - updateSpotlight(false); - } - - public void reenable() { - myContext.setHoldingFilter(true); - updateSpotlight(false); - } - - public ActionCallback update(DocumentEvent.EventType type, boolean adjustSelection, boolean now) { - if (!myUpdateEnabled) return new ActionCallback.Rejected(); - - final String text = mySearch.getText(); - if (getFilterText().length() == 0) { - myContext.setHoldingFilter(false); - myFiltered = null; - } else { - myContext.setHoldingFilter(true); - myHits = myIndex.getConfigurables(myGroups, type, myFiltered, text, myProject); - myFiltered = myHits.getAll(); - } - - if (myFiltered != null && myFiltered.isEmpty()) { - mySearch.getTextEditor().setBackground(LightColors.RED); - } else { - mySearch.getTextEditor().setBackground(UIUtil.getTextFieldBackground()); - } - - - final Configurable current = getContext().getCurrentConfigurable(); - - boolean shouldMoveSelection = true; - - if (myHits != null && (myHits.getNameFullHits().contains(current) || myHits.getContentHits().contains(current))) { - shouldMoveSelection = false; - } - - if (shouldMoveSelection && type != DocumentEvent.EventType.INSERT && (myFiltered == null || myFiltered.contains(current))) { - shouldMoveSelection = false; - } - - Configurable toSelect = adjustSelection ? current : null; - if (shouldMoveSelection && myHits != null) { - if (!myHits.getNameHits().isEmpty()) { - toSelect = suggestToSelect(myHits.getNameHits(), myHits.getNameFullHits()); - } else if (!myHits.getContentHits().isEmpty()) { - toSelect = suggestToSelect(myHits.getContentHits(), null); - } - } - - updateSpotlight(false); - - if ((myFiltered == null || !myFiltered.isEmpty()) && toSelect == null && myLastSelected != null) { - toSelect = myLastSelected; - myLastSelected = null; - } - - if (toSelect == null && current != null) { - myLastSelected = current; - } - - SimpleNode node = !adjustSelection ? null : myTreeView != null ? myTreeView.findNode(toSelect) : myTree.findNodeFor(toSelect); - final ActionCallback callback = fireUpdate(node, adjustSelection, now); - - myFilterDocumentWasChanged = true; - - return callback; - } - - private boolean isEmptyParent(Configurable configurable) { - return configurable instanceof SearchableConfigurable.Parent && !((SearchableConfigurable.Parent)configurable).hasOwnContent(); - } - - @Nullable - private Configurable suggestToSelect(Set<Configurable> set, Set<Configurable> fullHits) { - Configurable candidate = null; - for (Configurable each : set) { - if (fullHits != null && fullHits.contains(each)) return each; - if (!isEmptyParent(each) && candidate == null) { - candidate = each; - } - } - - return candidate; - } - - } - @Override public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) { final Configurable config = (Configurable)place.getPath("configurable"); @@ -1022,15 +867,10 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat final ActionCallback result = new ActionCallback(); - myFilter.refilterFor(filter, false, true).doWhenDone(new Runnable() { + myFilter.update(filter, false, true).doWhenDone(new Runnable() { @Override public void run() { - if (myTreeView != null) { - myTreeView.select(config).notifyWhenDone(result); - } - else { - myTree.select(config).notifyWhenDone(result); - } + selectInTree(config).notifyWhenDone(result); } }); @@ -1041,7 +881,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat public void queryPlace(@NotNull final Place place) { final Configurable current = getContext().getCurrentConfigurable(); place.putPath("configurable", current); - place.putPath("filter", getFilterText()); + place.putPath("filter", myFilter.getFilterText()); if (current instanceof Place.Navigator) { ((Place.Navigator)current).queryPlace(place); @@ -1060,7 +900,6 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myProperties.setValue(MAIN_SPLITTER_PROPORTION, String.valueOf(myMainSplitter.getProportion())); myProperties.setValue(DETAILS_SPLITTER_PROPORTION, String.valueOf(myContentWrapper.myLastSplitterProportion)); - myProperties.setValue(SEARCH_VISIBLE, Boolean.valueOf(isFilterFieldVisible()).toString()); Toolkit.getDefaultToolkit().removeAWTEventListener(this); @@ -1087,7 +926,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } public OptionsEditorContext getContext() { - return myContext; + return myFilter.myContext; } private class MyColleague extends OptionsEditorColleague.Adapter { @@ -1137,7 +976,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat final MouseEvent me = (MouseEvent)event; if (SwingUtilities.isDescendingFrom(me.getComponent(), SwingUtilities.getWindowAncestor(myContentWrapper)) || isPopupOverEditor(me.getComponent())) { queueModificationCheck(); - myFilter.clearTemporary(); + myFilter.setHoldingFilter(false); } } else if (event.getID() == KeyEvent.KEY_PRESSED || event.getID() == KeyEvent.KEY_RELEASED) { @@ -1175,6 +1014,23 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat private MySearchField() { super(false); addKeyListener(new KeyAdapter() {}); + if (Registry.is("ide.new.settings.dialog")) { + final JTextField editor = getTextEditor(); + if (!SystemInfo.isMac) { + editor.putClientProperty("JTextField.variant", "search"); + if (!(editor.getUI() instanceof DarculaTextFieldUI)) { + editor.setUI((DarculaTextFieldUI)DarculaTextFieldUI.createUI(editor)); + editor.setBorder(new DarculaTextBorder()); + } + } + setBackground(UIUtil.getSidePanelColor()); + setBorder(new EmptyBorder(5, 10, 2, 10)); + } + } + + @Override + protected boolean isSearchControlUISupported() { + return true; } @Override @@ -1236,7 +1092,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat return ApplicationManager.getApplication().isUnitTestMode(); } - String text = getFilterText(); + String text = myFilter.getFilterText(); try { final boolean sameText = @@ -1263,14 +1119,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myVisible = true;//myContext.isHoldingFilter(); runnable.run(); - boolean pushFilteringFurther = true; - if (sameText) { - pushFilteringFurther = false; - } else { - if (myFilter.myHits != null) { - pushFilteringFurther = !myFilter.myHits.getNameHits().contains(current); - } - } + boolean pushFilteringFurther = !sameText && !myFilter.contains(current); final Runnable ownSearch = searchable.enableSearch(text); if (pushFilteringFurther && ownSearch != null) { @@ -1295,10 +1144,6 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } } - private String getFilterText() { - return mySearch.getText() != null ? mySearch.getText().trim() : ""; - } - private static class SearachableWrappper implements SearchableConfigurable { private final Configurable myConfigurable; @@ -1366,6 +1211,49 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat abstract void setText(final String[] bannerText); } + /** + * Returns default view for the specified configurable. + * It uses the configurable identifier to retrieve description. + * + * @param searchable the configurable that does not have any view + * @return default view for the specified configurable + */ + private JComponent createDefaultComponent(SearchableConfigurable searchable) { + JPanel box = new JPanel(); + box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); + try { + box.add(new JLabel(getDefaultDescription(searchable))); + } + catch (AssertionError error) { + return null; // description is not set + } + if (searchable instanceof Configurable.Composite) { + box.add(Box.createVerticalStrut(10)); + Configurable.Composite composite = (Configurable.Composite)searchable; + for (final Configurable configurable : composite.getConfigurables()) { + box.add(new LinkLabel(configurable.getDisplayName(), AllIcons.Ide.Link) { + @Override + public void doClick() { + select(configurable, null); + } + }); + } + } + return box; + } + + @NotNull + private static String getDefaultDescription(SearchableConfigurable configurable) { + String key = configurable.getId() + ".settings.description"; + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper) configurable; + ConfigurableEP ep = wrapper.getExtensionPoint(); + ResourceBundle resourceBundle = AbstractBundle.getResourceBundle(ep.bundle, ep.getPluginDescriptor().getPluginClassLoader()); + return CommonBundle.message(resourceBundle, key); + } + return OptionsBundle.message(key); + } + private class Simple extends ConfigurableContent { JComponent myComponent; Configurable myConfigurable; @@ -1373,7 +1261,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat Simple(final Configurable configurable) { myConfigurable = configurable; myComponent = configurable.createComponent(); - + if (myComponent == null && configurable instanceof SearchableConfigurable) { + myComponent = createDefaultComponent((SearchableConfigurable)configurable); + } if (myComponent != null) { final Object clientProperty = myComponent.getClientProperty(NOT_A_NEW_COMPONENT); if (clientProperty != null && ApplicationManager.getApplication().isInternal()) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java index 175b994122ef..aee9e889fdf1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java @@ -37,6 +37,7 @@ import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure; import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; +import com.intellij.util.ui.tree.WideSelectionTreeUI; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import org.jetbrains.annotations.NotNull; @@ -49,33 +50,32 @@ import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.TreeUI; -import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.*; -import java.awt.event.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; import java.util.*; import java.util.List; public class OptionsTree extends JPanel implements Disposable, OptionsEditorColleague { - Project myProject; + private final SettingsFilter myFilter; final SimpleTree myTree; List<ConfigurableGroup> myGroups; FilteringTreeBuilder myBuilder; Root myRoot; - OptionsEditorContext myContext; Map<Configurable, EditorNode> myConfigurable2Node = new HashMap<Configurable, EditorNode>(); MergingUpdateQueue mySelection; private final OptionsTree.Renderer myRenderer; - public OptionsTree(Project project, ConfigurableGroup[] groups, OptionsEditorContext context) { - myProject = project; + public OptionsTree(SettingsFilter filter, ConfigurableGroup... groups) { + myFilter = filter; myGroups = Arrays.asList(groups); - myContext = context; - myRoot = new Root(); final SimpleTreeStructure structure = new SimpleTreeStructure() { @@ -132,35 +132,8 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } } }); - myTree.addKeyListener(new KeyListener() { - public void keyTyped(final KeyEvent e) { - _onTreeKeyEvent(e); - } - - public void keyPressed(final KeyEvent e) { - _onTreeKeyEvent(e); - } - - public void keyReleased(final KeyEvent e) { - _onTreeKeyEvent(e); - } - }); - } - - protected void _onTreeKeyEvent(KeyEvent e) { - final KeyStroke stroke = KeyStroke.getKeyStrokeForEvent(e); - - final Object action = myTree.getInputMap().get(stroke); - if (action == null) { - onTreeKeyEvent(e); - } - } - - protected void onTreeKeyEvent(KeyEvent e) { - } - ActionCallback select(@Nullable Configurable configurable) { return queueSelection(configurable); } @@ -191,7 +164,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl if (configurable == null) { myTree.getSelectionModel().clearSelection(); - myContext.fireSelected(null, OptionsTree.this); + myFilter.myContext.fireSelected(null, OptionsTree.this); } else { myBuilder.getReady(this).doWhenDone(new Runnable() { @@ -232,7 +205,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } private void fireSelected(Configurable configurable, final ActionCallback callback) { - myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); + myFilter.myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); } @@ -297,7 +270,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl myRendererComponent.setOpaqueActive(false); mySeparator = new GroupSeparator(); - myRendererComponent.add(Registry.is("ide.file.settings.order.new") ? mySeparator : mySeparatorComponent, BorderLayout.NORTH); + myRendererComponent.add(Registry.is("ide.new.settings.dialog") ? mySeparator : mySeparatorComponent, BorderLayout.NORTH); final NonOpaquePanel content = new NonOpaquePanel(new BorderLayout()); myHandle = new JLabel("", SwingConstants.CENTER); @@ -401,12 +374,12 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl myTextLabel.setForeground(selected ? UIUtil.getTreeSelectionForeground() : fg); myTextLabel.setOpaque(selected); - if (Registry.is("ide.file.settings.order.new")) { + if (Registry.is("ide.new.settings.dialog")) { myTextLabel.setBorder(new EmptyBorder(1,2,1,0)); } Project project = null; - if (base != null && Registry.is("ide.file.settings.order.new")) { + if (base != null && Registry.is("ide.new.settings.dialog")) { SimpleNode parent = base.getParent(); if (parent == myRoot) { project = getConfigurableProject(base); // show icon for top-level nodes @@ -433,6 +406,9 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } else { myProjectIcon.setVisible(false); } + if (Registry.is("ide.new.settings.dialog")) { + result.setBackground(selected ? UIUtil.getTreeSelectionBackground() : UIUtil.getSidePanelColor()); + } return result; } @@ -538,7 +514,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl final List<EditorNode> result = new ArrayList<EditorNode>(kids.length); for (Configurable child : kids) { result.add(new EditorNode(parent, child, group)); - myContext.registerKid(configurable, child); + myFilter.myContext.registerKid(configurable, child); } return result; // TODO: DECIDE IF INNERS SHOULD BE SORTED: sort(result); } @@ -602,12 +578,12 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl @Override boolean isModified() { - return myContext.getModified().contains(myConfigurable); + return myFilter.myContext.getModified().contains(myConfigurable); } @Override boolean isError() { - return myContext.getErrors().containsKey(myConfigurable); + return myFilter.myContext.getErrors().containsKey(myConfigurable); } } @@ -757,7 +733,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl super.processMouseEvent(e); } - private class MyTreeUi extends BasicTreeUI { + private class MyTreeUi extends WideSelectionTreeUI { @Override public void toggleExpandState(final TreePath path) { @@ -810,7 +786,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl boolean myWasHoldingFilter; public MyBuilder(SimpleTreeStructure structure) { - super(OptionsTree.this.myTree, myContext.getFilter(), structure, new WeightBasedComparator(false)); + super(myTree, myFilter, structure, new WeightBasedComparator(false)); myTree.addTreeExpansionListener(new TreeExpansionListener() { public void treeExpanded(TreeExpansionEvent event) { invalidateExpansions(); @@ -835,7 +811,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl @Override public boolean isAutoExpandNode(final NodeDescriptor nodeDescriptor) { - return myContext.isHoldingFilter(); + return myFilter.myContext.isHoldingFilter(); } @Override @@ -846,21 +822,21 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl @Override protected ActionCallback refilterNow(Object preferredSelection, boolean adjustSelection) { final List<Object> toRestore = new ArrayList<Object>(); - if (myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { + if (myFilter.myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { myToExpandOnResetFilter = myBuilder.getUi().getExpandedElements(); - } else if (!myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { + } else if (!myFilter.myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { toRestore.addAll(myToExpandOnResetFilter); myToExpandOnResetFilter = null; } - myWasHoldingFilter = myContext.isHoldingFilter(); + myWasHoldingFilter = myFilter.myContext.isHoldingFilter(); ActionCallback result = super.refilterNow(preferredSelection, adjustSelection); myRefilteringNow = true; return result.doWhenDone(new Runnable() { public void run() { myRefilteringNow = false; - if (!myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { + if (!myFilter.myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { restoreExpandedState(toRestore); } } @@ -968,7 +944,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl public void paint(Graphics g) { super.paint(g); - if (Registry.is("ide.file.settings.order.new")) { + if (Registry.is("ide.new.settings.dialog")) { ConfigurableGroup group = getGroup(GroupSeparator.SPACE + mySeparator.getFont().getSize()); if (group != null && group == getGroup(-GroupSeparator.SPACE)) { mySeparator.configure(group, false); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.form b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.form deleted file mode 100644 index bfd8e1f0e416..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.form +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.options.newEditor.PreferencesDialog"> - <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> - <margin top="0" left="0" bottom="0" right="0"/> - <constraints> - <xy x="20" y="20" width="500" height="400"/> - </constraints> - <properties/> - <border type="none"/> - <children> - <grid id="81fb6" binding="myTopPanel" custom-create="true" layout-manager="BorderLayout" hgap="0" vgap="0"> - <constraints> - <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"> - <preferred-size width="-1" height="50"/> - </grid> - </constraints> - <properties/> - <border type="none"/> - <children/> - </grid> - <vspacer id="420b1"> - <constraints> - <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> - </constraints> - </vspacer> - <grid id="b87b5" binding="myCenterPanel" layout-manager="BorderLayout" hgap="0" vgap="0"> - <constraints> - <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> - </constraints> - <properties/> - <border type="none"/> - <children/> - </grid> - <hspacer id="e2eff"> - <constraints> - <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> - </constraints> - </hspacer> - </children> - </grid> -</form> diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.java deleted file mode 100644 index 8ed3527e090a..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2000-2013 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.options.newEditor; - -import com.intellij.icons.AllIcons; -import com.intellij.openapi.options.ConfigurableGroup; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.ui.Gray; -import com.intellij.ui.SearchTextField; -import com.intellij.ui.border.CustomLineBorder; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; -import java.awt.*; - -/** - * @author Konstantin Bulenkov - */ -public class PreferencesDialog extends DialogWrapper { - private JPanel myRoot; - private JPanel myTopPanel; - private JPanel myCenterPanel; - private SearchTextField mySearchTextField; - - public PreferencesDialog(@Nullable Project project, ConfigurableGroup[] groups) { - super(project); - init(); - ((JDialog)getPeer().getWindow()).setUndecorated(true); - if (SystemInfo.isMac) { - ((JComponent)((JDialog)getPeer().getWindow()).getContentPane()).setBorder(new EmptyBorder(0, 0, 0, 0)); - } - else { - ((JComponent)((JDialog)getPeer().getWindow()).getContentPane()).setBorder(new LineBorder(Gray._140, 1)); - } - - setTitle("Preferences"); - } - - @Nullable - @Override - protected JComponent createCenterPanel() { - final JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); - panel.setBorder(null); - panel.add(createApplicationSettings()); - panel.add(createProjectSettings()); - panel.add(createEditorSettings()); - panel.add(createOtherSettings()); - panel.setPreferredSize(new Dimension(700, 370)); - myCenterPanel.add(panel, BorderLayout.CENTER); - return myRoot; - } - - @Nullable - @Override - public JComponent getPreferredFocusedComponent() { - return mySearchTextField.getTextEditor(); - } - - private static JComponent createEditorSettings() { - final LabeledButtonsPanel panel = new LabeledButtonsPanel("Editor"); - panel.addButton(new PreferenceButton("Editor", AllIcons.Preferences.Editor)); - panel.addButton(new PreferenceButton("Code Style", AllIcons.Preferences.CodeStyle)); - panel.setBackground(Gray._229); - panel.setBorder(new CustomLineBorder(Gray._223, 0, 0, 1, 0)); - return panel; - } - - private static JComponent createProjectSettings() { - final LabeledButtonsPanel panel = new LabeledButtonsPanel("Project"); - panel.addButton(new PreferenceButton("Compiler", AllIcons.Preferences.Compiler)); - panel.addButton(new PreferenceButton("Version Control", AllIcons.Preferences.VersionControl)); - panel.addButton(new PreferenceButton("File Colors", AllIcons.Preferences.FileColors)); - panel.addButton(new PreferenceButton("Scopes", AllIcons.Preferences.Editor)); - panel.setBackground(Gray._236); - panel.setBorder(new CustomLineBorder(Gray._223, 0, 0, 1, 0)); - return panel; - } - - private static JComponent createApplicationSettings() { - final LabeledButtonsPanel panel = new LabeledButtonsPanel("IDE"); - panel.addButton(new PreferenceButton("Appearance", AllIcons.Preferences.Appearance)); - panel.addButton(new PreferenceButton("General", AllIcons.Preferences.General)); - panel.addButton(new PreferenceButton("Keymap", AllIcons.Preferences.Keymap)); - panel.addButton(new PreferenceButton("File Types", AllIcons.Preferences.FileTypes)); - panel.setBackground(Gray._229); - panel.setBorder(new CustomLineBorder(Gray._223, 0, 0, 1, 0)); - return panel; - } - - private static JComponent createOtherSettings() { - final LabeledButtonsPanel panel = new LabeledButtonsPanel("Other"); - panel.addButton(new PreferenceButton("Plugins", AllIcons.Preferences.Plugins)); - panel.addButton(new PreferenceButton("Updates", AllIcons.Preferences.Updates)); - panel.setBackground(Gray._236); - panel.setBorder(new CustomLineBorder(Gray._223, 0, 0, 1, 0)); - return panel; - } - - - @Nullable - @Override - protected JComponent createSouthPanel() { - if (SystemInfo.isMac) { - return null; - } - final JComponent panel = super.createSouthPanel(); - if (panel != null) { - panel.setBorder(new EmptyBorder(5, 5, 10, 20)); - } - return panel; - } - - private void createUIComponents() { - myTopPanel = new JPanel(new BorderLayout()) { - @Override - protected void paintComponent(Graphics g) { - ((Graphics2D)g).setPaint(new GradientPaint(0, 0, Gray._206, 0, getHeight() - 1, Gray._172)); - g.fillRect(0, 0, getWidth(), getHeight()); - g.setColor(Gray._145); - g.drawLine(0, getHeight() - 2, getWidth(), getHeight() - 2); - g.setColor(Gray._103); - g.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1); - } - }; - final JLabel title = new JLabel("Preferences"); - if (!SystemInfo.isMac) { - title.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD, 14)); - } - else { - title.setFont(new Font("Lucuda Grande", Font.PLAIN, 12)); - } - title.setHorizontalTextPosition(SwingConstants.CENTER); - title.setHorizontalAlignment(SwingConstants.CENTER); - title.setVerticalAlignment(SwingConstants.TOP); - myTopPanel.add(title, BorderLayout.NORTH); - mySearchTextField = new SearchTextField(); - mySearchTextField.setOpaque(false); - myTopPanel.add(mySearchTextField, BorderLayout.EAST); - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsFilter.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsFilter.java new file mode 100644 index 000000000000..1885a852c5da --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsFilter.java @@ -0,0 +1,211 @@ +/* + * Copyright 2000-2014 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.options.newEditor; + +import com.intellij.ide.ui.search.ConfigurableHit; +import com.intellij.ide.ui.search.SearchableOptionsRegistrar; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.LightColors; +import com.intellij.ui.SearchTextField; +import com.intellij.ui.speedSearch.ElementFilter; +import com.intellij.ui.treeStructure.SimpleNode; +import com.intellij.util.ui.UIUtil; + +import javax.swing.event.DocumentEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Set; + +abstract class SettingsFilter extends ElementFilter.Active.Impl<SimpleNode> { + final OptionsEditorContext myContext = new OptionsEditorContext(this); + final Project myProject; + + boolean myDocumentWasChanged; + + private final SearchTextField mySearch; + private final ConfigurableGroup[] myGroups; + + private SearchableOptionsRegistrar myRegistrar = SearchableOptionsRegistrar.getInstance(); + private Set<Configurable> myFiltered; + private ConfigurableHit myHits; + + private boolean myUpdateRejected; + private Configurable myLastSelected; + + SettingsFilter(Project project, ConfigurableGroup[] groups, SearchTextField search) { + myProject = project; + myGroups = groups; + mySearch = search; + mySearch.addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent event) { + update(event.getType(), true, false); + } + }); + mySearch.getTextEditor().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent event) { + if (!mySearch.getText().isEmpty()) { + if (!myContext.isHoldingFilter()) { + setHoldingFilter(true); + } + if (!mySearch.getTextEditor().isFocusOwner()) { + mySearch.selectText(); + } + } + } + }); + } + + abstract Configurable getConfigurable(SimpleNode node); + + abstract SimpleNode findNode(Configurable configurable); + + abstract void updateSpotlight(boolean now); + + @Override + public boolean shouldBeShowing(SimpleNode node) { + if (myFiltered != null) { + Configurable configurable = getConfigurable(node); + if (configurable != null) { + if (!myFiltered.contains(configurable)) { + if (myHits != null) { + Set<Configurable> configurables = myHits.getNameFullHits(); + while (node != null) { + if (configurable != null) { + if (configurables.contains(configurable)) { + return true; + } + } + node = node.getParent(); + configurable = getConfigurable(node); + } + } + return false; + } + } + } + return true; + } + + String getFilterText() { + String text = mySearch.getText(); + return text == null ? "" : text.trim(); + } + + void setHoldingFilter(boolean holding) { + myContext.setHoldingFilter(holding); + updateSpotlight(false); + } + + boolean contains(Configurable configurable) { + return myHits != null && myHits.getNameHits().contains(configurable); + } + + ActionCallback update(boolean adjustSelection, boolean now) { + return update(DocumentEvent.EventType.CHANGE, adjustSelection, now); + } + + ActionCallback update(String text, boolean adjustSelection, boolean now) { + try { + myUpdateRejected = true; + mySearch.setText(text); + } + finally { + myUpdateRejected = false; + } + return update(adjustSelection, now); + } + + private ActionCallback update(DocumentEvent.EventType type, boolean adjustSelection, boolean now) { + if (myUpdateRejected) { + return new ActionCallback.Rejected(); + } + String text = getFilterText(); + if (text.isEmpty()) { + myContext.setHoldingFilter(false); + myFiltered = null; + } + else { + myContext.setHoldingFilter(true); + myHits = myRegistrar.getConfigurables(myGroups, type, myFiltered, text, myProject); + myFiltered = myHits.getAll(); + } + mySearch.getTextEditor().setBackground(myFiltered != null && myFiltered.isEmpty() + ? LightColors.RED + : UIUtil.getTextFieldBackground()); + + + Configurable current = myContext.getCurrentConfigurable(); + + boolean shouldMoveSelection = myHits == null || ( + !myHits.getNameFullHits().contains(current) && + !myHits.getContentHits().contains(current)); + + if (shouldMoveSelection && type != DocumentEvent.EventType.INSERT && (myFiltered == null || myFiltered.contains(current))) { + shouldMoveSelection = false; + } + + Configurable candidate = adjustSelection ? current : null; + if (shouldMoveSelection && myHits != null) { + if (!myHits.getNameHits().isEmpty()) { + candidate = findConfigurable(myHits.getNameHits(), myHits.getNameFullHits()); + } + else if (!myHits.getContentHits().isEmpty()) { + candidate = findConfigurable(myHits.getContentHits(), null); + } + } + updateSpotlight(false); + + if ((myFiltered == null || !myFiltered.isEmpty()) && candidate == null && myLastSelected != null) { + candidate = myLastSelected; + myLastSelected = null; + } + if (candidate == null && current != null) { + myLastSelected = current; + } + SimpleNode node = !adjustSelection ? null : findNode(candidate); + ActionCallback callback = fireUpdate(node, adjustSelection, now); + myDocumentWasChanged = true; + return callback; + } + + private static Configurable findConfigurable(Set<Configurable> configurables, Set<Configurable> hits) { + Configurable candidate = null; + for (Configurable configurable : configurables) { + if (hits != null && hits.contains(configurable)) { + return configurable; + } + if (candidate == null && !isEmptyParent(configurable)) { + candidate = configurable; + } + } + return candidate; + } + + private static boolean isEmptyParent(Configurable configurable) { + if (configurable instanceof SearchableConfigurable.Parent) { + SearchableConfigurable.Parent parent = (SearchableConfigurable.Parent)configurable; + return !parent.hasOwnContent(); + } + return false; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java index 9269da7ab967..e10367d583bf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java @@ -16,7 +16,6 @@ package com.intellij.openapi.options.newEditor; import com.intellij.icons.AllIcons; -import com.intellij.ide.ui.search.ConfigurableHit; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.Disposable; import com.intellij.openapi.options.*; @@ -26,55 +25,68 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; import com.intellij.ui.*; -import com.intellij.ui.treeStructure.*; +import com.intellij.ui.treeStructure.CachingSimpleNode; +import com.intellij.ui.treeStructure.SimpleNode; +import com.intellij.ui.treeStructure.SimpleTree; +import com.intellij.ui.treeStructure.SimpleTreeStructure; import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure; import com.intellij.util.ArrayUtil; +import com.intellij.util.ui.ButtonlessScrollBarUI; import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; +import com.intellij.util.ui.tree.WideSelectionTreeUI; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.*; -import java.awt.event.*; -import java.util.*; -import java.util.List; import javax.swing.*; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.TreeUI; -import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.*; +import java.util.List; /** * @author Sergey.Malenkov */ final class SettingsTreeView extends JComponent implements Disposable, OptionsEditorColleague { + private static final Color NORMAL_NODE = new JBColor(Gray._60, Gray._140); + private static final Color WRONG_CONTENT = JBColor.RED; + private static final Color MODIFIED_CONTENT = JBColor.BLUE; + final SimpleTree myTree; final FilteringTreeBuilder myBuilder; - private final OptionsEditorContext myContext; + private final SettingsFilter myFilter; private final MyRoot myRoot; private final JScrollPane myScroller; private JLabel mySeparator; private final MyRenderer myRenderer = new MyRenderer(); private final IdentityHashMap<Configurable, MyNode> myConfigurableToNodeMap = new IdentityHashMap<Configurable, MyNode>(); - private final MergingUpdateQueue myQueue = new MergingUpdateQueue("OptionsTree", 150, false, this, this, this).setRestartTimerOnAdd(true); + private final MergingUpdateQueue myQueue = new MergingUpdateQueue("SettingsTreeView", 150, false, this, this, this) + .setRestartTimerOnAdd(true); private Configurable myQueuedConfigurable; - SettingsTreeView(final KeyListener listener, OptionsEditorContext context, ConfigurableGroup... groups) { - myContext = context; + SettingsTreeView(SettingsFilter filter, ConfigurableGroup... groups) { + myFilter = filter; myRoot = new MyRoot(groups); - myTree = new MyTree(); + myTree.putClientProperty(WideSelectionTreeUI.TREE_TABLE_TREE_KEY, Boolean.TRUE); + myTree.setBackground(UIUtil.getSidePanelColor()); myTree.getInputMap().clear(); TreeUtil.installActions(myTree); @@ -88,8 +100,11 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myTree.setRootVisible(false); myTree.setShowsRootHandles(false); - myScroller = ScrollPaneFactory.createScrollPane(myTree); - myScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + myScroller = ScrollPaneFactory.createScrollPane(myTree, true); + myScroller.getVerticalScrollBar().setUI(ButtonlessScrollBarUI.createTransparent()); + myScroller.setBackground(UIUtil.getSidePanelColor()); + myScroller.getViewport().setBackground(UIUtil.getSidePanelColor()); + myScroller.getVerticalScrollBar().setBackground(UIUtil.getSidePanelColor()); add(myScroller); myTree.addComponentListener(new ComponentAdapter() { @@ -116,29 +131,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } }); - myTree.addKeyListener(new KeyListener() { - public void keyTyped(KeyEvent event) { - if (listener != null && isValid(event)) { - listener.keyTyped(event); - } - } - - public void keyPressed(KeyEvent event) { - if (listener != null && isValid(event)) { - listener.keyPressed(event); - } - } - - public void keyReleased(KeyEvent event) { - if (listener != null && isValid(event)) { - listener.keyReleased(event); - } - } - - private boolean isValid(KeyEvent event) { - return null == myTree.getInputMap().get(KeyStroke.getKeyStrokeForEvent(event)); - } - }); myBuilder = new MyBuilder(new SimpleTreeStructure.Impl(myRoot)); myBuilder.setFilteringMerge(300, null); Disposer.register(this, myBuilder); @@ -158,9 +150,15 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd return ArrayUtil.toStringArray(path); } + static Configurable getConfigurable(SimpleNode node) { + return node instanceof MyNode + ? ((MyNode)node).myConfigurable + : null; + } + @Nullable - SimpleNode findNode(Configurable toSelect) { - return myConfigurableToNodeMap.get(toSelect); + SimpleNode findNode(Configurable configurable) { + return myConfigurableToNodeMap.get(configurable); } @Nullable @@ -250,24 +248,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd : null; } - static boolean isFiltered(Set<Configurable> configurables, ConfigurableHit hits, SimpleNode value) { - if (value instanceof MyNode && !configurables.contains(((MyNode)value).myConfigurable)) { - if (hits != null) { - configurables = hits.getNameFullHits(); - while (value != null) { - if (value instanceof MyNode) { - if (configurables.contains(((MyNode)value).myConfigurable)) { - return true; - } - } - value = value.getParent(); - } - } - return false; - } - return true; - } - @Override public void doLayout() { myScroller.setBounds(0, 0, getWidth(), getHeight()); @@ -277,34 +257,34 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd public void paint(Graphics g) { super.paint(g); + if (0 == myTree.getY()) { + return; // separator is not needed without scrolling + } if (mySeparator == null) { mySeparator = new JLabel(); + mySeparator.setForeground(NORMAL_NODE); mySeparator.setFont(UIUtil.getLabelFont()); mySeparator.setFont(getFont().deriveFont(Font.BOLD)); } - ConfigurableGroup group = findConfigurableGroupAt(0, 5 + mySeparator.getFont().getSize()); - if (group != null && group == findConfigurableGroupAt(0, -5)) { - int offset = UIUtil.isUnderNativeMacLookAndFeel() ? 1 : 3; - mySeparator.setBorder(BorderFactory.createEmptyBorder(offset, 18, offset, 3)); + int height = mySeparator.getPreferredSize().height; + ConfigurableGroup group = findConfigurableGroupAt(0, height); + if (group != null && group == findConfigurableGroupAt(0, -myRenderer.getSeparatorHeight())) { + mySeparator.setBorder(BorderFactory.createEmptyBorder(0, 18, 0, 0)); mySeparator.setText(group.getDisplayName()); Rectangle bounds = myScroller.getViewport().getBounds(); - int height = mySeparator.getPreferredSize().height; if (bounds.height > height) { bounds.height = height; } g.setColor(myTree.getBackground()); + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); if (g instanceof Graphics2D) { - int h = bounds.height / 4; - int y = bounds.y + bounds.height - h; - g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height - h); + int h = 4; // gradient height + int y = bounds.y + bounds.height; ((Graphics2D)g).setPaint(UIUtil.getGradientPaint( 0, y, g.getColor(), 0, y + h, ColorUtil.toAlpha(g.getColor(), 0))); - g.fillRect(bounds.x, y, bounds.width, h + h); - } - else { - g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + g.fillRect(bounds.x, y, bounds.width, h); } mySeparator.setSize(bounds.width - 1, bounds.height); mySeparator.paint(g.create(bounds.x + 1, bounds.y, bounds.width - 1, bounds.height)); @@ -373,7 +353,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } private void fireSelected(Configurable configurable, ActionCallback callback) { - myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); + myFilter.myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); } @Override @@ -435,20 +415,21 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myConfigurable = configurable; String name = configurable.getDisplayName(); myDisplayName = name != null ? name.replace("\n", " ") : "{ " + configurable.getClass().getSimpleName() + " }"; - - myConfigurableToNodeMap.put(configurable, this); } private MyNode(CachingSimpleNode parent, ConfigurableGroup group) { super(parent); myComposite = group; - myConfigurable = null; + myConfigurable = group instanceof Configurable ? (Configurable)group : null; String name = group.getDisplayName(); myDisplayName = name != null ? name.replace("\n", " ") : "{ " + group.getClass().getSimpleName() + " }"; } @Override protected SimpleNode[] buildChildren() { + if (myConfigurable != null) { + myConfigurableToNodeMap.put(myConfigurable, this); + } if (myComposite == null) { return NO_CHILDREN; } @@ -460,7 +441,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd for (int i = 0; i < configurables.length; i++) { result[i] = new MyNode(this, configurables[i]); if (myConfigurable != null) { - myContext.registerKid(myConfigurable, configurables[i]); + myFilter.myContext.registerKid(myConfigurable, configurables[i]); } } return result; @@ -470,11 +451,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd public boolean isAlwaysLeaf() { return myComposite == null; } - - @Override - public int getWeight() { - return WeightBasedComparator.UNDEFINED_WEIGHT; - } } private final class MyRenderer extends GroupedElementsRenderer.Tree { @@ -490,8 +466,9 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd protected void layout() { myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); myProjectIcon = new JLabel(" ", SwingConstants.LEFT); - myProjectIcon.setOpaque(true); - myRendererComponent.add(BorderLayout.NORTH, mySeparatorComponent); + myNodeIcon.setOpaque(false); + myTextLabel.setOpaque(false); + myProjectIcon.setOpaque(false); myRendererComponent.add(BorderLayout.CENTER, myComponent); myRendererComponent.add(BorderLayout.WEST, myNodeIcon); myRendererComponent.add(BorderLayout.EAST, myProjectIcon); @@ -504,22 +481,17 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd boolean leaf, int row, boolean focused) { - myTextLabel.setOpaque(selected); myTextLabel.setFont(UIUtil.getLabelFont()); - - String text; - boolean hasSeparatorAbove = false; - int preferredForcedWidth = -1; + myRendererComponent.setBackground(selected ? UIUtil.getTreeSelectionBackground() : myTree.getBackground()); MyNode node = extractNode(value); if (node == null) { - text = value.toString(); + myTextLabel.setText(value.toString()); } else { - text = node.myDisplayName; + myTextLabel.setText(node.myDisplayName); // show groups in bold if (myRoot == node.getParent()) { - hasSeparatorAbove = node != myRoot.getChildAt(0); myTextLabel.setFont(myTextLabel.getFont().deriveFont(Font.BOLD)); } TreePath path = tree.getPathForRow(row); @@ -543,18 +515,18 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd forcedWidth = visibleRect.width > 0 ? visibleRect.width - indent : forcedWidth; } - preferredForcedWidth = forcedWidth - 4; + myRendererComponent.setPrefereedWidth(forcedWidth - 4); } - Component result = configureComponent(text, null, null, null, selected, hasSeparatorAbove, null, preferredForcedWidth); // update font color for modified configurables + myTextLabel.setForeground(selected ? UIUtil.getTreeSelectionForeground() : NORMAL_NODE); if (!selected && node != null) { Configurable configurable = node.myConfigurable; if (configurable != null) { - if (myContext.getErrors().containsKey(configurable)) { - myTextLabel.setForeground(JBColor.RED); + if (myFilter.myContext.getErrors().containsKey(configurable)) { + myTextLabel.setForeground(WRONG_CONTENT); } - else if (myContext.getModified().contains(configurable)) { - myTextLabel.setForeground(JBColor.BLUE); + else if (myFilter.myContext.getModified().contains(configurable)) { + myTextLabel.setForeground(MODIFIED_CONTENT); } } } @@ -586,7 +558,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myProjectIcon.setToolTipText(OptionsBundle.message(project.isDefault() ? "configurable.default.project.tooltip" : "configurable.current.project.tooltip")); - myProjectIcon.setBackground(myTextLabel.getBackground()); myProjectIcon.setVisible(true); } else { @@ -601,9 +572,12 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd else { myNodeIcon.setIcon(null); } - return result; + return myRendererComponent; } + int getSeparatorHeight() { + return mySeparatorComponent.getParent() == null ? 0 : mySeparatorComponent.getPreferredSize().height; + } public boolean isUnderHandle(Point point) { Point handlePoint = SwingUtilities.convertPoint(myRendererComponent, point, myNodeIcon); @@ -720,7 +694,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd super.processMouseEvent(e); } - private final class MyTreeUi extends BasicTreeUI { + private final class MyTreeUi extends WideSelectionTreeUI { @Override public void toggleExpandState(TreePath path) { @@ -773,7 +747,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd boolean myWasHoldingFilter; public MyBuilder(SimpleTreeStructure structure) { - super(myTree, myContext.getFilter(), structure, new WeightBasedComparator(false)); + super(myTree, myFilter, structure, null); myTree.addTreeExpansionListener(new TreeExpansionListener() { public void treeExpanded(TreeExpansionEvent event) { invalidateExpansions(); @@ -798,7 +772,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd @Override public boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) { - return myContext.isHoldingFilter(); + return myFilter.myContext.isHoldingFilter(); } @Override @@ -809,22 +783,22 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd @Override protected ActionCallback refilterNow(Object preferredSelection, boolean adjustSelection) { final List<Object> toRestore = new ArrayList<Object>(); - if (myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { + if (myFilter.myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { myToExpandOnResetFilter = myBuilder.getUi().getExpandedElements(); } - else if (!myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { + else if (!myFilter.myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { toRestore.addAll(myToExpandOnResetFilter); myToExpandOnResetFilter = null; } - myWasHoldingFilter = myContext.isHoldingFilter(); + myWasHoldingFilter = myFilter.myContext.isHoldingFilter(); ActionCallback result = super.refilterNow(preferredSelection, adjustSelection); myRefilteringNow = true; return result.doWhenDone(new Runnable() { public void run() { myRefilteringNow = false; - if (!myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { + if (!myFilter.myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { restoreExpandedState(toRestore); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java index 6a37bb2fb69e..3d478f2b7084 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java @@ -47,6 +47,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class ProgressManagerImpl extends ProgressManager implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.impl.ProgressManagerImpl"); + public static final int CHECK_CANCELED_DELAY_MILLIS = 10; private final AtomicInteger myCurrentUnsafeProgressCount = new AtomicInteger(0); private final AtomicInteger myCurrentModalProgressCount = new AtomicInteger(0); @@ -64,7 +65,7 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable { public void run() { ourNeedToCheckCancel = true; } - }, 0, 10, TimeUnit.MILLISECONDS); + }, 0, CHECK_CANCELED_DELAY_MILLIS, TimeUnit.MILLISECONDS); } } @@ -83,7 +84,7 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable { ourLockedCheckCounter++; if (ourLockedCheckCounter > 10) { ourLockedCheckCounter = 0; - ourNeedToCheckCancel = true; + canceled(); } } else { @@ -94,10 +95,6 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable { } } - public static void canceled() { - ourNeedToCheckCancel = true; - } - private static class NonCancelableIndicator extends EmptyProgressIndicator implements NonCancelableSection { private final ProgressIndicator myOld; diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorBase.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorBase.java index 2d9a7b3a88e6..b4821f52fddb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -19,7 +19,6 @@ import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.TaskInfo; -import com.intellij.openapi.progress.impl.ProgressManagerImpl; import com.intellij.openapi.wm.ex.ProgressIndicatorEx; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.WeakList; @@ -151,9 +150,6 @@ public class ProgressIndicatorBase extends AbstractProgressIndicatorBase impleme @Override public void cancel() { super.cancel(); - - ProgressManagerImpl.canceled(); - delegateRunningChange(CANCEL_ACTION); } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java index 0c18cb7706d9..368200492cae 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java @@ -22,7 +22,11 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.ui.AppUIUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.ide.PooledThreadExecutor; + +import java.util.concurrent.Executor; /** * @author gregsh @@ -44,96 +48,76 @@ public class ProgressIndicatorUtils { return progress; } - public static void runWithWriteActionPriority(@NotNull final Runnable action) { - runWithWriteActionPriority(new ReadTask() { - @Override - public void computeInReadAction(@NotNull ProgressIndicator indicator) { - action.run(); - } - - @Override - public void onCanceled(@NotNull ProgressIndicator indicator) { - } - }); - } - - public static void runWithWriteActionPriority(@NotNull final ReadTask task) { - runWithWriteActionPriority(new ProgressIndicatorBase(), task); + public static void scheduleWithWriteActionPriority(@NotNull ReadTask task) { + scheduleWithWriteActionPriority(new ProgressIndicatorBase(), task); } - private static void surroundWithListener(@NotNull final ProgressIndicator progressIndicator, @NotNull Runnable runnable) { - final ApplicationAdapter listener = new ApplicationAdapter() { - @Override - public void beforeWriteActionStart(Object action) { - progressIndicator.cancel(); - } - }; - final Application application = ApplicationManager.getApplication(); - application.addApplicationListener(listener); - try { - runnable.run(); - } - finally { - application.removeApplicationListener(listener); - } + public static void scheduleWithWriteActionPriority(@NotNull ProgressIndicator progressIndicator, @NotNull ReadTask readTask) { + scheduleWithWriteActionPriority(progressIndicator, PooledThreadExecutor.INSTANCE, readTask); } - - public static void runWithWriteActionPriority(@NotNull final ProgressIndicator progressIndicator, @NotNull final ReadTask task) { - surroundWithListener(progressIndicator, new Runnable() { + public static void scheduleWithWriteActionPriority(@NotNull final ProgressIndicator progressIndicator, + @NotNull final Executor executor, + @NotNull final ReadTask readTask) { + AppUIUtil.invokeOnEdt(new Runnable() { @Override public void run() { - runUnderProgress(progressIndicator, task); + final Application application = ApplicationManager.getApplication(); + application.assertIsDispatchThread(); + final ApplicationAdapter listener = new ApplicationAdapter() { + @Override + public void beforeWriteActionStart(Object action) { + progressIndicator.cancel(); + } + }; + application.addApplicationListener(listener); + try { + executor.execute(new Runnable() { + @Override + public void run() { + try { + runUnderProgress(progressIndicator, readTask); + } + finally { + application.removeApplicationListener(listener); + } + } + }); + } + catch (RuntimeException e) { + application.removeApplicationListener(listener); + throw e; + } + catch (Error e) { + application.removeApplicationListener(listener); + throw e; + } } }); } private static void runUnderProgress(@NotNull final ProgressIndicator progressIndicator, @NotNull final ReadTask task) { ProgressManager.getInstance().runProcess(new Runnable() { - @Override - public void run() { - // This read action can possible last for a long time, we want it to stop immediately on the first write access. - // For this purpose we launch it under empty progress and invoke progressIndicator#cancel on write access to avoid possible write lock delays. - try { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - task.computeInReadAction(progressIndicator); - } - }); - } - catch (ProcessCanceledException ignore) { - } - finally { - if (progressIndicator.isCanceled()) { - task.onCanceled(progressIndicator); - } - } - } - }, progressIndicator); - } - - public static void scheduleWithWriteActionPriority(@NotNull final ReadTask task) { - scheduleWithWriteActionPriority(new ProgressIndicatorBase(), task); - } - - public static void scheduleWithWriteActionPriority(@NotNull final ProgressIndicator indicator, @NotNull final ReadTask task) { - // we have to attach listeners in EDT to avoid "fire write action started while attach listeners from another thread" race condition - ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - surroundWithListener(indicator, new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - runUnderProgress(indicator, task); - } - }); + // This read action can possible last for a long time, we want it to stop immediately on the first write access. + // For this purpose we launch it under empty progress and invoke progressIndicator#cancel on write access to avoid possible write lock delays. + try { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + task.computeInReadAction(progressIndicator); + } + }); + } + catch (ProcessCanceledException ignore) { + } + finally { + if (progressIndicator.isCanceled()) { + task.onCanceled(progressIndicator); } - }); + } } - }); + }, progressIndicator); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectMacrosUtil.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectMacrosUtil.java index 77baf2a6b98c..1f3b9d149ccc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectMacrosUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectMacrosUtil.java @@ -25,17 +25,13 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.PathMacros; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.options.ex.SingleConfigurableEditor; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.WaitForProgressToShow; import org.jetbrains.annotations.NonNls; -import javax.swing.*; -import java.lang.reflect.InvocationTargetException; import java.util.*; public class ProjectMacrosUtil { @@ -50,11 +46,7 @@ public class ProjectMacrosUtil { if (application.isHeadlessEnvironment() || application.isUnitTestMode()) { throw new RuntimeException(text + ": " + StringUtil.join(undefinedMacros, ", ")); } - final UndefinedMacrosConfigurable configurable = - new UndefinedMacrosConfigurable(text, undefinedMacros); - final SingleConfigurableEditor editor = new SingleConfigurableEditor(project, configurable); - editor.show(); - return editor.isOK(); + return ShowSettingsUtil.getInstance().editConfigurable(project, new UndefinedMacrosConfigurable(text, undefinedMacros)); } public static boolean checkNonIgnoredMacros(final Project project, final Set<String> usedMacros){ diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 2f95c21cb558..e7b20959369e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -21,6 +21,7 @@ import com.intellij.conversion.ConversionService; import com.intellij.ide.AppLifecycleListener; import com.intellij.ide.RecentProjectsManagerBase; import com.intellij.ide.impl.ProjectUtil; +import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.startup.impl.StartupManagerImpl; import com.intellij.notification.NotificationsManager; import com.intellij.openapi.Disposable; @@ -354,7 +355,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt myDefaultProjectRootElement = null; } catch (Throwable t) { - LOG.error(t); + PluginManager.processException(t); } } }); @@ -362,6 +363,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt return myDefaultProject; } + @Nullable public Element getDefaultProjectRootElement() { return myDefaultProjectRootElement; } @@ -1126,33 +1128,27 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt } @Override - public void writeExternal(Element parentNode) throws WriteExternalException { + public void writeExternal(Element parentNode) { if (myDefaultProject != null) { myDefaultProject.save(); } - if (myDefaultProjectRootElement == null) { //read external isn't called if config folder is absent - myDefaultProjectRootElement = new Element(ELEMENT_DEFAULT_PROJECT); + if (myDefaultProjectRootElement != null) { + myDefaultProjectRootElement.detach(); + parentNode.addContent(myDefaultProjectRootElement); } - - myDefaultProjectRootElement.detach(); - parentNode.addContent(myDefaultProjectRootElement); } - public void setDefaultProjectRootElement(final Element defaultProjectRootElement) { myDefaultProjectRootElement = defaultProjectRootElement; } @Override - public void readExternal(Element parentNode) throws InvalidDataException { + public void readExternal(Element parentNode) { myDefaultProjectRootElement = parentNode.getChild(ELEMENT_DEFAULT_PROJECT); - - if (myDefaultProjectRootElement == null) { - myDefaultProjectRootElement = new Element(ELEMENT_DEFAULT_PROJECT); + if (myDefaultProjectRootElement != null) { + myDefaultProjectRootElement.detach(); } - - myDefaultProjectRootElement.detach(); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginAdvertiserEditorNotificationProvider.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginAdvertiserEditorNotificationProvider.java index 394dec20f4ef..1160c64bbeb8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginAdvertiserEditorNotificationProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginAdvertiserEditorNotificationProvider.java @@ -15,7 +15,8 @@ */ package com.intellij.openapi.updateSettings.impl.pluginsAdvertisement; -import com.intellij.ide.plugins.*; +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.RepositoryHelper; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileTypes.FileTypeFactory; @@ -24,7 +25,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.updateSettings.impl.*; +import com.intellij.openapi.updateSettings.impl.PluginDownloader; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.EditorNotificationPanel; @@ -94,7 +95,8 @@ public class PluginAdvertiserEditorNotificationProvider extends EditorNotificati @Nullable private EditorNotificationPanel createPanel(final String extension, final Set<PluginsAdvertiser.Plugin> plugins) { final EditorNotificationPanel panel = new EditorNotificationPanel(); - panel.setText("Plugins supporting files with " + extension + " are found"); + + panel.setText("Plugins supporting " + extension + " files are found"); final IdeaPluginDescriptor disabledPlugin = PluginsAdvertiser.getDisabledPlugin(plugins); if (disabledPlugin != null) { panel.createActionLabel("Enable " + disabledPlugin.getName() + " plugin", new Runnable() { @@ -143,6 +145,7 @@ public class PluginAdvertiserEditorNotificationProvider extends EditorNotificati if (PropertiesComponent.getInstance().isTrueValue(PluginsAdvertiser.IGNORE_ULTIMATE_EDITION)) { return null; } + panel.setText(extension + " files are supported by " + PluginsAdvertiser.IDEA_ULTIMATE_EDITION); panel.createActionLabel(PluginsAdvertiser.CHECK_ULTIMATE_EDITION_TITLE, new Runnable() { @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginsAdvertiser.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginsAdvertiser.java index ed6a629bff0d..4ae3caee88e1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginsAdvertiser.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginsAdvertiser.java @@ -63,8 +63,9 @@ public class PluginsAdvertiser implements StartupActivity { private static final String FEATURE_IMPLEMENTATIONS_URL = "http://plugins.jetbrains.com/feature/getImplementations?"; private static final String CASHED_EXTENSIONS = "extensions.xml"; + public static final String IDEA_ULTIMATE_EDITION = "IntelliJ IDEA Ultimate Edition"; public static final String ULTIMATE_EDITION_SUGGESTION = "Do not suggest Ultimate Edition"; - public static final String CHECK_ULTIMATE_EDITION_TITLE = "Check IntelliJ IDEA Ultimate Edition"; + public static final String CHECK_ULTIMATE_EDITION_TITLE = "Check " + IDEA_ULTIMATE_EDITION; public static final String DISPLAY_ID = "Plugins Suggestion"; public static final NotificationGroup NOTIFICATION_GROUP = new NotificationGroup(DISPLAY_ID, NotificationDisplayType.STICKY_BALLOON, true); @@ -198,7 +199,7 @@ public class PluginsAdvertiser implements StartupActivity { } public static void openDownloadPage() { - BrowserUtil.open(ApplicationInfo.getInstance().getCompanyURL()); + BrowserUtil.browse(ApplicationInfo.getInstance().getCompanyURL()); } static void enablePlugins(Project project, final Collection<IdeaPluginDescriptor> disabledPlugins) { @@ -325,7 +326,8 @@ public class PluginsAdvertiser implements StartupActivity { message += "<a href=\"ignore\">Ignore All</a>"; } else if (myBundledPlugin != null && !PropertiesComponent.getInstance().isTrueValue(IGNORE_ULTIMATE_EDITION)) { - message = "Features covered by IntelliJ IDEA Ultimate Edition (" + StringUtil.join(myBundledPlugin, ", ") + ") are detected.<br>" + + message = "Features covered by " + IDEA_ULTIMATE_EDITION + + " (" + StringUtil.join(myBundledPlugin, ", ") + ") are detected.<br>" + "<a href=\"open\">" + CHECK_ULTIMATE_EDITION_TITLE + "</a><br>" + "<a href=\"ignoreUltimate\">" + ULTIMATE_EDITION_SUGGESTION + "</a>"; } diff --git a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/issueLinks/LinkMouseListenerBase.java b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/issueLinks/LinkMouseListenerBase.java index 402d88cef38a..92eed9582fd2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/issueLinks/LinkMouseListenerBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/issueLinks/LinkMouseListenerBase.java @@ -27,18 +27,24 @@ import java.awt.event.MouseMotionListener; public abstract class LinkMouseListenerBase<T> extends ClickListener implements MouseMotionListener { public static void installSingleTagOn(@NotNull SimpleColoredComponent component) { - new LinkMouseListenerBase<Consumer<MouseEvent>>() { + new LinkMouseListenerBase<Object>() { @Nullable @Override - protected Consumer<MouseEvent> getTagAt(@NotNull MouseEvent e) { + protected Object getTagAt(@NotNull MouseEvent e) { //noinspection unchecked - return (Consumer<MouseEvent>)((SimpleColoredComponent)e.getSource()).getFragmentTagAt(e.getX()); + return ((SimpleColoredComponent)e.getSource()).getFragmentTagAt(e.getX()); } @Override - protected void handleTagClick(@Nullable Consumer<MouseEvent> tag, @NotNull MouseEvent event) { + protected void handleTagClick(@Nullable Object tag, @NotNull MouseEvent event) { if (tag != null) { - tag.consume(event); + if (tag instanceof Consumer) { + //noinspection unchecked + ((Consumer<MouseEvent>)tag).consume(event); + } + else { + ((Runnable)tag).run(); + } } } }.installOn(component); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java index 1c6e173b45d1..33ecf625c05d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java @@ -136,7 +136,8 @@ public class TempFileSystem extends LocalFileSystemBase { } fsItem.getParent().removeChild(fsItem); - ((FSDir)newParentItem).addChild(fsItem); + newDir.addChild(fsItem); + fsItem.myParent = newDir; } @Override @@ -263,7 +264,7 @@ public class TempFileSystem extends LocalFileSystemBase { } private abstract static class FSItem { - private final FSDir myParent; + private FSDir myParent; private String myName; private long myTimestamp; private boolean myWritable; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java index d7b90178e7e8..db5de4258d41 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java @@ -67,15 +67,16 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone private final Object myInputLock = new Object(); private final AtomicBoolean myShutDown = new AtomicBoolean(false); + @SuppressWarnings("FieldCanBeLocal") + private final LowMemoryWatcher myWatcher = LowMemoryWatcher.register(new Runnable() { + @Override + public void run() { + clearIdCache(); + } + }); public PersistentFSImpl(@NotNull MessageBus bus) { myEventBus = bus; - LowMemoryWatcher.register(new Runnable() { - @Override - public void run() { - clearIdCache(); - } - }); ShutDownTracker.getInstance().registerShutdownTask(new Runnable() { @Override public void run() { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PresentationModeProgressPanel.form b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PresentationModeProgressPanel.form index 2e1bb768a614..4e362a30a1ed 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PresentationModeProgressPanel.form +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PresentationModeProgressPanel.form @@ -22,7 +22,7 @@ </constraints> <properties> <autoscrolls value="true"/> - <text value="Text"/> + <text value=""/> </properties> </component> <component id="94f42" class="javax.swing.JProgressBar" binding="myProgressBar" default-binding="true"> @@ -54,7 +54,7 @@ </grid> </constraints> <properties> - <text value="Text 2"/> + <text value=""/> </properties> </component> <vspacer id="8e674"> diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PresentationModeProgressPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PresentationModeProgressPanel.java index d7bc7024f0ed..9f82a2f60cdf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PresentationModeProgressPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PresentationModeProgressPanel.java @@ -22,6 +22,7 @@ import com.intellij.ui.InplaceButton; import com.intellij.ui.TransparentPanel; import com.intellij.util.ui.EmptyIcon; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -69,7 +70,7 @@ public class PresentationModeProgressPanel { } if (!myProgressBar.isIndeterminate()) { - myProgressBar.setValue(((int)(myProgress.getFraction() * 99)) + 1); + myProgressBar.setValue((int)(myProgress.getFraction() * 99) + 1); } } @@ -83,7 +84,7 @@ public class PresentationModeProgressPanel { AllIcons.Process.Stop, AllIcons.Process.StopHovered); myCancelButton = new InplaceButton(iconButton, new ActionListener() { - public void actionPerformed(final ActionEvent e) { + public void actionPerformed(@NotNull final ActionEvent e) { myProgress.cancel(); } }).setFillBg(false); diff --git a/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java b/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java index 1fc23e5f4055..d9ebeefa7acc 100644 --- a/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java +++ b/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java @@ -368,6 +368,7 @@ public abstract class AbstractExpandableItemsHandler<KeyType, ComponentType exte if (!(renderer instanceof JComponent)) return null; myKeyItemBounds = rendererAndBounds.second; + myKeyItemBounds.width = Math.min(myKeyItemBounds.width, myComponent.getToolkit().getScreenSize().width); Rectangle cellBounds = myKeyItemBounds; Rectangle visibleRect = getVisibleRect(key); diff --git a/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java b/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java index 2a7d44d017bd..f4bf768e92a6 100644 --- a/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java +++ b/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java @@ -90,19 +90,19 @@ public class AppUIUtil { invokeOnEdt(runnable, null); } - public static void invokeOnEdt(Runnable runnable, @Nullable Condition condition) { + public static void invokeOnEdt(Runnable runnable, @Nullable Condition expired) { Application application = ApplicationManager.getApplication(); if (application.isDispatchThread()) { //noinspection unchecked - if (condition == null || !condition.value(null)) { + if (expired == null || !expired.value(null)) { runnable.run(); } } - else if (condition == null) { + else if (expired == null) { application.invokeLater(runnable); } else { - application.invokeLater(runnable, condition); + application.invokeLater(runnable, expired); } } diff --git a/platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java b/platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java index f4dfb1059651..60371365f2c3 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java @@ -17,6 +17,7 @@ package com.intellij.ui; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NonNls; @@ -34,10 +35,19 @@ public class EditorComboBoxEditor implements ComboBoxEditor{ @NonNls protected static final String NAME = "ComboBox.textField"; public EditorComboBoxEditor(Project project, FileType fileType) { - myTextField = new ComboboxEditorTextField((Document)null, project, fileType); + myTextField = new ComboboxEditorTextField((Document)null, project, fileType) { + @Override + protected EditorEx createEditor() { + EditorEx editor = super.createEditor(); + onEditorCreate(editor); + return editor; + } + }; myTextField.setName(NAME); } + protected void onEditorCreate(EditorEx editor) {} + @Override public void selectAll() { myTextField.selectAll(); diff --git a/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java b/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java index 5721f4af4821..4b788c80ae54 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java @@ -102,13 +102,7 @@ public class EditorNotificationsImpl extends EditorNotifications { task.computeInReadAction(indicator); } else { - final ProgressIndicator indicator1 = indicator; - myExecutor.execute(new Runnable() { - @Override - public void run() { - ProgressIndicatorUtils.runWithWriteActionPriority(indicator1, task); - } - }); + ProgressIndicatorUtils.scheduleWithWriteActionPriority(indicator, myExecutor, task); } } }); @@ -165,7 +159,7 @@ public class EditorNotificationsImpl extends EditorNotifications { } @Override - public void onCanceled(@NotNull ProgressIndicator _) { + public void onCanceled(@NotNull ProgressIndicator ignored) { UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { diff --git a/platform/platform-impl/src/com/intellij/ui/EditorTextField.java b/platform/platform-impl/src/com/intellij/ui/EditorTextField.java index c16f5f188cdc..27b1a2be56ae 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorTextField.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorTextField.java @@ -278,13 +278,18 @@ public class EditorTextField extends NonOpaquePanel implements DocumentListener, public void selectAll() { if (myEditor != null) { - myEditor.getSelectionModel().setSelection(0, myDocument.getTextLength()); + doSelectAll(myEditor); } else { myWholeTextSelected = true; } } + private static void doSelectAll(@NotNull Editor editor) { + editor.getCaretModel().removeSecondaryCarets(); + editor.getCaretModel().getPrimaryCaret().setSelection(0, editor.getDocument().getTextLength(), false); + } + public void removeSelection() { if (myEditor != null) { myEditor.getSelectionModel().removeSelection(); @@ -514,7 +519,8 @@ public class EditorTextField extends NonOpaquePanel implements DocumentListener, editor.getSelectionModel().removeSelection(); } else if (myWholeTextSelected) { - editor.getSelectionModel().setSelection(0, myDocument.getTextLength()); + doSelectAll(editor); + myWholeTextSelected = false; } editor.putUserData(SUPPLEMENTARY_KEY, myIsSupplementary); diff --git a/platform/platform-impl/src/com/intellij/ui/FocusTrackback.java b/platform/platform-impl/src/com/intellij/ui/FocusTrackback.java index 9ad0931c85b9..5c531a7b6ae8 100644 --- a/platform/platform-impl/src/com/intellij/ui/FocusTrackback.java +++ b/platform/platform-impl/src/com/intellij/ui/FocusTrackback.java @@ -191,10 +191,12 @@ public class FocusTrackback { if (app == null || wrongOS() || myConsumed || isSheduledForRestore()) return; Project project = null; - DataContext context = - myParentWindow == null ? DataManager.getInstance().getDataContext() : DataManager.getInstance().getDataContext(myParentWindow); - if (context != null) { - project = CommonDataKeys.PROJECT.getData(context); + DataManager dataManager = DataManager.getInstance(); + if (dataManager != null) { + DataContext context = myParentWindow == null ? dataManager.getDataContext() : dataManager.getDataContext(myParentWindow); + if (context != null) { + project = CommonDataKeys.PROJECT.getData(context); + } } mySheduledForRestore = true; diff --git a/platform/platform-impl/src/com/intellij/ui/LibNotifyWrapper.java b/platform/platform-impl/src/com/intellij/ui/LibNotifyWrapper.java index e8c6f49f8088..7e613982a406 100644 --- a/platform/platform-impl/src/com/intellij/ui/LibNotifyWrapper.java +++ b/platform/platform-impl/src/com/intellij/ui/LibNotifyWrapper.java @@ -50,6 +50,8 @@ class LibNotifyWrapper implements SystemNotificationsImpl.Notifier { private final LibNotify myLibNotify; private final String myIcon; + private final Object myLock = new Object(); + private boolean myDisposed = false; private LibNotifyWrapper() { myLibNotify = (LibNotify)Native.loadLibrary("libnotify.so.4", LibNotify.class); @@ -66,14 +68,21 @@ class LibNotifyWrapper implements SystemNotificationsImpl.Notifier { connection.subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() { @Override public void appClosing() { - myLibNotify.notify_uninit(); + synchronized (myLock) { + myDisposed = true; + myLibNotify.notify_uninit(); + } } }); } @Override public void notify(@NotNull Set<String> allNames, @NotNull String name, @NotNull String title, @NotNull String description) { - Pointer notification = myLibNotify.notify_notification_new(title, description, myIcon); - myLibNotify.notify_notification_show(notification, null); + synchronized (myLock) { + if (!myDisposed) { + Pointer notification = myLibNotify.notify_notification_new(title, description, myIcon); + myLibNotify.notify_notification_show(notification, null); + } + } } } diff --git a/platform/platform-impl/src/com/intellij/ui/SpeedSearchBase.java b/platform/platform-impl/src/com/intellij/ui/SpeedSearchBase.java index cbcde2d36418..76985ad77362 100644 --- a/platform/platform-impl/src/com/intellij/ui/SpeedSearchBase.java +++ b/platform/platform-impl/src/com/intellij/ui/SpeedSearchBase.java @@ -298,6 +298,7 @@ public abstract class SpeedSearchBase<Comp extends JComponent> extends SpeedSear protected void processKeyEvent(KeyEvent e) { if (e.isAltDown()) return; + if (e.isShiftDown() && isNavigationKey(e.getKeyCode())) return; if (mySearchPopup != null) { mySearchPopup.processKeyEvent(e); return; @@ -499,6 +500,15 @@ public abstract class SpeedSearchBase<Comp extends JComponent> extends SpeedSear return keyCode == KeyEvent.VK_HOME || keyCode == KeyEvent.VK_END || keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN; } + private static boolean isPgUpPgDown(int keyCode) { + return keyCode == KeyEvent.VK_PAGE_UP || keyCode == KeyEvent.VK_PAGE_DOWN; + } + + private static boolean isNavigationKey(int keyCode) { + return isPgUpPgDown(keyCode) || isUpDownHomeEnd(keyCode); + } + + private void manageSearchPopup(@Nullable SearchPopup searchPopup) { Project project = null; if (ApplicationManager.getApplication() != null && !ApplicationManager.getApplication().isDisposed()) { diff --git a/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java b/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java index 2d76d6391ee8..bf0b37116941 100755 --- a/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java @@ -70,6 +70,8 @@ public class SheetController { private static int GAP_BETWEEN_BUTTONS = 5; + private static String SPACE_OR_LINE_SEPARATOR_PATTERN = "[\\s" + System.getProperty("line.separator") + "]+"; + // SHEET public int SHEET_WIDTH = 400; @@ -289,7 +291,7 @@ public class SheetController { int widestWordWidth = 250; - String [] words = (message == null) ? ArrayUtil.EMPTY_STRING_ARRAY : message.split(" "); + String [] words = (message == null) ? ArrayUtil.EMPTY_STRING_ARRAY : message.split(SPACE_OR_LINE_SEPARATOR_PATTERN); for (String word : words) { widestWordWidth = Math.max(fontMetrics.stringWidth(word), widestWordWidth); diff --git a/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java b/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java index 446722c081e4..fee84330f0a2 100755 --- a/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java @@ -70,6 +70,8 @@ public class SheetMessage { final Component recentFocusOwner = activeWindow == null ? null : activeWindow.getMostRecentFocusOwner(); beforeShowFocusOwner = new WeakReference<Component>(recentFocusOwner); + maximizeIfNeeded(owner); + myWindow = new JDialog(owner, "This should not be shown", Dialog.ModalityType.APPLICATION_MODAL); myWindow.getRootPane().putClientProperty("apple.awt.draggableWindowBackground", Boolean.FALSE); @@ -132,6 +134,16 @@ public class SheetMessage { } + private static void maximizeIfNeeded(final Window owner) { + if (owner == null) return; + if (owner instanceof Frame) { + Frame f = (Frame)owner; + if (f.getState() == Frame.ICONIFIED) { + f.setState(Frame.NORMAL); + } + } + } + private void setWindowOpacity(float opacity) { try { Method setOpacityMethod = myWindow.getClass().getMethod("setOpacity", Float.TYPE); diff --git a/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java index 04c38a6cc295..1b9190f6a740 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java @@ -177,15 +177,19 @@ public abstract class WizardPopup extends AbstractPopup implements ActionListene LOG.assertTrue (!isDisposed()); Rectangle targetBounds = new Rectangle(new Point(aScreenX, aScreenY), getContent().getPreferredSize()); - ScreenUtil.moveRectangleToFitTheScreen(targetBounds); if (getParent() != null) { final Rectangle parentBounds = getParent().getBounds(); parentBounds.x += STEP_X_PADDING; parentBounds.width -= STEP_X_PADDING * 2; + ScreenUtil.moveToFit(targetBounds, ScreenUtil.getScreenRectangle( + parentBounds.x + parentBounds.width / 2, + parentBounds.y + parentBounds.height / 2), null); if (parentBounds.intersects(targetBounds)) { targetBounds.x = getParent().getBounds().x - targetBounds.width - STEP_X_PADDING; } + } else { + ScreenUtil.moveToFit(targetBounds, ScreenUtil.getScreenRectangle(aScreenX, aScreenY), null); } if (getParent() == null) { diff --git a/platform/platform-impl/src/net/sf/cglib/proxy/AdvancedEnhancer.java b/platform/platform-impl/src/net/sf/cglib/proxy/AdvancedEnhancer.java index 5b22e706508a..a2f21c3d0018 100644 --- a/platform/platform-impl/src/net/sf/cglib/proxy/AdvancedEnhancer.java +++ b/platform/platform-impl/src/net/sf/cglib/proxy/AdvancedEnhancer.java @@ -15,7 +15,7 @@ */ package net.sf.cglib.proxy; -import com.intellij.ide.plugins.PluginManager; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.plugins.cl.PluginClassLoader; import com.intellij.util.ReflectionUtil; import net.sf.cglib.core.*; @@ -26,6 +26,7 @@ import org.objectweb.asm.Type; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.*; /** @@ -61,6 +62,7 @@ import java.util.*; * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class. */ +@SuppressWarnings("StaticFieldReferencedViaSubclass") public class AdvancedEnhancer extends AbstractClassGenerator { private static final CallbackFilter ALL_ZERO = new CallbackFilter(){ @@ -125,7 +127,7 @@ public class AdvancedEnhancer extends AbstractClassGenerator /** Internal interface, only public due to ClassLoader issues. */ public interface EnhancerKey { - public Object newInstance(String type, + Object newInstance(String type, String[] interfaces, CallbackFilter filter, Type[] callbackTypes, @@ -250,18 +252,6 @@ public class AdvancedEnhancer extends AbstractClassGenerator } /** - * Set the single type of {@link Callback} to use. - * This may be used instead of {@link #setCallback} when calling - * {@link #createClass}, since it may not be possible to have - * an array of actual callback instances. - * @param callbackType the type of callback to use for all methods - * @see #setCallbackTypes - */ - public void setCallbackType(Class callbackType) { - setCallbackTypes(new Class[]{ callbackType }); - } - - /** * Set the array of callback types to use. * This may be used instead of {@link #setCallbacks} when calling * {@link #createClass}, since it may not be possible to have @@ -315,27 +305,6 @@ public class AdvancedEnhancer extends AbstractClassGenerator } } - /** - * Generate a new class if necessary and return it without creating a new instance. - * This ignores any callbacks that have been set. - * To create a new instance you will have to use reflection, and methods - * called during the constructor will not be intercepted. To avoid this problem, - * use the multi-arg <code>create</code> method. - * @see #create(Class[], Object[]) - */ - public Class createClass() { - classOnly = true; - return (Class)createHelper(); - } - - /** - * Insert a static serialVersionUID field into the generated class. - * @param sUID the field value, or null to avoid generating field. - */ - public void setSerialVersionUID(Long sUID) { - serialVersionUID = sUID; - } - private void validate() { if (classOnly ^ (callbacks == null)) { if (classOnly) { @@ -401,7 +370,7 @@ public class AdvancedEnhancer extends AbstractClassGenerator for (final Class anInterface : interfaces) { final ClassLoader loader = anInterface.getClassLoader(); if (loader instanceof PluginClassLoader) { - final int order = PluginManager.getPluginLoadingOrder(((PluginClassLoader)loader).getPluginId()); + final int order = PluginManagerCore.getPluginLoadingOrder(((PluginClassLoader)loader).getPluginId()); if (maxIndex < order) { maxIndex = order; bestLoader = loader; @@ -413,7 +382,7 @@ public class AdvancedEnhancer extends AbstractClassGenerator if (superclass != null) { superLoader = superclass.getClassLoader(); if (superLoader instanceof PluginClassLoader && - maxIndex < PluginManager.getPluginLoadingOrder(((PluginClassLoader)superLoader).getPluginId())) { + maxIndex < PluginManagerCore.getPluginLoadingOrder(((PluginClassLoader)superLoader).getPluginId())) { return superLoader; } } @@ -426,24 +395,6 @@ public class AdvancedEnhancer extends AbstractClassGenerator sig.getDescriptor()); } - /** - * Finds all of the methods that will be extended by an - * Enhancer-generated class using the specified superclass and - * interfaces. This can be useful in building a list of Callback - * objects. The methods are added to the end of the given list. Due - * to the subclassing nature of the classes generated by Enhancer, - * the methods are guaranteed to be non-static, non-final, and - * non-private. Each method signature will only occur once, even if - * it occurs in multiple classes. - * @param superclass the class that will be extended, or null - * @param interfaces the list of interfaces that will be implemented, or null - * @param methods the list into which to copy the applicable methods - */ - public static void getMethods(Class superclass, Class[] interfaces, List<Method> methods) - { - getMethods(superclass, interfaces, methods, null, null); - } - private static void getMethods(Class superclass, Class[] interfaces, List<Method> methods, List<Method> interfaceMethods, Set forcePublic) { ReflectUtils.addAllMethods(superclass, methods); @@ -520,6 +471,9 @@ public class AdvancedEnhancer extends AbstractClassGenerator } final Map<Method, MethodInfo> methodInfoMap = new HashMap<Method, MethodInfo>(); for (Method method : actualMethods) { + if (isJdk8DefaultMethod(method)) { + continue; + } int modifiers = Constants.ACC_FINAL | (method.getModifiers() & ~Constants.ACC_ABSTRACT & ~Constants.ACC_NATIVE & ~Constants.ACC_SYNCHRONIZED); if (forcePublic.contains(MethodWrapper.create(method))) { @@ -551,6 +505,11 @@ public class AdvancedEnhancer extends AbstractClassGenerator e.end_class(); } + private static boolean isJdk8DefaultMethod(Method method) { + return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == + Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); + } + private static void removeAllCovariantMethods(final List<Method> actualMethods, final Method method, final Map<Method, Method> covariantMethods) { if ((method.getModifiers() & Constants.ACC_SYNTHETIC) != 0) { return; @@ -618,57 +577,6 @@ public class AdvancedEnhancer extends AbstractClassGenerator } } - /** - * Call this method to register the {@link Callback} array to use before - * creating a new instance of the generated class via reflection. If you are using - * an instance of <code>Enhancer</code> or the {@link Factory} interface to create - * new instances, this method is unnecessary. Its primary use is for when you want to - * cache and reuse a generated class yourself, and the generated class does - * <i>not</i> implement the {@link Factory} interface. - * <p> - * Note that this method only registers the callbacks on the current thread. - * If you want to register callbacks for instances created by multiple threads, - * use {@link #registerStaticCallbacks}. - * <p> - * The registered callbacks are overwritten and subsequently cleared - * when calling any of the <code>create</code> methods (such as - * {@link #create}). - * @param generatedClass a class previously created by {@link Enhancer} - * @param callbacks the array of callbacks to use when instances of the generated - * class are created - * @see #setUseFactory - */ - public static void registerCallbacks(Class generatedClass, Callback[] callbacks) { - setThreadCallbacks(generatedClass, callbacks); - } - - /** - * Similar to {@link #registerCallbacks}, but suitable for use - * when multiple threads will be creating instances of the generated class. - * The thread-level callbacks will always override the static callbacks. - * Static callbacks are never cleared. - * @param generatedClass a class previously created by {@link Enhancer} - * @param callbacks the array of callbacks to use when instances of the generated - * class are created - */ - public static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks) { - setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME); - } - - /** - * Determine if a class was generated using <code>Enhancer</code>. - * @param type any class - * @return whether the class was generated using <code>Enhancer</code> - */ - public static boolean isEnhanced(Class type) { - try { - getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME); - return true; - } catch (NoSuchMethodException e) { - return false; - } - } - private static void setThreadCallbacks(Class type, Callback[] callbacks) { setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME); } @@ -710,54 +618,6 @@ public class AdvancedEnhancer extends AbstractClassGenerator } } - /** - * Helper method to create an intercepted object. - * For finer control over the generated instance, use a new instance of <code>Enhancer</code> - * instead of this static method. - * @param type class to extend or interface to implement - * @param callback the callback to use for all methods - */ - public static Object create(Class type, Callback callback) { - Enhancer e = new Enhancer(); - e.setSuperclass(type); - e.setCallback(callback); - return e.create(); - } - - /** - * Helper method to create an intercepted object. - * For finer control over the generated instance, use a new instance of <code>Enhancer</code> - * instead of this static method. - * @param type class to extend or interface to implement - * @param interfaces array of interfaces to implement, or null - * @param callback the callback to use for all methods - */ - public static Object create(Class superclass, Class interfaces[], Callback callback) { - Enhancer e = new Enhancer(); - e.setSuperclass(superclass); - e.setInterfaces(interfaces); - e.setCallback(callback); - return e.create(); - } - - /** - * Helper method to create an intercepted object. - * For finer control over the generated instance, use a new instance of <code>Enhancer</code> - * instead of this static method. - * @param type class to extend or interface to implement - * @param interfaces array of interfaces to implement, or null - * @param filter the callback filter to use when generating a new class - * @param callbacks callback implementations to use for the enhanced object - */ - public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) { - Enhancer e = new Enhancer(); - e.setSuperclass(superclass); - e.setInterfaces(interfaces); - e.setCallbackFilter(filter); - e.setCallbacks(callbacks); - return e.create(); - } - private void emitConstructors(ClassEmitter ce, List constructors) { boolean seenNull = false; for (final Object constructor1 : constructors) { @@ -791,7 +651,7 @@ public class AdvancedEnhancer extends AbstractClassGenerator return keys; } - private void emitGetCallback(ClassEmitter ce, int[] keys) { + private static void emitGetCallback(ClassEmitter ce, int[] keys) { final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null); e.load_this(); e.invoke_static_this(BIND_CALLBACKS); @@ -866,14 +726,14 @@ public class AdvancedEnhancer extends AbstractClassGenerator e.end_method(); } - private void emitNewInstanceCallbacks(ClassEmitter ce) { + private static void emitNewInstanceCallbacks(ClassEmitter ce) { CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); e.load_arg(0); e.invoke_static_this(SET_THREAD_CALLBACKS); emitCommonNewInstance(e); } - private void emitCommonNewInstance(CodeEmitter e) { + private static void emitCommonNewInstance(CodeEmitter e) { e.new_instance_this(); e.dup(); e.invoke_constructor_this(); @@ -905,7 +765,7 @@ public class AdvancedEnhancer extends AbstractClassGenerator emitCommonNewInstance(e); } - private void emitNewInstanceMultiarg(ClassEmitter ce, List constructors) { + private static void emitNewInstanceMultiarg(ClassEmitter ce, List constructors) { final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null); e.load_arg(2); e.invoke_static_this(SET_THREAD_CALLBACKS); @@ -1034,7 +894,7 @@ public class AdvancedEnhancer extends AbstractClassGenerator se.end_method(); } - private void emitSetThreadCallbacks(ClassEmitter ce) { + private static void emitSetThreadCallbacks(ClassEmitter ce) { CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, SET_THREAD_CALLBACKS, null); @@ -1045,7 +905,7 @@ public class AdvancedEnhancer extends AbstractClassGenerator e.end_method(); } - private void emitSetStaticCallbacks(ClassEmitter ce) { + private static void emitSetStaticCallbacks(ClassEmitter ce) { CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, SET_STATIC_CALLBACKS, null); @@ -1055,7 +915,7 @@ public class AdvancedEnhancer extends AbstractClassGenerator e.end_method(); } - private void emitCurrentCallback(CodeEmitter e, int index) { + private static void emitCurrentCallback(CodeEmitter e, int index) { e.load_this(); e.getfield(getCallbackField(index)); e.dup(); diff --git a/platform/platform-impl/src/org/jetbrains/io/BuiltInServer.java b/platform/platform-impl/src/org/jetbrains/io/BuiltInServer.java index 7310b936246d..cba4a70487b3 100644 --- a/platform/platform-impl/src/org/jetbrains/io/BuiltInServer.java +++ b/platform/platform-impl/src/org/jetbrains/io/BuiltInServer.java @@ -17,6 +17,7 @@ package org.jetbrains.io; import com.intellij.ide.XmlRpcServer; import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfo; @@ -91,6 +92,10 @@ public class BuiltInServer implements Disposable { } private void bindCustomPorts(int firstPort, int port, NioEventLoopGroup eventLoopGroup) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + return; + } + for (CustomPortServerManager customPortServerManager : CustomPortServerManager.EP_NAME.getExtensions()) { try { int customPortServerManagerPort = customPortServerManager.getPort(); |