diff options
Diffstat (limited to 'platform/platform-impl/src/com/intellij')
103 files changed, 3399 insertions, 1458 deletions
diff --git a/platform/platform-impl/src/com/intellij/codeInsight/editorActions/TextBlockTransferable.java b/platform/platform-impl/src/com/intellij/codeInsight/editorActions/TextBlockTransferable.java new file mode 100644 index 000000000000..7d972816769c --- /dev/null +++ b/platform/platform-impl/src/com/intellij/codeInsight/editorActions/TextBlockTransferable.java @@ -0,0 +1,127 @@ +/* + * 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.codeInsight.editorActions; + +import com.intellij.openapi.editor.RawText; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.text.StringUtil; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class TextBlockTransferable implements Transferable { + private final Collection<TextBlockTransferableData> myExtraData; + private final RawText myRawText; + private final String myText; + private final DataFlavor[] myTransferDataFlavors; + + public TextBlockTransferable(String text, Collection<TextBlockTransferableData> extraData, RawText rawText) { + myText = text; + myExtraData = extraData; + myRawText = rawText; + + List<DataFlavor> dataFlavors = new ArrayList<DataFlavor>(); + Collections.addAll(dataFlavors, DataFlavor.stringFlavor, DataFlavor.plainTextFlavor); + final DataFlavor flavor = RawText.getDataFlavor(); + if (myRawText != null && flavor != null) { + dataFlavors.add(flavor); + } + for(TextBlockTransferableData data: extraData) { + final DataFlavor blockFlavor = data.getFlavor(); + if (blockFlavor != null) { + dataFlavors.add(blockFlavor); + } + } + myTransferDataFlavors = dataFlavors.toArray(new DataFlavor[dataFlavors.size()]); + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + return myTransferDataFlavors; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + DataFlavor[] flavors = getTransferDataFlavors(); + for (DataFlavor flavor1 : flavors) { + if (flavor.equals(flavor1)) { + return true; + } + } + return false; + } + + @Override + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + try { + for(TextBlockTransferableData data: myExtraData) { + if (Comparing.equal(data.getFlavor(), flavor)) { + return data; + } + } + if (myRawText != null && Comparing.equal(RawText.getDataFlavor(), flavor)) { + return myRawText; + } + else if (DataFlavor.stringFlavor.equals(flavor)) { + return myText; + } + else if (DataFlavor.plainTextFlavor.equals(flavor)) { + return new StringReader(myText); + } + } + catch(NoClassDefFoundError e) { + // ignore + } + throw new UnsupportedFlavorException(flavor); + } + + public static String convertLineSeparators(String text, + String newSeparator, + Collection<TextBlockTransferableData> transferableDatas) { + if (transferableDatas.size() > 0){ + int size = 0; + for(TextBlockTransferableData data: transferableDatas) { + size += data.getOffsetCount(); + } + + int[] offsets = new int[size]; + int index = 0; + for(TextBlockTransferableData data: transferableDatas) { + index = data.getOffsets(offsets, index); + } + + text = StringUtil.convertLineSeparators(text, newSeparator, offsets); + + index = 0; + for(TextBlockTransferableData data: transferableDatas) { + index = data.setOffsets(offsets, index); + } + + return text; + } + else{ + return StringUtil.convertLineSeparators(text, newSeparator); + } + } +}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/codeInsight/editorActions/TextBlockTransferableData.java b/platform/platform-impl/src/com/intellij/codeInsight/editorActions/TextBlockTransferableData.java new file mode 100644 index 000000000000..bdaf6f2ea546 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/codeInsight/editorActions/TextBlockTransferableData.java @@ -0,0 +1,30 @@ +/* + * 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.codeInsight.editorActions; + +import java.awt.datatransfer.DataFlavor; + +/** + * @author yole + */ +public interface TextBlockTransferableData { + DataFlavor getFlavor(); + + int getOffsetCount(); + int getOffsets(final int[] offsets, final int index); + int setOffsets(final int[] offsets, final int index); +} diff --git a/platform/platform-impl/src/com/intellij/execution/DelayedDocumentWatcher.java b/platform/platform-impl/src/com/intellij/execution/DelayedDocumentWatcher.java index 64aefa4c0977..b8e17ea3eded 100644 --- a/platform/platform-impl/src/com/intellij/execution/DelayedDocumentWatcher.java +++ b/platform/platform-impl/src/com/intellij/execution/DelayedDocumentWatcher.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. @@ -115,7 +115,7 @@ public class DelayedDocumentWatcher { public void documentChanged(DocumentEvent event) { if (myDocumentSavingInProgress) { /** When {@link FileDocumentManager#saveAllDocuments} is called, - * {@link com.intellij.openapi.fileEditor.impl.TrailingSpacesStripper} can change a document. + * {@link com.intellij.openapi.editor.impl.TrailingSpacesStripper} can change a document. * These needless 'documentChanged' events should be filtered out. */ return; diff --git a/platform/platform-impl/src/com/intellij/help/impl/MacHelpUtil.java b/platform/platform-impl/src/com/intellij/help/impl/MacHelpUtil.java index 90d633f0d621..a73295c90f74 100644 --- a/platform/platform-impl/src/com/intellij/help/impl/MacHelpUtil.java +++ b/platform/platform-impl/src/com/intellij/help/impl/MacHelpUtil.java @@ -41,7 +41,7 @@ public class MacHelpUtil { } static boolean isApplicable() { - return SystemInfo.isMac && Registry.is("ide.mac.show.native.help", false) && !PlatformUtils.isCidr() && !PlatformUtils + return SystemInfo.isMac && Registry.is("ide.mac.show.native.help") && !PlatformUtils.isCidr() && !PlatformUtils .isIdeaCommunity(); } } diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index 1bbd74984fcc..c8699c083ef4 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -952,25 +952,18 @@ public class IdeEventQueue extends EventQueue { } private static class WindowsAltSupressor implements EventDispatcher { - - private boolean myPureAltWasPressed; private boolean myWaitingForAltRelease; - private boolean myWaiterScheduled; - private Robot myRobot; @Override public boolean dispatch(AWTEvent e) { boolean dispatch = true; - if (!Registry.is("actionSystem.win.suppressAlt.new") && e instanceof KeyEvent) { + if (e instanceof KeyEvent) { KeyEvent ke = (KeyEvent)e; final Component component = ke.getComponent(); - final Window window = component == null ? null : SwingUtilities.windowForComponent(component); boolean pureAlt = ke.getKeyCode() == KeyEvent.VK_ALT && (ke.getModifiers() | InputEvent.ALT_MASK) == InputEvent.ALT_MASK; if (!pureAlt) { - myPureAltWasPressed = false; myWaitingForAltRelease = false; - myWaiterScheduled = false; } else { if (ApplicationManager.getApplication() == null || @@ -978,28 +971,25 @@ public class IdeEventQueue extends EventQueue { !SystemInfo.isWindows || !Registry.is("actionSystem.win.suppressAlt") || !(UISettings.getInstance().HIDE_TOOL_STRIPES || UISettings.getInstance().PRESENTATION_MODE)) { - return !dispatch; + return true; } if (ke.getID() == KeyEvent.KEY_PRESSED) { - myPureAltWasPressed = true; dispatch = !myWaitingForAltRelease; } else if (ke.getID() == KeyEvent.KEY_RELEASED) { if (myWaitingForAltRelease) { - myPureAltWasPressed = false; myWaitingForAltRelease = false; - myWaiterScheduled = false; dispatch = false; } - else { - myWaiterScheduled = true; + else if (component != null) { //noinspection SSBasedInspection SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { - if (SystemInfo.isWindows || window == null || !window.isActive()) { + final Window window = component instanceof Window ? (Window)component : SwingUtilities.windowForComponent(component); + if (window == null || !window.isActive()) { return; } myWaitingForAltRelease = true; diff --git a/platform/platform-impl/src/com/intellij/ide/IdeRepaintManager.java b/platform/platform-impl/src/com/intellij/ide/IdeRepaintManager.java index 8aa0e38c0d0d..d7118949e5de 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeRepaintManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeRepaintManager.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. @@ -24,13 +24,12 @@ package com.intellij.ide; import com.intellij.openapi.diagnostic.Logger; import com.intellij.reference.SoftReference; -import org.jetbrains.annotations.NonNls; +import com.intellij.util.ReflectionUtil; import javax.swing.*; import java.awt.*; import java.awt.image.VolatileImage; import java.lang.ref.WeakReference; -import java.lang.reflect.Field; import java.util.Map; /** @@ -40,7 +39,6 @@ public class IdeRepaintManager extends RepaintManager { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.HackyRepaintManager"); private Map<GraphicsConfiguration, VolatileImage> myImagesMap; - @NonNls private static final String FAULTY_FIELD_NAME = "volatileMap"; WeakReference<JComponent> myLastComponent; @@ -53,14 +51,7 @@ public class IdeRepaintManager extends RepaintManager { // sync here is to avoid data race when two(!) AWT threads on startup try to compete for the single myImagesMap private synchronized void clearLeakyImages(boolean force) { if (myImagesMap == null) { - try { - Field volMapField = RepaintManager.class.getDeclaredField(FAULTY_FIELD_NAME); - volMapField.setAccessible(true); - myImagesMap = (Map<GraphicsConfiguration, VolatileImage>)volMapField.get(this); - } - catch (Exception e) { - LOG.error(e); - } + myImagesMap = ReflectionUtil.getField(RepaintManager.class, this, Map.class, "volatileMap"); } if (force || @@ -90,12 +81,12 @@ public class IdeRepaintManager extends RepaintManager { // We must keep a strong reference to the DisplayChangedListener, // since SunDisplayChanger keeps only a WeakReference to it. - private Object displayChangeHack; + private DisplayChangeHandler displayChangeHack; { try { GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice[] devices = env.getScreenDevices(); // init + env.getScreenDevices(); // init Class<?> aClass = Class.forName("sun.awt.DisplayChangedListener"); // might be absent displayChangeHack = new DisplayChangeHandler(); @@ -104,7 +95,8 @@ public class IdeRepaintManager extends RepaintManager { .getMethod("addDisplayChangedListener", new Class[]{aClass}) .invoke(env, displayChangeHack); } - } catch (Throwable t) { + } + catch (Throwable t) { if (!(t instanceof HeadlessException)) LOG.error("Cannot setup display change listener", t); } } diff --git a/platform/platform-impl/src/com/intellij/ide/XmlRpcServerImpl.java b/platform/platform-impl/src/com/intellij/ide/XmlRpcServerImpl.java index e3e1945d14f1..3b8bacee5457 100644 --- a/platform/platform-impl/src/com/intellij/ide/XmlRpcServerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/XmlRpcServerImpl.java @@ -59,7 +59,7 @@ public class XmlRpcServerImpl implements XmlRpcServer { static final class XmlRpcRequestHandler extends HttpRequestHandler { @Override public boolean isSupported(@NotNull FullHttpRequest request) { - return request.getMethod() == HttpMethod.POST || request.getMethod() == HttpMethod.OPTIONS; + return request.method() == HttpMethod.POST || request.method() == HttpMethod.OPTIONS; } @Override @@ -89,7 +89,7 @@ public class XmlRpcServerImpl implements XmlRpcServer { return false; } - if (request.getMethod() == HttpMethod.POST) { + if (request.method() == HttpMethod.POST) { ByteBuf result; ByteBufInputStream in = new ByteBufInputStream(request.content()); try { @@ -116,7 +116,7 @@ public class XmlRpcServerImpl implements XmlRpcServer { return true; } else if (HttpMethod.POST.name().equals(request.headers().get("Access-Control-Request-Method"))) { - LOG.assertTrue(request.getMethod() == HttpMethod.OPTIONS); + LOG.assertTrue(request.method() == HttpMethod.OPTIONS); Responses.sendOptionsResponse("POST, OPTIONS", request, context); return true; } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ChooseComponentsToExportDialog.java b/platform/platform-impl/src/com/intellij/ide/actions/ChooseComponentsToExportDialog.java index 7bbc3e0c6cba..006830479db3 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ChooseComponentsToExportDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ChooseComponentsToExportDialog.java @@ -33,6 +33,7 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.FieldPanel; import com.intellij.util.Consumer; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -55,15 +56,14 @@ public class ChooseComponentsToExportDialog extends DialogWrapper { private final boolean myShowFilePath; private final String myDescription; - public ChooseComponentsToExportDialog(List<ExportableComponent> components, - Map<File, Set<ExportableComponent>> fileToComponents, + public ChooseComponentsToExportDialog(MultiMap<File, ExportableComponent> fileToComponents, boolean showFilePath, final String title, String description) { super(false); myDescription = description; myShowFilePath = showFilePath; Map<ExportableComponent, ComponentElementProperties> componentToContainingListElement = new LinkedHashMap<ExportableComponent, ComponentElementProperties>(); - for (ExportableComponent component : components) { + for (ExportableComponent component : fileToComponents.values()) { if (!addToExistingListElement(component, componentToContainingListElement, fileToComponents)) { ComponentElementProperties componentElementProperties = new ComponentElementProperties(); componentElementProperties.addComponent(component); @@ -149,14 +149,14 @@ public class ChooseComponentsToExportDialog extends DialogWrapper { } private static boolean addToExistingListElement(ExportableComponent component, - Map<ExportableComponent,ComponentElementProperties> componentToContainingListElement, - Map<File, Set<ExportableComponent>> fileToComponents) { + Map<ExportableComponent, ComponentElementProperties> componentToContainingListElement, + MultiMap<File, ExportableComponent> fileToComponents) { final File[] exportFiles = component.getExportFiles(); File file = null; for (File exportFile : exportFiles) { - final Set<ExportableComponent> tiedComponents = fileToComponents.get(exportFile); + Collection<ExportableComponent> tiedComponents = fileToComponents.get(exportFile); - for (final ExportableComponent tiedComponent : tiedComponents) { + for (ExportableComponent tiedComponent : tiedComponents) { if (tiedComponent == component) continue; final ComponentElementProperties elementProperties = componentToContainingListElement.get(tiedComponent); if (elementProperties != null && !FileUtil.filesEqual(exportFile, file)) { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java index e82e22d926ea..19a6502b8add 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java @@ -35,7 +35,9 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import com.intellij.util.io.ZipUtil; +import org.jetbrains.annotations.NotNull; import java.io.BufferedOutputStream; import java.io.File; @@ -47,10 +49,18 @@ import java.util.jar.JarOutputStream; public class ExportSettingsAction extends AnAction implements DumbAware { public void actionPerformed(AnActionEvent e) { Project project = getEventProject(e); - List<ExportableComponent> exportableComponents = new ArrayList<ExportableComponent>(); - Map<File,Set<ExportableComponent>> fileToComponents = getRegisteredComponentsAndFiles(exportableComponents); - final ChooseComponentsToExportDialog dialog = new ChooseComponentsToExportDialog(exportableComponents, fileToComponents, true, + ApplicationManager.getApplication().saveSettings(); + + MultiMap<File, ExportableComponent> fileToComponents = getExportableComponentsMap(); + for (Iterator<File> it = fileToComponents.keySet().iterator(); it.hasNext(); ) { + File file = it.next(); + if (!file.exists()) { + it.remove(); + } + } + + final ChooseComponentsToExportDialog dialog = new ChooseComponentsToExportDialog(fileToComponents, true, IdeBundle.message("title.select.components.to.export"), IdeBundle.message( "prompt.please.check.all.components.to.export")); @@ -65,8 +75,6 @@ public class ExportSettingsAction extends AnAction implements DumbAware { ContainerUtil.addAll(exportFiles, markedComponent.getExportFiles()); } - ApplicationManager.getApplication().saveSettings(); - final File saveFile = dialog.getExportFile(); try { if (saveFile.exists()) { @@ -121,24 +129,18 @@ public class ExportSettingsAction extends AnAction implements DumbAware { } } - public static Map<File, Set<ExportableComponent>> getRegisteredComponentsAndFiles(List<ExportableComponent> exportableComponents) { - Map<File,Set<ExportableComponent>> fileToComponents = new HashMap<File, Set<ExportableComponent>>(); + @NotNull + public static MultiMap<File, ExportableComponent> getExportableComponentsMap() { + MultiMap<File, ExportableComponent> result = MultiMap.createSet(); - final List<ExportableComponent> components = new ArrayList<ExportableComponent>(Arrays.asList(ApplicationManager.getApplication().getComponents(ExportableApplicationComponent.class))); + ExportableApplicationComponent[] components1 = ApplicationManager.getApplication().getComponents(ExportableApplicationComponent.class); + List<ExportableComponent> components2 = ServiceBean.loadServicesFromBeans(ExportableComponent.EXTENSION_POINT, ExportableComponent.class); - components.addAll(ServiceBean.loadServicesFromBeans(ExportableComponent.EXTENSION_POINT, ExportableComponent.class)); - - for (ExportableComponent component : components) { - exportableComponents.add(component); + for (ExportableComponent component : ContainerUtil.concat(Arrays.asList(components1), components2)) { for (File exportFile : component.getExportFiles()) { - Set<ExportableComponent> componentsTied = fileToComponents.get(exportFile); - if (componentsTied == null) { - componentsTied = new HashSet<ExportableComponent>(); - fileToComponents.put(exportFile, componentsTied); - } - componentsTied.add(component); + result.putValue(exportFile, component); } } - return fileToComponents; + return result; } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ImportSettingsAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ImportSettingsAction.java index 1e989946ab2f..f06c45e1af7a 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ImportSettingsAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ImportSettingsAction.java @@ -36,13 +36,17 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.updateSettings.impl.UpdateSettings; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.Consumer; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import com.intellij.util.io.ZipUtil; import java.awt.*; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; @@ -77,10 +81,10 @@ public class ImportSettingsAction extends AnAction implements DumbAware { return; } - final ArrayList<ExportableComponent> registeredComponents = new ArrayList<ExportableComponent>(); - final Map<File, Set<ExportableComponent>> filesToComponents = ExportSettingsAction.getRegisteredComponentsAndFiles(registeredComponents); - List<ExportableComponent> components = getComponentsStored(saveFile, registeredComponents); - final ChooseComponentsToExportDialog dialog = new ChooseComponentsToExportDialog(components, filesToComponents, false, + MultiMap<File, ExportableComponent> filesToComponents = ExportSettingsAction.getExportableComponentsMap(); + List<ExportableComponent> components = getComponentsStored(saveFile, ContainerUtil.newArrayList(filesToComponents.values())); + filesToComponents.values().retainAll(components); + final ChooseComponentsToExportDialog dialog = new ChooseComponentsToExportDialog(filesToComponents, false, IdeBundle.message("title.select.components.to.import"), IdeBundle.message("prompt.check.components.to.import")); dialog.show(); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/SendFeedbackAction.java b/platform/platform-impl/src/com/intellij/ide/actions/SendFeedbackAction.java index 40bfe41fb26f..4a72eda21a3e 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/SendFeedbackAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/SendFeedbackAction.java @@ -27,17 +27,20 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; import com.intellij.ui.LicensingFacade; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nullable; import java.awt.*; public class SendFeedbackAction extends AnAction implements DumbAware { + @Override public void actionPerformed(AnActionEvent e) { - launchBrowser(); + launchBrowser(e.getProject()); } - public static void launchBrowser() { + public static void launchBrowser(@Nullable Project project) { final ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx(); boolean eap = appInfo.isEAP(); String urlTemplate = eap ? appInfo.getEAPFeedbackUrl() : appInfo.getReleaseFeedbackUrl(); @@ -47,7 +50,7 @@ public class SendFeedbackAction extends AnAction implements DumbAware { .replace("$VERSION", appInfo.getFullVersion()) .replace("$EVAL", isEvaluationLicense() ? "true" : "false") .replace("$DESCR", getDescription()); - BrowserUtil.browse(urlTemplate); + BrowserUtil.browse(urlTemplate, project); } private static String getDescription() { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/SplitterActionBase.java b/platform/platform-impl/src/com/intellij/ide/actions/SplitterActionBase.java index 851c9096a38b..dde5a5c898dd 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/SplitterActionBase.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/SplitterActionBase.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. @@ -27,14 +27,9 @@ public abstract class SplitterActionBase extends AnAction implements DumbAware { public void update(final AnActionEvent event) { final Project project = CommonDataKeys.PROJECT.getData(event.getDataContext()); final Presentation presentation = event.getPresentation(); - boolean enabled; - if (project == null) { - enabled = false; - } - else { - enabled = isActionEnabled(project); - } - if (ActionPlaces.isPopupPlace(event.getPlace())) { + boolean inContextMenu = ActionPlaces.isPopupPlace(event.getPlace()); + boolean enabled = project != null && isActionEnabled(project, inContextMenu); + if (inContextMenu) { presentation.setVisible(enabled); } else { @@ -42,7 +37,17 @@ public abstract class SplitterActionBase extends AnAction implements DumbAware { } } - protected boolean isActionEnabled(Project project) { + /** + * This method determines whether the action is enabled for {@code project} + * if {@code inContextMenu} is set to {@code false}. Otherwise, + * it determines whether the action is visible in the context menu. + * + * @param project the specified project + * @param inContextMenu whether the action is used in context menu + * @return {@code true} if the action is enabled, + * {@code false} otherwise + */ + protected boolean isActionEnabled(Project project, boolean inContextMenu) { final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project); return fileEditorManager.isInSplitter(); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java index 4541f4664751..cb35b5a504f9 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java @@ -1071,7 +1071,7 @@ public class Switcher extends AnAction implements DumbAware { public void layoutContainer(Container target) { final JScrollPane scrollPane = UIUtil.getParentOfType(JScrollPane.class, files); JComponent filesPane = scrollPane != null ? scrollPane : files; - if (sBounds == null) { + if (sBounds == null || !target.isShowing()) { super.layoutContainer(target); sBounds = separator.getBounds(); tBounds = toolWindows.getBounds(); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/TabsAlphabeticalModeSwitcher.java b/platform/platform-impl/src/com/intellij/ide/actions/TabsAlphabeticalModeSwitcher.java index 8431364c5d61..7b000f530bdf 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/TabsAlphabeticalModeSwitcher.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/TabsAlphabeticalModeSwitcher.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. @@ -18,6 +18,7 @@ package com.intellij.ide.actions; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.tabs.impl.JBEditorTabs; import javax.swing.*; @@ -28,7 +29,7 @@ import javax.swing.*; public class TabsAlphabeticalModeSwitcher extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { - return JBEditorTabs.isAlphabeticalMode(); + return Registry.is(JBEditorTabs.TABS_ALPHABETICAL_KEY); } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/actions/UnsplitAllAction.java b/platform/platform-impl/src/com/intellij/ide/actions/UnsplitAllAction.java index 12df75e75656..ec724884ef2d 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/UnsplitAllAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/UnsplitAllAction.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,8 +32,8 @@ public final class UnsplitAllAction extends SplitterActionBase { } @Override - protected boolean isActionEnabled(Project project) { + protected boolean isActionEnabled(Project project, boolean inContextMenu) { final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project); - return fileEditorManager.getWindowSplitCount() > 2; + return inContextMenu ? fileEditorManager.getWindowSplitCount() > 2 : fileEditorManager.isInSplitter(); } } 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 d450cf454291..1af2fb883efb 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java @@ -25,7 +25,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.util.IconUtil; -import com.intellij.util.PlatformUtils; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; @@ -55,12 +54,12 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { myLafNames.put(DARCULA, IconLoader.getIcon("/lafs/OSXDarcula.png")); } else if (SystemInfo.isWindows) { - if (PlatformUtils.isIdeaCommunity()) { + //if (PlatformUtils.isIdeaCommunity()) { myLafNames.put(INTELLIJ, IconLoader.getIcon("/lafs/WindowsIntelliJ.png")); - } - else { - myLafNames.put(ALLOY, IconLoader.getIcon("/lafs/WindowsAlloy.png")); - } + //} + //else { + // myLafNames.put(ALLOY, IconLoader.getIcon("/lafs/WindowsAlloy.png")); + //} myLafNames.put(DARCULA, IconLoader.getIcon("/lafs/WindowsDarcula.png")); } else { diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordStorage.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordStorage.java index e5b9f1530256..b1ea1332986e 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordStorage.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordStorage.java @@ -25,21 +25,15 @@ import org.jetbrains.annotations.Nullable; * The interface defines basic password management operations */ public interface PasswordStorage { - /** - * @deprecated To remove in IDEA 15. Use {@link #getPassword(Project, Class, String, ModalityState)} - */ - @Deprecated - @Nullable - String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException; - - /** - * Get password stored in a password safe. - * <p/> - * The method may be called from any thread. It may need to show a master password dialog to unlock the password database, - * and then will use {@link Application#invokeAndWait(Runnable, ModalityState) invokeAndWait()}. - * So make sure to pass correct {@link ModalityState} to the method to make sure the dialog is shown above all other dialog or progress - * windows. + * <p>Get password stored in a password safe.</p> + * + * <p><b>NB: </b> + * This method may be called from the background, + * and it may need to ask user to enter the master password to access the database by calling + * {@link Application#invokeAndWait(Runnable, ModalityState) invokeAndWait()} to show a modal dialog. + * So make sure not to call it from the read action. + * Calling this method from the dispatch thread is allowed.</p> * * @param project the project, that is used to ask for the master password if this is the first access to password safe * @param requestor the requestor class @@ -49,53 +43,25 @@ public interface PasswordStorage { * @throws IllegalStateException if the method is called from the read action. */ @Nullable - String getPassword(@Nullable Project project, @NotNull Class requestor, String key, - @Nullable ModalityState state) throws PasswordSafeException; - + String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException; /** - * Store password in password safe - * <p/> - * The method may be called from any thread. It may need to show a master password dialog to unlock the password database, - * and then will use {@link Application#invokeAndWait(Runnable, ModalityState) invokeAndWait()}. - * So make sure to pass correct {@link ModalityState} to the method to make sure the dialog is shown above all other dialog or progress - * windows. + * Remove password stored in a password safe * * @param project the project, that is used to ask for the master password if this is the first access to password safe * @param requestor the requestor class * @param key the key for the password - * @param value the value to store + * @return the plugin key * @throws PasswordSafeException if password safe cannot be accessed */ - void storePassword(@Nullable Project project, @NotNull Class requestor, String key, String value, - @Nullable ModalityState modalityState) throws PasswordSafeException; - - /** - * @deprecated To remove in IDEA 15. Use {@link #storePassword(Project, Class, String, String, ModalityState)} - */ - @Deprecated - void storePassword(@Nullable Project project, @NotNull Class requestor, String key, String value) throws PasswordSafeException; - - /** - * @deprecated To remove in IDEA 15. Use {@link #removePassword(Project, Class, String, ModalityState)} - */ - @Deprecated void removePassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException; - /** - * Remove password stored in a password safe - * <p/> - * The method may be called from any thread. It may need to show a master password dialog to unlock the password database, - * and then will use {@link Application#invokeAndWait(Runnable, ModalityState) invokeAndWait()}. - * So make sure to pass correct {@link ModalityState} to the method to make sure the dialog is shown above all other dialog or progress - * windows. + * Store password in password safe * * @param project the project, that is used to ask for the master password if this is the first access to password safe * @param requestor the requestor class * @param key the key for the password - * @return the plugin key + * @param value the value to store * @throws PasswordSafeException if password safe cannot be accessed */ - void removePassword(@Nullable Project project, @NotNull Class requestor, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException; - + void storePassword(@Nullable Project project, @NotNull Class requestor, String key, String value) throws PasswordSafeException; } diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/PasswordSafeImpl.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/PasswordSafeImpl.java index 34aee7aaad38..6d5127b376ad 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/PasswordSafeImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/PasswordSafeImpl.java @@ -22,7 +22,6 @@ import com.intellij.ide.passwordSafe.impl.providers.masterKey.MasterKeyPasswordS import com.intellij.ide.passwordSafe.impl.providers.masterKey.PasswordDatabase; import com.intellij.ide.passwordSafe.impl.providers.memory.MemoryPasswordSafe; import com.intellij.ide.passwordSafe.impl.providers.nil.NilProvider; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; @@ -71,19 +70,11 @@ public class PasswordSafeImpl extends PasswordSafe { } @Nullable - @Override public String getPassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException { - return getPassword(project, requester, key, null); - } - - @Nullable - @Override - public String getPassword(@Nullable Project project, @NotNull Class requester, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException { if (mySettings.getProviderType().equals(PasswordSafeSettings.ProviderType.MASTER_PASSWORD)) { - String password = getMemoryProvider().getPassword(project, requester, key, modalityState); + String password = getMemoryProvider().getPassword(project, requester, key); if (password == null) { - password = provider().getPassword(project, requester, key, modalityState); + password = provider().getPassword(project, requester, key); if (password != null) { // cache the password in memory as well for easier access during the session getMemoryProvider().storePassword(project, requester, key, password); @@ -91,35 +82,21 @@ public class PasswordSafeImpl extends PasswordSafe { } return password; } - return provider().getPassword(project, requester, key, modalityState); + return provider().getPassword(project, requester, key); } - @Override - public void removePassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException { - removePassword(project, requestor, key, null); - } - - @Override - public void removePassword(@Nullable Project project, @NotNull Class requester, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException { + public void removePassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException { if (mySettings.getProviderType().equals(PasswordSafeSettings.ProviderType.MASTER_PASSWORD)) { getMemoryProvider().removePassword(project, requester, key); } - provider().removePassword(project, requester, key, modalityState); - } - - @Override - public void storePassword(@Nullable Project project, @NotNull Class requestor, String key, String value) throws PasswordSafeException { - storePassword(project, requestor, key, value, null); + provider().removePassword(project, requester, key); } - @Override - public void storePassword(@Nullable Project project, @NotNull Class requester, String key, String value, - @Nullable ModalityState modalityState) throws PasswordSafeException { + public void storePassword(@Nullable Project project, @NotNull Class requester, String key, String value) throws PasswordSafeException { if (mySettings.getProviderType().equals(PasswordSafeSettings.ProviderType.MASTER_PASSWORD)) { getMemoryProvider().storePassword(project, requester, key, value); } - provider().storePassword(project, requester, key, value, modalityState); + provider().storePassword(project, requester, key, value); } /** diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/PasswordSafeProvider.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/PasswordSafeProvider.java index 3a40d617e99b..18dee7c755f5 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/PasswordSafeProvider.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/PasswordSafeProvider.java @@ -15,11 +15,7 @@ */ package com.intellij.ide.passwordSafe.impl; -import com.intellij.ide.passwordSafe.PasswordSafeException; import com.intellij.ide.passwordSafe.PasswordStorage; -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * The provider for password safe component @@ -38,21 +34,4 @@ public abstract class PasswordSafeProvider implements PasswordStorage { * @return the name of provider */ public abstract String getName(); - - @Nullable - @Override - public String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException { - return getPassword(project, requestor, key, null); - } - - @Override - public void storePassword(@Nullable Project project, @NotNull Class requestor, String key, String value) throws PasswordSafeException { - storePassword(project, requestor, key, value, null); - } - - @Override - public void removePassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException { - removePassword(project, requestor, key, null); - } - } diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java index 8c8f921f18b8..a0b001824d90 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java @@ -43,15 +43,13 @@ public abstract class BasePasswordSafeProvider extends PasswordSafeProvider { * @throws PasswordSafeException in case of problems with access to the password database. * @throws IllegalStateException if the method is called from the read action. */ - protected abstract byte[] key(@Nullable Project project, @NotNull Class requestor, - @Nullable ModalityState modalityState) throws PasswordSafeException; + protected abstract byte[] key(@Nullable Project project, @NotNull Class requestor) throws PasswordSafeException; @Nullable - public String getPassword(@Nullable Project project, @NotNull Class requestor, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException { - byte[] k = dbKey(project, requestor, key, modalityState); + public String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException { + byte[] k = dbKey(project, requestor, key); byte[] ct = getEncryptedPassword(k); - return ct == null ? null : EncryptionUtil.decryptText(key(project, requestor, modalityState), ct); + return ct == null ? null : EncryptionUtil.decryptText(key(project, requestor), ct); } /** @@ -68,17 +66,14 @@ public abstract class BasePasswordSafeProvider extends PasswordSafeProvider { * @param project * @param requestor the requestor class * @param key the key to use - * @param modalityState * @return the key to use for map */ - private byte[] dbKey(@Nullable Project project, @NotNull Class requestor, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException { - return EncryptionUtil.dbKey(key(project, requestor, modalityState), requestor, key); + private byte[] dbKey(@Nullable Project project, Class requestor, String key) throws PasswordSafeException { + return EncryptionUtil.dbKey(key(project, requestor), requestor, key); } - public void removePassword(@Nullable Project project, @NotNull Class requester, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException { - byte[] k = dbKey(project, requester, key, modalityState); + public void removePassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException { + byte[] k = dbKey(project, requester, key); removeEncryptedPassword(k); } @@ -89,10 +84,9 @@ public abstract class BasePasswordSafeProvider extends PasswordSafeProvider { */ protected abstract void removeEncryptedPassword(byte[] key); - public void storePassword(@Nullable Project project, @NotNull Class requestor, String key, String value, - @Nullable ModalityState modalityState) throws PasswordSafeException { - byte[] k = dbKey(project, requestor, key, modalityState); - byte[] ct = EncryptionUtil.encryptText(key(project, requestor, modalityState), value); + public void storePassword(@Nullable Project project, @NotNull Class requestor, String key, String value) throws PasswordSafeException { + byte[] k = dbKey(project, requestor, key); + byte[] ct = EncryptionUtil.encryptText(key(project, requestor), value); storeEncryptedPassword(k, ct); } 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 b74e51315cd5..840159270a8d 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 @@ -154,8 +154,7 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider { } @Override - protected byte[] key(@Nullable final Project project, @NotNull final Class requestor, - @Nullable ModalityState modalityState) throws PasswordSafeException { + protected byte[] key(@Nullable final Project project, @NotNull final Class requestor) throws PasswordSafeException { Application application = ApplicationManager.getApplication(); if (!isTestMode() && application.isHeadlessEnvironment()) { throw new MasterPasswordUnavailableException("The provider is not available in headless environment"); @@ -200,7 +199,7 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider { } } } - }, modalityState == null ? ModalityState.defaultModalityState() : modalityState); + }, ModalityState.any()); //noinspection ThrowableResultOfMethodCallIgnored if (ex.get() != null) { throw ex.get(); @@ -211,12 +210,11 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider { } @Override - public String getPassword(@Nullable Project project, @NotNull Class requestor, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException { + public String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException { if (database.isEmpty()) { return null; } - return super.getPassword(project, requestor, key, modalityState); + return super.getPassword(project, requestor, key); } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java index da82bb986fdb..4989789b4ec0 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java @@ -19,12 +19,10 @@ import com.intellij.ide.passwordSafe.impl.PasswordSafeTimed; import com.intellij.ide.passwordSafe.impl.providers.BasePasswordSafeProvider; import com.intellij.ide.passwordSafe.impl.providers.ByteArrayWrapper; import com.intellij.ide.passwordSafe.impl.providers.EncryptionUtil; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.registry.Registry; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.security.SecureRandom; import java.util.Collections; @@ -60,7 +58,7 @@ public class MemoryPasswordSafe extends BasePasswordSafeProvider { } @Override - protected byte[] key(Project project, @NotNull Class requestor, @Nullable ModalityState modalityState) { + protected byte[] key(Project project, @NotNull Class requestor) { if (key.get() == null) { byte[] rnd = new byte[EncryptionUtil.SECRET_KEY_SIZE_BYTES * 16]; new SecureRandom().nextBytes(rnd); diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/nil/NilProvider.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/nil/NilProvider.java index e3a77325b06e..4bf024d697c2 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/nil/NilProvider.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/nil/NilProvider.java @@ -17,7 +17,6 @@ package com.intellij.ide.passwordSafe.impl.providers.nil; import com.intellij.ide.passwordSafe.PasswordSafeException; import com.intellij.ide.passwordSafe.impl.PasswordSafeProvider; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,22 +41,16 @@ public final class NilProvider extends PasswordSafeProvider { return "Do not Store"; } - @Nullable - public String getPassword(@Nullable Project project, @NotNull Class requester, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException { + public String getPassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException { // nothing is stored return null; } - @Override - public void removePassword(@Nullable Project project, @NotNull Class requester, String key, - @Nullable ModalityState modalityState) throws PasswordSafeException { + public void removePassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException { // do nothing } - @Override - public void storePassword(@Nullable Project project, @NotNull Class requester, String key, String value, - @Nullable ModalityState modalityState) throws PasswordSafeException { + public void storePassword(@Nullable Project project, @NotNull Class requester, String key, String value) throws PasswordSafeException { // just forget about password } } diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java index 353e836f3f33..8aa0099ccd1d 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java @@ -73,10 +73,7 @@ public class PasswordSafePromptDialog extends DialogWrapper { /** * Ask password possibly asking password database first. The method could be invoked from any thread. If UI needs to be shown, * the method invokes {@link UIUtil#invokeAndWaitIfNeeded(Runnable)} - * * @param project the context project - * @param modalityState the modality state using which any prompts initiated by the git process should be shown in the UI. - * If null then {@link ModalityState#defaultModalityState() the default modality state} will be used. * @param title the dialog title * @param message the message describing a resource for which password is asked * @param requestor the password requestor @@ -86,13 +83,12 @@ public class PasswordSafePromptDialog extends DialogWrapper { */ @Nullable public static String askPassword(final Project project, - @Nullable ModalityState modalityState, final String title, final String message, @NotNull final Class<?> requestor, final String key, boolean resetPassword, String error) { - return askPassword(project, modalityState, title, message, requestor, key, resetPassword, error, null, null); + return askPassword(project, title, message, requestor, key, resetPassword, error, null, null); } /** @@ -112,17 +108,14 @@ public class PasswordSafePromptDialog extends DialogWrapper { @NotNull final Class<?> requestor, final String key, boolean resetPassword) { - return askPassword(null, null, title, message, requestor, key, resetPassword, null); + return askPassword(null, title, message, requestor, key, resetPassword, null); } /** * Ask passphrase possibly asking password database first. The method could be invoked from any thread. If UI needs to be shown, * the method invokes {@link UIUtil#invokeAndWaitIfNeeded(Runnable)} - * * @param project the context project (might be null) - * @param modalityState the modality state using which any prompts initiated by the git process should be shown in the UI. - * If null then {@link ModalityState#defaultModalityState() the default modality state} will be used. * @param title the dialog title * @param message the message describing a resource for which password is asked * @param requestor the password requestor @@ -132,13 +125,13 @@ public class PasswordSafePromptDialog extends DialogWrapper { */ @Nullable public static String askPassphrase(final Project project, - @Nullable ModalityState modalityState, final String title, + final String title, final String message, @NotNull final Class<?> requestor, final String key, boolean resetPassword, String error) { - return askPassword(project, modalityState, title, message, requestor, key, resetPassword, error, + return askPassword(project, title, message, requestor, key, resetPassword, error, "Passphrase:", "Remember the passphrase"); } @@ -146,10 +139,7 @@ public class PasswordSafePromptDialog extends DialogWrapper { /** * Ask password possibly asking password database first. The method could be invoked from any thread. If UI needs to be shown, * the method invokes {@link UIUtil#invokeAndWaitIfNeeded(Runnable)} - * * @param project the context project - * @param modalityState the modality state using which any prompts initiated by the git process should be shown in the UI. - * If null then {@link ModalityState#defaultModalityState() the default modality state} will be used. * @param title the dialog title * @param message the message describing a resource for which password is asked * @param requestor the password requestor @@ -161,7 +151,6 @@ public class PasswordSafePromptDialog extends DialogWrapper { */ @Nullable private static String askPassword(final Project project, - @Nullable ModalityState modalityState, final String title, final String message, @NotNull final Class<?> requestor, @@ -176,7 +165,7 @@ public class PasswordSafePromptDialog extends DialogWrapper { ps.removePassword(project, requestor, key); } else { - String pw = ps.getPassword(project, requestor, key, modalityState); + String pw = ps.getPassword(project, requestor, key); if (pw != null) { return pw; } @@ -214,7 +203,7 @@ public class PasswordSafePromptDialog extends DialogWrapper { } } } - }, modalityState == null ? ModalityState.defaultModalityState() : modalityState); + }, ModalityState.any()); return ref.get(); } } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.java index 20a41e3a886a..931458ff8bc0 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.java @@ -161,7 +161,7 @@ public abstract class PluginManagerMain implements Disposable { g.fillRect(0,0, getWidth(), getHeight()); } }; - header.setBorder(new CustomLineBorder(UIUtil.getBorderColor(), 1, 1, 0, 1)); + header.setBorder(new CustomLineBorder(1, 1, 0, 1)); final JLabel mySortLabel = new JLabel(); mySortLabel.setForeground(UIUtil.getLabelDisabledForeground()); mySortLabel.setBorder(new EmptyBorder(1, 1, 1, 5)); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java b/platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java index be7aa3c8f600..ec763a8d5a76 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java @@ -68,6 +68,8 @@ public class RepositoryHelper { HttpConfigurable.getInstance().openHttpConnection(url) : (HttpURLConnection)new URL(url).openConnection(); connection.setRequestProperty("Accept-Encoding", "gzip"); + connection.setReadTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + connection.setConnectTimeout(HttpConfigurable.CONNECTION_TIMEOUT); if (indicator != null) { indicator.setText2(IdeBundle.message("progress.waiting.for.reply.from.plugin.manager", appInfo.getPluginManagerUrl())); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java index 19d924dff777..1036307e97d7 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java @@ -46,6 +46,7 @@ import com.intellij.ui.JBColor; import com.intellij.ui.ScreenUtil; import com.intellij.ui.content.Content; import com.intellij.ui.mac.MacPopupMenuUI; +import com.intellij.ui.popup.OurHeavyWeightPopup; import com.intellij.util.IJSwingUtilities; import com.intellij.util.ObjectUtils; import com.intellij.util.PlatformUtils; @@ -853,6 +854,9 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo final Point point = fixPopupLocation(contents, x, y); final int popupType = UIUtil.isUnderGTKLookAndFeel() ? WEIGHT_HEAVY : PopupUtil.getPopupType(this); + if (popupType == WEIGHT_HEAVY && OurHeavyWeightPopup.isEnabled()) { + return new OurHeavyWeightPopup(owner, contents, point.x, point.y); + } if (popupType >= 0) { PopupUtil.setPopupType(myDelegate, popupType); } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java index a86a28a375d5..b06cd21daa60 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java @@ -79,7 +79,7 @@ public class DarculaLaf extends BasicLookAndFeel { @SuppressWarnings("UnusedParameters") private static void log(Exception e) { // everything is gonna be alright - e.printStackTrace(); +// e.printStackTrace(); } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/darcula.properties b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/darcula.properties index 255d356d5bc0..5ddd24560338 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/darcula.properties +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/darcula.properties @@ -48,6 +48,13 @@ MenuBar.shadow=3c3f41 MenuBar.darcula.borderColor=555555 MenuBar.darcula.borderShadowColor=282828 +CheckBoxMenuItemUI=com.intellij.ide.ui.laf.darcula.ui.DarculaCheckBoxMenuItemUI +CheckBoxMenuItem.borderPainted=false + +RadioButtonMenuItemUI=com.intellij.ide.ui.laf.darcula.ui.DarculaRadioButtonMenuItemUI +RadioButtonMenuItem.borderPainted=false + + TabbedPaneUI=com.intellij.ide.ui.laf.darcula.ui.DarculaTabbedPaneUI TabbedPane.tabInsets=0,4,0,4 TabbedPane.highlight=292b2d diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaCheckBoxMenuItemUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaCheckBoxMenuItemUI.java new file mode 100644 index 000000000000..d9c4ac7895e6 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaCheckBoxMenuItemUI.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.ide.ui.laf.darcula.ui; + +import com.intellij.openapi.ui.GraphicsConfig; +import com.intellij.ui.Gray; +import com.intellij.util.ui.UIUtil; +import sun.swing.MenuItemLayoutHelper; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * @author Konstantin Bulenkov + */ +public class DarculaCheckBoxMenuItemUI extends DarculaMenuItemUIBase { + + @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"}) + public static ComponentUI createUI(JComponent c) { + return new DarculaCheckBoxMenuItemUI(); + } + + protected String getPropertyPrefix() { + return "CheckBoxMenuItem"; + } + + @Override + public Dimension getPreferredSize(JComponent c) { + return super.getPreferredSize(c); + } + + @Override + protected void paintCheckIcon(Graphics g2, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color holdc, Color foreground) { + Graphics2D g = (Graphics2D) g2; + final GraphicsConfig config = new GraphicsConfig(g); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); + + g.translate(lr.getCheckRect().x-2, lr.getCheckRect().y); + + final int sz = 13; + g.setPaint(new GradientPaint(sz / 2, 1, Gray._110, sz / 2, sz, Gray._95)); + g.fillRoundRect(0, 0, sz, sz - 1 , 4, 4); + + g.setPaint(new GradientPaint(sz / 2, 1, Gray._120.withAlpha(0x5a), sz / 2, sz, Gray._105.withAlpha(90))); + g.drawRoundRect(0, (UIUtil.isUnderDarcula() ? 1 : 0), sz, sz - 1, 4, 4); + + g.setPaint(Gray._40.withAlpha(180)); + g.drawRoundRect(0, 0, sz, sz - 1, 4, 4); + + + if (lh.getMenuItem().isSelected()) { + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + g.setPaint(Gray._30); + g.drawLine(4, 7, 7, 10); + g.drawLine(7, 10, sz, 2); + g.setPaint(Gray._170); + g.drawLine(4, 5, 7, 8); + g.drawLine(7, 8, sz, 0); + } + + g.translate(-lr.getCheckRect().x+2, -lr.getCheckRect().y); + config.restore(); + g.setColor(foreground); + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaMenuItemUIBase.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaMenuItemUIBase.java new file mode 100644 index 000000000000..cc69d24b6ca3 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaMenuItemUIBase.java @@ -0,0 +1,208 @@ +/* + * 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.ui.laf.darcula.ui; + +import sun.swing.MenuItemLayoutHelper; +import sun.swing.SwingUtilities2; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicMenuItemUI; +import java.awt.*; +import java.awt.event.MouseEvent; + +/** + * @author Konstantin Bulenkov + */ +public class DarculaMenuItemUIBase extends BasicMenuItemUI { + @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"}) + public static ComponentUI createUI(JComponent c) { + return new DarculaMenuItemUIBase(); + } + + public void processMouseEvent(JMenuItem item, MouseEvent e, MenuElement path[], MenuSelectionManager manager) { + Point p = e.getPoint(); + if (p.x >= 0 && p.x < item.getWidth() && + p.y >= 0 && p.y < item.getHeight()) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + manager.clearSelectedPath(); + item.doClick(0); + item.setArmed(false); + } else + manager.setSelectedPath(path); + } else if (item.getModel().isArmed()) { + MenuElement newPath[] = new MenuElement[path.length - 1]; + int i, c; + for (i = 0, c = path.length - 1; i < c; i++) + newPath[i] = path[i]; + manager.setSelectedPath(newPath); + } + } + + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + // Save original graphics font and color + Font holdf = g.getFont(); + Color holdc = g.getColor(); + + JMenuItem mi = (JMenuItem) c; + g.setFont(mi.getFont()); + + Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); + applyInsets(viewRect, mi.getInsets()); + + MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, + arrowIcon, viewRect, defaultTextIconGap, "-", //todo[kb] use protected field BasicMenuItemUI.acceleratorDelimiter when we move to java 1.7 + mi.getComponentOrientation().isLeftToRight(), mi.getFont(), + acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem), + getPropertyPrefix()); + MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); + + paintBackground(g, mi, background); + paintCheckIcon(g, lh, lr, holdc, foreground); + paintIcon(g, lh, lr, holdc); + g.setColor(foreground); + paintText(g, lh, lr); + paintAccText(g, lh, lr); + paintArrowIcon(g, lh, lr, foreground); + + // Restore original graphics font and color + g.setColor(holdc); + g.setFont(holdf); + } + + protected void paintIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, Color holdc) { + if (lh.getIcon() != null) { + Icon icon; + ButtonModel model = lh.getMenuItem().getModel(); + if (!model.isEnabled()) { + icon = lh.getMenuItem().getDisabledIcon(); + } else if (model.isPressed() && model.isArmed()) { + icon = lh.getMenuItem().getPressedIcon(); + if (icon == null) { + // Use default icon + icon = lh.getMenuItem().getIcon(); + } + } else { + icon = lh.getMenuItem().getIcon(); + } + + if (icon != null) { + icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, + lr.getIconRect().y); + g.setColor(holdc); + } + } + } + + protected void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color holdc, Color foreground) { + if (lh.getCheckIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(foreground); + } else { + g.setColor(holdc); + } + if (lh.useCheckAndArrow()) { + lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, + lr.getCheckRect().x, lr.getCheckRect().y); + } + g.setColor(holdc); + } + } + + protected void paintAccText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (!lh.getAccText().equals("")) { + ButtonModel model = lh.getMenuItem().getModel(); + g.setFont(lh.getAccFontMetrics().getFont()); + if (!model.isEnabled()) { + // *** paint the accText disabled + if (disabledForeground != null) { + g.setColor(disabledForeground); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + } else { + g.setColor(lh.getMenuItem().getBackground().brighter()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + g.setColor(lh.getMenuItem().getBackground().darker()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x - 1, + lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); + } + } else { + // *** paint the accText normally + if (model.isArmed() + || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(acceleratorSelectionForeground); + } else { + g.setColor(acceleratorForeground); + } + SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), + lr.getAccRect().x, lr.getAccRect().y + + lh.getAccFontMetrics().getAscent()); + } + } + } + + protected void paintText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (!lh.getText().equals("")) { + if (lh.getHtmlView() != null) { + // Text is HTML + lh.getHtmlView().paint(g, lr.getTextRect()); + } else { + // Text isn't HTML + paintText(g, lh.getMenuItem(), lr.getTextRect(), lh.getText()); + } + } + } + + protected void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color foreground) { + if (lh.getArrowIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(foreground); + } + if (lh.useCheckAndArrow()) { + lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, + lr.getArrowRect().x, lr.getArrowRect().y); + } + } + } + + protected void applyInsets(Rectangle rect, Insets insets) { + if(insets != null) { + rect.x += insets.left; + rect.y += insets.top; + rect.width -= (insets.right + rect.x); + rect.height -= (insets.bottom + rect.y); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonMenuItemUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonMenuItemUI.java new file mode 100644 index 000000000000..1048e5d0449b --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonMenuItemUI.java @@ -0,0 +1,85 @@ +/* + * 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.ui.laf.darcula.ui; + +import com.intellij.openapi.ui.GraphicsConfig; +import com.intellij.ui.ColorUtil; +import com.intellij.ui.Gray; +import sun.swing.MenuItemLayoutHelper; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * @author Konstantin Bulenkov + */ +public class DarculaRadioButtonMenuItemUI extends DarculaMenuItemUIBase { + @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"}) + public static ComponentUI createUI(JComponent c) { + return new DarculaRadioButtonMenuItemUI(); + } + + protected String getPropertyPrefix() { + return "RadioButtonMenuItem"; + } + + @Override + protected void paintCheckIcon(Graphics g2, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color holdc, Color foreground) { + Graphics2D g = (Graphics2D) g2; + final GraphicsConfig config = new GraphicsConfig(g); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); + + g.translate(lr.getCheckRect().x-1, lr.getCheckRect().y-1); + + int rad = 5; + + final int x = 0; + final int y = 0; + final int w = 13; + final int h = 13; + + g.translate(x, y); + + //setup AA for lines + Color bg = lh.getMenuItem().getBackground(); + g.setPaint(new GradientPaint(0, 0, ColorUtil.shift(bg, 1.5), + 0, 16, ColorUtil.shift(bg, 1.2))); + + g.fillOval(0, 1, w - 1, h - 1); + + g.setPaint(new GradientPaint(w / 2, 1, Gray._160.withAlpha(90), w / 2, h, Gray._100.withAlpha(90))); + g.drawOval(0, 2, w - 1, h - 1); + + g.setPaint(Gray._40.withAlpha(200)); + g.drawOval(0, 1, w - 1, h - 1); + + if (lh.getMenuItem().isSelected()) { + final boolean enabled = lh.getMenuItem().isEnabled(); + g.setColor(UIManager.getColor(enabled ? "RadioButton.darcula.selectionEnabledShadowColor" : "RadioButton.darcula.selectionDisabledShadowColor")); + g.fillOval((w - rad)/2 , h/2 , rad, rad); + g.setColor(UIManager.getColor(enabled ? "RadioButton.darcula.selectionEnabledColor" : "RadioButton.darcula.selectionDisabledColor")); + g.fillOval((w - rad)/2 , h/2 - 1, rad, rad); + } + config.restore(); + g.translate(-x, -y); + + + g.translate(-lr.getCheckRect().x+1, -lr.getCheckRect().y+1); + config.restore(); + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/util/ElementsChooser.java b/platform/platform-impl/src/com/intellij/ide/util/ElementsChooser.java index 2b8a8a499621..fcbe27f5c4a5 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/ElementsChooser.java +++ b/platform/platform-impl/src/com/intellij/ide/util/ElementsChooser.java @@ -15,383 +15,89 @@ */ package com.intellij.ide.util; -import com.intellij.ui.*; -import com.intellij.ui.table.JBTable; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.ComponentWithEmptyText; -import com.intellij.util.ui.StatusText; -import com.intellij.util.ui.Table; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.util.*; +import javax.swing.table.TableCellRenderer; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Map; /** * @see ChooseElementsDialog */ -public class ElementsChooser<T> extends JPanel implements ComponentWithEmptyText, ComponentWithExpandableItems<TableCell> { - private JBTable myTable = null; - private MyTableModel myTableModel = null; - private boolean myColorUnmarkedElements = true; - private final List<ElementsMarkListener<T>> myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private final Map<T,ElementProperties> myElementToPropertiesMap = new HashMap<T, ElementProperties>(); - private final Map<T, Boolean> myDisabledMap = new HashMap<T, Boolean>(); +public class ElementsChooser<T> extends MultiStateElementsChooser<T, Boolean> { + private static final BooleanMarkStateDescriptor MARK_STATE_DESCRIPTOR = new BooleanMarkStateDescriptor(); public interface ElementsMarkListener<T> { void elementMarkChanged(T element, boolean isMarked); } public ElementsChooser(final boolean elementsCanBeMarked) { - this(null, false, elementsCanBeMarked); + super(elementsCanBeMarked, ElementsChooser.<T>getMarkStateDescriptor()); } public ElementsChooser(List<T> elements, boolean marked) { - this(elements, marked, true); - } - - private ElementsChooser(@Nullable List<T> elements, boolean marked, boolean elementsCanBeMarked) { - super(new BorderLayout()); - - myTableModel = new MyTableModel(elementsCanBeMarked); - myTable = new Table(myTableModel); - myTable.setShowGrid(false); - myTable.setIntercellSpacing(new Dimension(0, 0)); - myTable.setTableHeader(null); - myTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); - myTable.setColumnSelectionAllowed(false); - JScrollPane pane = ScrollPaneFactory.createScrollPane(myTable); - pane.setPreferredSize(new Dimension(100, 155)); - TableColumnModel columnModel = myTable.getColumnModel(); - - if (elementsCanBeMarked) { - TableColumn checkMarkColumn = columnModel.getColumn(myTableModel.CHECK_MARK_COLUM_INDEX); - TableUtil.setupCheckboxColumn(checkMarkColumn); - checkMarkColumn.setCellRenderer(new CheckMarkColumnCellRenderer(myTable.getDefaultRenderer(Boolean.class))); - } - columnModel.getColumn(myTableModel.ELEMENT_COLUMN_INDEX).setCellRenderer(new MyElementColumnCellRenderer()); - - add(pane, BorderLayout.CENTER); - myTable.registerKeyboardAction( - new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final int[] selectedRows = myTable.getSelectedRows(); - boolean currentlyMarked = true; - for (int selectedRow : selectedRows) { - currentlyMarked = myTableModel.isElementMarked(selectedRow); - if (!currentlyMarked) { - break; - } - } - myTableModel.setMarked(selectedRows, !currentlyMarked); - } - }, - KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), - JComponent.WHEN_FOCUSED - ); - - final SpeedSearchBase<JBTable> speedSearch = new SpeedSearchBase<JBTable>(myTable) { - @Override - public int getSelectedIndex() { - return myTable.getSelectedRow(); - } - - @Override - protected int convertIndexToModel(int viewIndex) { - return myTable.convertRowIndexToModel(viewIndex); - } - - @Override - public Object[] getAllElements() { - final int count = myTableModel.getRowCount(); - Object[] elements = new Object[count]; - for (int idx = 0; idx < count; idx++) { - elements[idx] = myTableModel.getElementAt(idx); - } - return elements; - } - - @Override - public String getElementText(Object element) { - return getItemText((T)element); - } - - @Override - public void selectElement(Object element, String selectedText) { - final int count = myTableModel.getRowCount(); - for (int row = 0; row < count; row++) { - if (element.equals(myTableModel.getElementAt(row))) { - final int viewRow = myTable.convertRowIndexToView(row); - myTable.getSelectionModel().setSelectionInterval(viewRow, viewRow); - TableUtil.scrollSelectionToVisible(myTable); - break; - } - } - } - }; - speedSearch.setComparator(new SpeedSearchComparator(false)); - setElements(elements, marked); - installActions(myTable); - } - - private static void installActions(JTable table) { - InputMap inputMap = table.getInputMap(WHEN_FOCUSED); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), "selectLastRow"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), "selectFirstRow"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.SHIFT_DOWN_MASK), "selectFirstRowExtendSelection"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.SHIFT_DOWN_MASK), "selectLastRowExtendSelection"); - } - - @NotNull - @Override - public StatusText getEmptyText() { - return myTable.getEmptyText(); - } - - @NotNull - @Override - public ExpandableItemsHandler<TableCell> getExpandableItemsHandler() { - return myTable.getExpandableItemsHandler(); - } - - @Override - public void setExpandableItemsEnabled(boolean enabled) { - myTable.setExpandableItemsEnabled(enabled); - } - - public void setSingleSelectionMode() { - myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - } - - public void refresh() { - myTableModel.fireTableDataChanged(); - } - - public void refresh(T element) { - final int row = myTableModel.getElementRow(element); - if (row >= 0) { - myTableModel.fireTableRowsUpdated(row, row); - } - } - - private int[] mySavedSelection = null; - public void saveSelection() { - mySavedSelection = myTable.getSelectedRows(); - } - - public void restoreSelection() { - if (mySavedSelection != null) { - TableUtil.selectRows(myTable, mySavedSelection); - mySavedSelection = null; - } - } - - public boolean isColorUnmarkedElements() { - return myColorUnmarkedElements; - } - - public void setColorUnmarkedElements(boolean colorUnmarkedElements) { - myColorUnmarkedElements = colorUnmarkedElements; + super(elements, marked, ElementsChooser.<T>getMarkStateDescriptor()); } public void addElementsMarkListener(ElementsMarkListener<T> listener) { - myListeners.add(listener); + addElementsMarkListener(new ElementsMarkStateListenerAdapter<T>(listener)); } public void removeElementsMarkListener(ElementsMarkListener<T> listener) { - myListeners.remove(listener); - } - - public void addListSelectionListener(ListSelectionListener listener) { - myTable.getSelectionModel().addListSelectionListener(listener); - } - public void removeListSelectionListener(ListSelectionListener listener) { - myTable.getSelectionModel().removeListSelectionListener(listener); + removeElementsMarkListener(new ElementsMarkStateListenerAdapter<T>(listener)); } public void addElement(T element, final boolean isMarked) { - addElement(element, isMarked, element instanceof ElementProperties ? (ElementProperties)element : null); + addElement(element, getMarkState(isMarked)); } /** * Check if element is marked + * * @param element an element to test * @return true if element is marked */ public boolean isElementMarked(T element) { - final int elementRow = myTableModel.getElementRow(element); - return myTableModel.isElementMarked(elementRow); + return getElementMarkState(element); } /** - * Check if element is marked + * Update element mark + * * @param element an element to test - * @param marked a new value of mark. + * @param marked a new value of mark. */ public void setElementMarked(T element, boolean marked) { - final int elementRow = myTableModel.getElementRow(element); - myTableModel.setMarked(elementRow, marked); - } - - - public void removeElement(T element) { - final int elementRow = myTableModel.getElementRow(element); - if (elementRow < 0) { - return; // no such element - } - final boolean wasSelected = myTable.getSelectionModel().isSelectedIndex(elementRow); - - myTableModel.removeElement(element); - myElementToPropertiesMap.remove(element); - - if (wasSelected) { - final int rowCount = myTableModel.getRowCount(); - if (rowCount > 0) { - selectRow(elementRow % rowCount); - } - else { - myTable.getSelectionModel().clearSelection(); - } - } - myTable.requestFocus(); + setElementMarkState(element, getMarkState(marked)); } - public void removeAllElements() { - myTableModel.removeAllElements(); - myTable.getSelectionModel().clearSelection(); - } - - private void selectRow(final int row) { - myTable.getSelectionModel().setSelectionInterval(row, row); - myTable.scrollRectToVisible(myTable.getCellRect(row, 0, true)); - } - - public void moveElement(T element, int newRow) { - final int elementRow = myTableModel.getElementRow(element); - if (elementRow < 0 || elementRow == newRow || newRow < 0 || newRow >= myTableModel.getRowCount()) { - return; - } - final boolean wasSelected = myTable.getSelectionModel().isSelectedIndex(elementRow); - myTableModel.changeElementRow(element, newRow); - if (wasSelected) { - selectRow(newRow); - } - } - - public interface ElementProperties { - @Nullable - Icon getIcon(); - @Nullable - Color getColor(); - } public void addElement(T element, final boolean isMarked, ElementProperties elementProperties) { - myTableModel.addElement(element, isMarked); - myElementToPropertiesMap.put(element, elementProperties); - selectRow(myTableModel.getRowCount() - 1); - myTable.requestFocus(); - } - - public void setElementProperties(T element, ElementProperties properties) { - myElementToPropertiesMap.put(element, properties); + addElement(element, getMarkState(isMarked), elementProperties); } public void setElements(List<T> elements, boolean marked) { - myTableModel.clear(); - myTableModel.addElements(elements, marked); - } - - @Nullable - public T getSelectedElement() { - final int selectedRow = getSelectedElementRow(); - return selectedRow < 0? null : myTableModel.getElementAt(selectedRow); - } - - public int getSelectedElementRow() { - return myTable.getSelectedRow(); - } - - @NotNull - public List<T> getSelectedElements() { - final List<T> elements = new ArrayList<T>(); - final int[] selectedRows = myTable.getSelectedRows(); - for (int selectedRow : selectedRows) { - if (selectedRow < 0) { - continue; - } - elements.add(myTableModel.getElementAt(selectedRow)); - } - return elements; - } - - public void selectElements(Collection<? extends T> elements) { - if (elements.isEmpty()) { - myTable.clearSelection(); - return; - } - final int[] rows = getElementsRows(elements); - TableUtil.selectRows(myTable, rows); - TableUtil.scrollSelectionToVisible(myTable); - myTable.requestFocus(); - } - - private int[] getElementsRows(final Collection<? extends T> elements) { - final int[] rows = new int[elements.size()]; - int index = 0; - for (final T element : elements) { - rows[index++] = myTable.convertRowIndexToView(myTableModel.getElementRow(element)); - } - return rows; + setElements(elements, getMarkState(marked)); } public void markElements(Collection<T> elements) { - myTableModel.setMarked(getElementsRows(elements), true); + markElements(elements, Boolean.TRUE); } @NotNull public List<T> getMarkedElements() { - final int count = myTableModel.getRowCount(); + Map<T, Boolean> elementMarkStates = getElementMarkStates(); List<T> elements = new ArrayList<T>(); - for (int idx = 0; idx < count; idx++) { - final T element = myTableModel.getElementAt(idx); - if (myTableModel.isElementMarked(idx)) { - elements.add(element); + for (Map.Entry<T, Boolean> entry : elementMarkStates.entrySet()) { + if (entry.getValue()) { + elements.add(entry.getKey()); } } return elements; } - public void sort(Comparator<T> comparator) { - myTableModel.sort(comparator); - } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - myTable.setRowSelectionAllowed(enabled); - myTableModel.fireTableDataChanged(); - } - - public void stopEditing() { - TableCellEditor editor = myTable.getCellEditor(); - if (editor != null) { - editor.stopCellEditing(); - } - } - - public JComponent getComponent() { - return myTable; - } - public void invertSelection() { final int count = getElementCount(); for (int i = 0; i < count; i++) { @@ -401,264 +107,89 @@ public class ElementsChooser<T> extends JPanel implements ComponentWithEmptyText } public void setAllElementsMarked(boolean marked) { - final int[] rows = new int[myTableModel.getRowCount()]; - for (int idx = 0; idx < rows.length; idx++) { - rows[idx] = idx; - } - myTableModel.setMarked(rows, marked); - } - - private void notifyElementMarked(T element, boolean isMarked) { - for (ElementsMarkListener<T> listener : myListeners) { - listener.elementMarkChanged(element, isMarked); - } + setAllElementsMarked(getMarkState(marked)); } - public void clear() { - myTableModel.clear(); - myElementToPropertiesMap.clear(); + private static Boolean getMarkState(boolean marked) { + return marked; } - public int getElementCount() { - return myTableModel.getRowCount(); + @SuppressWarnings("unchecked") + private static <T> MarkStateDescriptor<T, Boolean> getMarkStateDescriptor() { + return MARK_STATE_DESCRIPTOR; } - public T getElementAt(int row) { - return myTableModel.getElementAt(row); - } - - public void disableElement(T element) { - myDisabledMap.put(element, Boolean.TRUE); - } - - private final class MyTableModel extends AbstractTableModel { - private final List<T> myElements = new ArrayList<T>(); - private final Map<T, Boolean> myMarkedMap = new HashMap<T, Boolean>(); - public final int CHECK_MARK_COLUM_INDEX; - public final int ELEMENT_COLUMN_INDEX; - private final boolean myElementsCanBeMarked; - - public MyTableModel(final boolean elementsCanBeMarked) { - myElementsCanBeMarked = elementsCanBeMarked; - if (elementsCanBeMarked) { - CHECK_MARK_COLUM_INDEX = 0; - ELEMENT_COLUMN_INDEX = 1; - } - else { - CHECK_MARK_COLUM_INDEX = -1; - ELEMENT_COLUMN_INDEX = 0; - } - } - - public void sort(Comparator<T> comparator) { - Collections.sort(myElements, comparator); - fireTableDataChanged(); - } - - public T getElementAt(int index) { - return myElements.get(index); - } - - public boolean isElementMarked(int index) { - final T element = myElements.get(index); - final Boolean isMarked = myMarkedMap.get(element); - return isMarked.booleanValue(); - } - - private void addElement(T element, boolean isMarked) { - myElements.add(element); - myMarkedMap.put(element, isMarked? Boolean.TRUE : Boolean.FALSE); - int row = myElements.size() - 1; - fireTableRowsInserted(row, row); - } - - private void addElements(@Nullable List<T> elements, boolean isMarked) { - if (elements == null || elements.isEmpty()) { - return; - } - for (final T element : elements) { - myElements.add(element); - myMarkedMap.put(element, isMarked ? Boolean.TRUE : Boolean.FALSE); - } - fireTableRowsInserted(myElements.size() - elements.size(), myElements.size() - 1); - } - - public void removeElement(T element) { - final boolean reallyRemoved = myElements.remove(element); - if (reallyRemoved) { - myMarkedMap.remove(element); - fireTableDataChanged(); - } - } - - public void changeElementRow(T element, int row) { - final boolean reallyRemoved = myElements.remove(element); - if (reallyRemoved) { - myElements.add(row, element); - fireTableDataChanged(); - } - } - - public int getElementRow(T element) { - return myElements.indexOf(element); - } - - public void removeAllElements() { - myElements.clear(); - fireTableDataChanged(); - } - - public void removeRows(int[] rows) { - final List<T> toRemove = new ArrayList<T>(); - for (int row : rows) { - final T element = myElements.get(row); - toRemove.add(element); - myMarkedMap.remove(element); - } - myElements.removeAll(toRemove); - fireTableDataChanged(); - } - + private static class BooleanMarkStateDescriptor<T> implements MarkStateDescriptor<T, Boolean> { + @NotNull @Override - public int getRowCount() { - return myElements.size(); + public Boolean getDefaultState(@NotNull T element) { + return Boolean.FALSE; } + @NotNull @Override - public int getColumnCount() { - return myElementsCanBeMarked? 2 : 1; + public Boolean getNextState(@NotNull T element, @NotNull Boolean state) { + return !state; } - @Override @Nullable - public Object getValueAt(int rowIndex, int columnIndex) { - T element = myElements.get(rowIndex); - if (columnIndex == ELEMENT_COLUMN_INDEX) { - return element; - } - if (columnIndex == CHECK_MARK_COLUM_INDEX) { - return myMarkedMap.get(element); - } - return null; - } - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - if (columnIndex == CHECK_MARK_COLUM_INDEX) { - setMarked(rowIndex, ((Boolean)aValue).booleanValue()); - } - } - - private void setMarked(int rowIndex, final boolean marked) { - final T element = myElements.get(rowIndex); - final Boolean newValue = marked? Boolean.TRUE : Boolean.FALSE; - final Boolean prevValue = myMarkedMap.put(element, newValue); - fireTableRowsUpdated(rowIndex, rowIndex); - if (!newValue.equals(prevValue)) { - notifyElementMarked(element, marked); - } - } - - private void setMarked(int[] rows, final boolean marked) { - if (rows == null || rows.length == 0) { - return; - } - int firstRow = Integer.MAX_VALUE; - int lastRow = Integer.MIN_VALUE; - final Boolean newValue = marked? Boolean.TRUE : Boolean.FALSE; - for (final int row : rows) { - final T element = myElements.get(row); - final Boolean prevValue = myMarkedMap.put(element, newValue); - if (!newValue.equals(prevValue)) { - notifyElementMarked(element, newValue.booleanValue()); + public Boolean getNextState(@NotNull Map<T, Boolean> elementsWithStates) { + boolean currentlyMarked = true; + for (Boolean state : elementsWithStates.values()) { + currentlyMarked = state; + if (!currentlyMarked) { + break; } - firstRow = Math.min(firstRow, row); - lastRow = Math.max(lastRow, row); } - fireTableRowsUpdated(firstRow, lastRow); + return !currentlyMarked; } @Override - public Class getColumnClass(int columnIndex) { - if (columnIndex == CHECK_MARK_COLUM_INDEX) { - return Boolean.class; - } - return super.getColumnClass(columnIndex); + public boolean isMarked(@NotNull Boolean state) { + return state; } + @Nullable @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - if (!isEnabled() || columnIndex != CHECK_MARK_COLUM_INDEX) { - return false; - } - final T o = (T)getValueAt(rowIndex, ELEMENT_COLUMN_INDEX); - return myDisabledMap.get(o) == null; + public Boolean getMarkState(@Nullable Object value) { + return value instanceof Boolean ? ((Boolean)value) : null; } - public void clear() { - myElements.clear(); - myMarkedMap.clear(); - fireTableDataChanged(); + @Nullable + @Override + public TableCellRenderer getMarkRenderer() { + return null; } } - protected String getItemText(@NotNull T value) { - return value.toString(); - } + private static class ElementsMarkStateListenerAdapter<T> implements ElementsMarkStateListener<T, Boolean> { + private final ElementsMarkListener<T> myListener; - @Nullable - protected Icon getItemIcon(@NotNull T value) { - return null; - } + public ElementsMarkStateListenerAdapter(ElementsMarkListener<T> listener) { + myListener = listener; + } - private class MyElementColumnCellRenderer extends DefaultTableCellRenderer { @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - final Color color = UIUtil.getTableFocusCellBackground(); - Component component; - T t = (T)value; - try { - UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, table.getSelectionBackground()); - component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - setText(t != null ? getItemText(t) : ""); - if (component instanceof JLabel) { - ((JLabel)component).setBorder(noFocusBorder); - } - } - finally { - UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, color); - } - final MyTableModel model = (MyTableModel)table.getModel(); - component.setEnabled(ElementsChooser.this.isEnabled() && (!myColorUnmarkedElements || model.isElementMarked(row))); - final ElementProperties properties = myElementToPropertiesMap.get(t); - if (component instanceof JLabel) { - final Icon icon = properties != null ? properties.getIcon() : t != null ? getItemIcon(t) : null; - JLabel label = (JLabel)component; - label.setIcon(icon); - label.setDisabledIcon(icon); - } - component.setForeground(properties != null && properties.getColor() != null ? - properties.getColor() : - isSelected ? table.getSelectionForeground() : table.getForeground()); - return component; + public void elementMarkChanged(T element, Boolean markState) { + myListener.elementMarkChanged(element, markState); } - } - private class CheckMarkColumnCellRenderer implements TableCellRenderer { - private final TableCellRenderer myDelegate; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ElementsMarkStateListenerAdapter that = (ElementsMarkStateListenerAdapter)o; - public CheckMarkColumnCellRenderer(TableCellRenderer delegate) { - myDelegate = delegate; + if (!myListener.equals(that.myListener)) return false; + + return true; } @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Component component = myDelegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - component.setEnabled(isEnabled()); - if (component instanceof JComponent) { - ((JComponent)component).setBorder(null); - } - return component; + public int hashCode() { + return myListener.hashCode(); } } } diff --git a/platform/platform-impl/src/com/intellij/ide/util/MultiStateElementsChooser.java b/platform/platform-impl/src/com/intellij/ide/util/MultiStateElementsChooser.java new file mode 100644 index 000000000000..bccbd7b144ac --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/util/MultiStateElementsChooser.java @@ -0,0 +1,692 @@ +/* + * 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.util; + +import com.intellij.ui.*; +import com.intellij.ui.table.JBTable; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.ComponentWithEmptyText; +import com.intellij.util.ui.StatusText; +import com.intellij.util.ui.Table; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.*; +import java.util.List; + +public class MultiStateElementsChooser<T, S> extends JPanel implements ComponentWithEmptyText, ComponentWithExpandableItems<TableCell> { + private MarkStateDescriptor<T, S> myMarkStateDescriptor; + private JBTable myTable = null; + private MyTableModel myTableModel = null; + private boolean myColorUnmarkedElements = true; + private final List<ElementsMarkStateListener<T, S>> myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); + private final Map<T,ElementProperties> myElementToPropertiesMap = new HashMap<T, ElementProperties>(); + private final Map<T, Boolean> myDisabledMap = new HashMap<T, Boolean>(); + + public interface ElementsMarkStateListener<T, S> { + void elementMarkChanged(T element, S markState); + } + + public interface MarkStateDescriptor<T, S> { + @NotNull + S getDefaultState(@NotNull T element); + + @NotNull + S getNextState(@NotNull T element, @NotNull S state); + + @Nullable + S getNextState(@NotNull Map<T, S> elementsWithStates); + + boolean isMarked(@NotNull S state); + + @Nullable + S getMarkState(@Nullable Object value); + + @Nullable + TableCellRenderer getMarkRenderer(); + } + + public MultiStateElementsChooser(final boolean elementsCanBeMarked, MarkStateDescriptor<T, S> markStateDescriptor) { + this(null, null, elementsCanBeMarked, markStateDescriptor); + } + + public MultiStateElementsChooser(List<T> elements, S markState, MarkStateDescriptor<T, S> markStateDescriptor) { + this(elements, markState, true, markStateDescriptor); + } + + private MultiStateElementsChooser(@Nullable List<T> elements, + S markState, + boolean elementsCanBeMarked, + MarkStateDescriptor<T, S> markStateDescriptor) { + super(new BorderLayout()); + + myMarkStateDescriptor = markStateDescriptor; + + myTableModel = new MyTableModel(elementsCanBeMarked); + myTable = new Table(myTableModel); + myTable.setShowGrid(false); + myTable.setIntercellSpacing(new Dimension(0, 0)); + myTable.setTableHeader(null); + myTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + myTable.setColumnSelectionAllowed(false); + JScrollPane pane = ScrollPaneFactory.createScrollPane(myTable); + pane.setPreferredSize(new Dimension(100, 155)); + TableColumnModel columnModel = myTable.getColumnModel(); + + if (elementsCanBeMarked) { + TableColumn checkMarkColumn = columnModel.getColumn(myTableModel.CHECK_MARK_COLUM_INDEX); + TableUtil.setupCheckboxColumn(checkMarkColumn); + TableCellRenderer checkMarkRenderer = myMarkStateDescriptor.getMarkRenderer(); + if (checkMarkRenderer == null) { + checkMarkRenderer = new CheckMarkColumnCellRenderer(myTable.getDefaultRenderer(Boolean.class)); + } + checkMarkColumn.setCellRenderer(checkMarkRenderer); + } + columnModel.getColumn(myTableModel.ELEMENT_COLUMN_INDEX).setCellRenderer(new MyElementColumnCellRenderer()); + + add(pane, BorderLayout.CENTER); + myTable.registerKeyboardAction( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + final int[] selectedRows = myTable.getSelectedRows(); + Map<T, S> selectedElements = new LinkedHashMap<T, S>(selectedRows.length); + for (int selectedRow : selectedRows) { + selectedElements.put(myTableModel.getElementAt(selectedRow), myTableModel.getElementMarkState(selectedRow)); + } + S nextState = myMarkStateDescriptor.getNextState(selectedElements); + if (nextState != null) { + myTableModel.setMarkState(selectedRows, nextState); + } + } + }, + KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), + JComponent.WHEN_FOCUSED + ); + + final SpeedSearchBase<JBTable> speedSearch = new SpeedSearchBase<JBTable>(myTable) { + @Override + public int getSelectedIndex() { + return myTable.getSelectedRow(); + } + + @Override + protected int convertIndexToModel(int viewIndex) { + return myTable.convertRowIndexToModel(viewIndex); + } + + @Override + public Object[] getAllElements() { + final int count = myTableModel.getRowCount(); + Object[] elements = new Object[count]; + for (int idx = 0; idx < count; idx++) { + elements[idx] = myTableModel.getElementAt(idx); + } + return elements; + } + + @Override + public String getElementText(Object element) { + return getItemText((T)element); + } + + @Override + public void selectElement(Object element, String selectedText) { + final int count = myTableModel.getRowCount(); + for (int row = 0; row < count; row++) { + if (element.equals(myTableModel.getElementAt(row))) { + final int viewRow = myTable.convertRowIndexToView(row); + myTable.getSelectionModel().setSelectionInterval(viewRow, viewRow); + TableUtil.scrollSelectionToVisible(myTable); + break; + } + } + } + }; + speedSearch.setComparator(new SpeedSearchComparator(false)); + setElements(elements, markState); + installActions(myTable); + } + + private static void installActions(JTable table) { + InputMap inputMap = table.getInputMap(WHEN_FOCUSED); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), "selectLastRow"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), "selectFirstRow"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.SHIFT_DOWN_MASK), "selectFirstRowExtendSelection"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.SHIFT_DOWN_MASK), "selectLastRowExtendSelection"); + } + + @NotNull + @Override + public StatusText getEmptyText() { + return myTable.getEmptyText(); + } + + @NotNull + @Override + public ExpandableItemsHandler<TableCell> getExpandableItemsHandler() { + return myTable.getExpandableItemsHandler(); + } + + @Override + public void setExpandableItemsEnabled(boolean enabled) { + myTable.setExpandableItemsEnabled(enabled); + } + + public void setSingleSelectionMode() { + myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } + + public void refresh() { + myTableModel.fireTableDataChanged(); + } + + public void refresh(T element) { + final int row = myTableModel.getElementRow(element); + if (row >= 0) { + myTableModel.fireTableRowsUpdated(row, row); + } + } + + private int[] mySavedSelection = null; + public void saveSelection() { + mySavedSelection = myTable.getSelectedRows(); + } + + public void restoreSelection() { + if (mySavedSelection != null) { + TableUtil.selectRows(myTable, mySavedSelection); + mySavedSelection = null; + } + } + + public boolean isColorUnmarkedElements() { + return myColorUnmarkedElements; + } + + public void setColorUnmarkedElements(boolean colorUnmarkedElements) { + myColorUnmarkedElements = colorUnmarkedElements; + } + + public void addElementsMarkListener(ElementsMarkStateListener<T, S> listener) { + myListeners.add(listener); + } + + public void removeElementsMarkListener(ElementsMarkStateListener<T, S> listener) { + myListeners.remove(listener); + } + + public void addListSelectionListener(ListSelectionListener listener) { + myTable.getSelectionModel().addListSelectionListener(listener); + } + public void removeListSelectionListener(ListSelectionListener listener) { + myTable.getSelectionModel().removeListSelectionListener(listener); + } + + public void addElement(T element, final S markState) { + addElement(element, markState, element instanceof ElementProperties ? (ElementProperties)element : null); + } + + /** + * Gets element mark state + * @param element an element to test + * @return state of element + */ + public S getElementMarkState(T element) { + final int elementRow = myTableModel.getElementRow(element); + return myTableModel.getElementMarkState(elementRow); + } + + /** + * Update element mark state + * @param element an element to test + * @param markState a new value of mark state + */ + public void setElementMarkState(T element, S markState) { + final int elementRow = myTableModel.getElementRow(element); + myTableModel.setMarkState(elementRow, markState); + } + + + public void removeElement(T element) { + final int elementRow = myTableModel.getElementRow(element); + if (elementRow < 0) { + return; // no such element + } + final boolean wasSelected = myTable.getSelectionModel().isSelectedIndex(elementRow); + + myTableModel.removeElement(element); + myElementToPropertiesMap.remove(element); + + if (wasSelected) { + final int rowCount = myTableModel.getRowCount(); + if (rowCount > 0) { + selectRow(elementRow % rowCount); + } + else { + myTable.getSelectionModel().clearSelection(); + } + } + myTable.requestFocus(); + } + + public void removeAllElements() { + myTableModel.removeAllElements(); + myTable.getSelectionModel().clearSelection(); + } + + private void selectRow(final int row) { + myTable.getSelectionModel().setSelectionInterval(row, row); + myTable.scrollRectToVisible(myTable.getCellRect(row, 0, true)); + } + + public void moveElement(T element, int newRow) { + final int elementRow = myTableModel.getElementRow(element); + if (elementRow < 0 || elementRow == newRow || newRow < 0 || newRow >= myTableModel.getRowCount()) { + return; + } + final boolean wasSelected = myTable.getSelectionModel().isSelectedIndex(elementRow); + myTableModel.changeElementRow(element, newRow); + if (wasSelected) { + selectRow(newRow); + } + } + + public interface ElementProperties { + @Nullable + Icon getIcon(); + @Nullable + Color getColor(); + } + + public void addElement(T element, final S markState, ElementProperties elementProperties) { + myTableModel.addElement(element, markState); + myElementToPropertiesMap.put(element, elementProperties); + selectRow(myTableModel.getRowCount() - 1); + myTable.requestFocus(); + } + + public void setElementProperties(T element, ElementProperties properties) { + myElementToPropertiesMap.put(element, properties); + } + + public void setElements(List<T> elements, S markState) { + myTableModel.clear(); + myTableModel.addElements(elements, markState); + } + + @Nullable + public T getSelectedElement() { + final int selectedRow = getSelectedElementRow(); + return selectedRow < 0? null : myTableModel.getElementAt(selectedRow); + } + + public int getSelectedElementRow() { + return myTable.getSelectedRow(); + } + + @NotNull + public List<T> getSelectedElements() { + final List<T> elements = new ArrayList<T>(); + final int[] selectedRows = myTable.getSelectedRows(); + for (int selectedRow : selectedRows) { + if (selectedRow < 0) { + continue; + } + elements.add(myTableModel.getElementAt(selectedRow)); + } + return elements; + } + + public void selectElements(Collection<? extends T> elements) { + if (elements.isEmpty()) { + myTable.clearSelection(); + return; + } + final int[] rows = getElementsRows(elements); + TableUtil.selectRows(myTable, rows); + TableUtil.scrollSelectionToVisible(myTable); + myTable.requestFocus(); + } + + private int[] getElementsRows(final Collection<? extends T> elements) { + final int[] rows = new int[elements.size()]; + int index = 0; + for (final T element : elements) { + rows[index++] = myTable.convertRowIndexToView(myTableModel.getElementRow(element)); + } + return rows; + } + + public void markElements(Collection<T> elements, S markState) { + myTableModel.setMarkState(getElementsRows(elements), markState); + } + + @NotNull + public Map<T, S> getElementMarkStates() { + final int count = myTableModel.getRowCount(); + Map<T, S> elements = new LinkedHashMap<T, S>(); + for (int idx = 0; idx < count; idx++) { + final T element = myTableModel.getElementAt(idx); + elements.put(element, myTableModel.getElementMarkState(idx)); + } + return elements; + } + + public void sort(Comparator<T> comparator) { + myTableModel.sort(comparator); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + myTable.setRowSelectionAllowed(enabled); + myTableModel.fireTableDataChanged(); + } + + public void stopEditing() { + TableCellEditor editor = myTable.getCellEditor(); + if (editor != null) { + editor.stopCellEditing(); + } + } + + public JComponent getComponent() { + return myTable; + } + + public void setAllElementsMarked(S markState) { + final int[] rows = new int[myTableModel.getRowCount()]; + for (int idx = 0; idx < rows.length; idx++) { + rows[idx] = idx; + } + myTableModel.setMarkState(rows, markState); + } + + private void notifyElementMarked(T element, S markState) { + for (ElementsMarkStateListener<T, S> listener : myListeners) { + listener.elementMarkChanged(element, markState); + } + } + + public void clear() { + myTableModel.clear(); + myElementToPropertiesMap.clear(); + } + + public int getElementCount() { + return myTableModel.getRowCount(); + } + + public T getElementAt(int row) { + return myTableModel.getElementAt(row); + } + + public void disableElement(T element) { + myDisabledMap.put(element, Boolean.TRUE); + } + + private final class MyTableModel extends AbstractTableModel { + private final List<T> myElements = new ArrayList<T>(); + private final Map<T, S> myMarkedMap = new HashMap<T, S>(); + public final int CHECK_MARK_COLUM_INDEX; + public final int ELEMENT_COLUMN_INDEX; + private final boolean myElementsCanBeMarked; + + public MyTableModel(final boolean elementsCanBeMarked) { + myElementsCanBeMarked = elementsCanBeMarked; + if (elementsCanBeMarked) { + CHECK_MARK_COLUM_INDEX = 0; + ELEMENT_COLUMN_INDEX = 1; + } + else { + CHECK_MARK_COLUM_INDEX = -1; + ELEMENT_COLUMN_INDEX = 0; + } + } + + public void sort(Comparator<T> comparator) { + Collections.sort(myElements, comparator); + fireTableDataChanged(); + } + + public T getElementAt(int index) { + return myElements.get(index); + } + + public S getElementMarkState(int index) { + final T element = myElements.get(index); + return myMarkedMap.get(element); + } + + private void addElement(T element, S markState) { + myElements.add(element); + myMarkedMap.put(element, notNullMarkState(element, markState)); + int row = myElements.size() - 1; + fireTableRowsInserted(row, row); + } + + private void addElements(@Nullable List<T> elements, S markState) { + if (elements == null || elements.isEmpty()) { + return; + } + for (final T element : elements) { + myElements.add(element); + myMarkedMap.put(element, notNullMarkState(element, markState)); + } + fireTableRowsInserted(myElements.size() - elements.size(), myElements.size() - 1); + } + + public void removeElement(T element) { + final boolean reallyRemoved = myElements.remove(element); + if (reallyRemoved) { + myMarkedMap.remove(element); + fireTableDataChanged(); + } + } + + public void changeElementRow(T element, int row) { + final boolean reallyRemoved = myElements.remove(element); + if (reallyRemoved) { + myElements.add(row, element); + fireTableDataChanged(); + } + } + + public int getElementRow(T element) { + return myElements.indexOf(element); + } + + public void removeAllElements() { + myElements.clear(); + fireTableDataChanged(); + } + + public void removeRows(int[] rows) { + final List<T> toRemove = new ArrayList<T>(); + for (int row : rows) { + final T element = myElements.get(row); + toRemove.add(element); + myMarkedMap.remove(element); + } + myElements.removeAll(toRemove); + fireTableDataChanged(); + } + + @Override + public int getRowCount() { + return myElements.size(); + } + + @Override + public int getColumnCount() { + return myElementsCanBeMarked? 2 : 1; + } + + @Override + @Nullable + public Object getValueAt(int rowIndex, int columnIndex) { + T element = myElements.get(rowIndex); + if (columnIndex == ELEMENT_COLUMN_INDEX) { + return element; + } + if (columnIndex == CHECK_MARK_COLUM_INDEX) { + return myMarkedMap.get(element); + } + return null; + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + if (columnIndex == CHECK_MARK_COLUM_INDEX) { + S nextState = myMarkStateDescriptor.getMarkState(aValue); + if (nextState == null) { + T element = myTableModel.getElementAt(rowIndex); + S currentState = myTableModel.getElementMarkState(rowIndex); + nextState = myMarkStateDescriptor.getNextState(element, currentState); + } + setMarkState(rowIndex, nextState); + } + } + + private void setMarkState(int rowIndex, final S markState) { + final T element = myElements.get(rowIndex); + final S newValue = notNullMarkState(element, markState); + final S prevValue = myMarkedMap.put(element, newValue); + fireTableRowsUpdated(rowIndex, rowIndex); + if (!newValue.equals(prevValue)) { + notifyElementMarked(element, newValue); + } + } + + private void setMarkState(int[] rows, final S markState) { + if (rows == null || rows.length == 0) { + return; + } + int firstRow = Integer.MAX_VALUE; + int lastRow = Integer.MIN_VALUE; + for (final int row : rows) { + final T element = myElements.get(row); + final S newValue = notNullMarkState(element, markState); + final S prevValue = myMarkedMap.put(element, newValue); + if (!newValue.equals(prevValue)) { + notifyElementMarked(element, newValue); + } + firstRow = Math.min(firstRow, row); + lastRow = Math.max(lastRow, row); + } + fireTableRowsUpdated(firstRow, lastRow); + } + + @NotNull + private S notNullMarkState(T element, S markState) { + return markState != null ? markState : myMarkStateDescriptor.getDefaultState(element); + } + + @Override + public Class getColumnClass(int columnIndex) { + if (columnIndex == CHECK_MARK_COLUM_INDEX) { + return Boolean.class; + } + return super.getColumnClass(columnIndex); + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + if (!isEnabled() || columnIndex != CHECK_MARK_COLUM_INDEX) { + return false; + } + final T o = (T)getValueAt(rowIndex, ELEMENT_COLUMN_INDEX); + return myDisabledMap.get(o) == null; + } + + public void clear() { + myElements.clear(); + myMarkedMap.clear(); + fireTableDataChanged(); + } + } + + protected String getItemText(@NotNull T value) { + return value.toString(); + } + + @Nullable + protected Icon getItemIcon(@NotNull T value) { + return null; + } + + private class MyElementColumnCellRenderer extends DefaultTableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + final Color color = UIUtil.getTableFocusCellBackground(); + Component component; + T t = (T)value; + try { + UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, table.getSelectionBackground()); + component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setText(t != null ? getItemText(t) : ""); + if (component instanceof JLabel) { + ((JLabel)component).setBorder(noFocusBorder); + } + } + finally { + UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, color); + } + final MyTableModel model = (MyTableModel)table.getModel(); + component.setEnabled(MultiStateElementsChooser.this.isEnabled() && + (!myColorUnmarkedElements || myMarkStateDescriptor.isMarked(model.getElementMarkState(row)))); + final ElementProperties properties = myElementToPropertiesMap.get(t); + if (component instanceof JLabel) { + final Icon icon = properties != null ? properties.getIcon() : t != null ? getItemIcon(t) : null; + JLabel label = (JLabel)component; + label.setIcon(icon); + label.setDisabledIcon(icon); + } + component.setForeground(properties != null && properties.getColor() != null ? + properties.getColor() : + isSelected ? table.getSelectionForeground() : table.getForeground()); + return component; + } + } + + private class CheckMarkColumnCellRenderer implements TableCellRenderer { + private final TableCellRenderer myDelegate; + + public CheckMarkColumnCellRenderer(TableCellRenderer delegate) { + myDelegate = delegate; + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component component = myDelegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + component.setEnabled(isEnabled()); + if (component instanceof JComponent) { + ((JComponent)component).setBorder(null); + } + return component; + } + } +} diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/ideSettings/IdeSettingsStatisticsUtils.java b/platform/platform-impl/src/com/intellij/internal/statistic/ideSettings/IdeSettingsStatisticsUtils.java index 897491532833..2873ed3a222a 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/ideSettings/IdeSettingsStatisticsUtils.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/ideSettings/IdeSettingsStatisticsUtils.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. @@ -21,12 +21,11 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.Function; +import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.hash.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; import java.util.Set; @@ -60,19 +59,11 @@ public class IdeSettingsStatisticsUtils { @Nullable private static Object getPropertyValue(Object componentInstance, String propertyName) { final Class<? extends Object> componentInstanceClass = componentInstance.getClass(); - Object propertyValue = null; - try { - Field field = componentInstanceClass.getDeclaredField(propertyName); - propertyValue = field.get(componentInstance); - } - catch (NoSuchFieldException ignored) { - } - catch (IllegalAccessException ignored) { - } + Object propertyValue = ReflectionUtil.getField(componentInstanceClass, componentInstance, null, propertyName); if (propertyValue == null) { - Method method = getMethod(componentInstanceClass, "get" + StringUtil.capitalize(propertyName)); + Method method = ReflectionUtil.getMethod(componentInstanceClass, "get" + StringUtil.capitalize(propertyName)); if (method == null) { - method = getMethod(componentInstanceClass, "is" + StringUtil.capitalize(propertyName)); + method = ReflectionUtil.getMethod(componentInstanceClass, "is" + StringUtil.capitalize(propertyName)); } if (method != null) { try { @@ -85,16 +76,6 @@ public class IdeSettingsStatisticsUtils { return propertyValue; } - @Nullable - private static Method getMethod(@NotNull Class componentInstanceClass, @NotNull String name) { - try { - return componentInstanceClass.getMethod(name); - } - catch (NoSuchMethodException ignored) { - } - return null; - } - private static String getUsageDescriptorKey(@NotNull String providerName, @NotNull String name, @NotNull String value) { final String shortName = StringUtil.getShortName(providerName); return shortName + "#" + name + "(" + value + ")"; diff --git a/platform/platform-impl/src/com/intellij/notification/impl/ui/NotificationsConfigurablePanel.java b/platform/platform-impl/src/com/intellij/notification/impl/ui/NotificationsConfigurablePanel.java index 73af2c304beb..2d6a768a92c2 100644 --- a/platform/platform-impl/src/com/intellij/notification/impl/ui/NotificationsConfigurablePanel.java +++ b/platform/platform-impl/src/com/intellij/notification/impl/ui/NotificationsConfigurablePanel.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. @@ -22,6 +22,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.ui.ComboBoxTableRenderer; import com.intellij.openapi.ui.StripeTable; import com.intellij.openapi.util.SystemInfo; +import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.TableSpeedSearch; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.UIUtil; @@ -30,6 +31,7 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.border.LineBorder; import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import java.awt.*; import java.awt.event.ActionEvent; @@ -126,6 +128,22 @@ public class NotificationsConfigurablePanel extends JPanel implements Disposable final TableColumn idColumn = getColumnModel().getColumn(ID_COLUMN); idColumn.setPreferredWidth(200); + idColumn.setCellRenderer(new DefaultTableCellRenderer() { + @NotNull + @Override + public Component getTableCellRendererComponent(@NotNull JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (component instanceof JComponent) { + ((JComponent)component).setBorder(IdeBorderFactory.createEmptyBorder(0, 4, 0, 4)); + } + return component; + } + }); final TableColumn displayTypeColumn = getColumnModel().getColumn(DISPLAY_TYPE_COLUMN); displayTypeColumn.setMaxWidth(300); diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java index ed40589a7d20..8120b0644675 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.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. @@ -22,7 +22,6 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.AppUIUtil; import com.intellij.util.PlatformUtils; -import com.intellij.util.SystemProperties; import com.intellij.util.ThreeState; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -423,15 +422,12 @@ public class ConfigImportHelper { dir = dir.substring(1, dir.length() - 1); } if (replaceUserHome) { - if (dir.startsWith("~\\") || dir.startsWith("~//") || StringUtil.startsWithConcatenation(dir, "~", File.separator)) { - dir = SystemProperties.getUserHome() + dir.substring(1); - } + dir = FileUtil.expandUserHome(dir); } return dir; } - public static boolean isInstallationHomeOrConfig(@NotNull final String installationHome, - @NotNull final ConfigImportSettings settings) { + public static boolean isInstallationHomeOrConfig(@NotNull final String installationHome, @NotNull final ConfigImportSettings settings) { if (new File(installationHome, OPTIONS_XML).exists()) return true; if (new File(installationHome, CONFIG_RELATED_PATH + OPTIONS_XML).exists()) return true; diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportSettings.java b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportSettings.java index b0d4d3f909c2..005c006d1717 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportSettings.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportSettings.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,8 +15,7 @@ */ package com.intellij.openapi.application; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.util.SystemProperties; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.ThreeState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -74,10 +73,7 @@ public class ConfigImportSettings { } protected String getAutoImportLabel(File guessedOldConfig) { - String path = guessedOldConfig.getAbsolutePath(); - if (SystemInfo.isUnix) { - path = path.replace(SystemProperties.getUserHome(), "~"); - } + String path = FileUtil.getLocationRelativeToUserHome(guessedOldConfig.getAbsolutePath()); return ApplicationBundle.message("radio.import.auto", path); } diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java index bf7fe9c9652f..9f43164cf9d4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java @@ -20,7 +20,6 @@ import com.intellij.ide.DataManager; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.*; @@ -96,11 +95,11 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap } public static int getGlobalUndoLimit() { - return Registry.intValue("undo.globalUndoLimit", 10); + return Registry.intValue("undo.globalUndoLimit"); } public static int getDocumentUndoLimit() { - return Registry.intValue("undo.documentUndoLimit", 100); + return Registry.intValue("undo.documentUndoLimit"); } public UndoManagerImpl(Application application, CommandProcessor commandProcessor) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/actions/CompareClipboardWithSelection.java b/platform/platform-impl/src/com/intellij/openapi/diff/actions/CompareClipboardWithSelection.java index a916d9e5bb34..372b14efcfe6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/actions/CompareClipboardWithSelection.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/actions/CompareClipboardWithSelection.java @@ -73,8 +73,9 @@ public class CompareClipboardWithSelection extends BaseDiffAction { SelectionModel selectionModel = myEditor.getSelectionModel(); if (selectionModel.hasSelection()) { TextRange range = new TextRange(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); + boolean forceReadOnly = myEditor.isViewer(); myContents[1] = new FragmentContent(DiffContent.fromDocument(getProject(), getDocument()), - range, getProject(), getDocumentFile(getDocument())); + range, getProject(), getDocumentFile(getDocument()), forceReadOnly); } else { myContents [1] = DiffContent.fromDocument(getProject(), getDocument()); 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 538058d2d190..ec7a316357e2 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 @@ -23,6 +23,8 @@ import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.DialogWrapperDialog; import com.intellij.openapi.util.Disposer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.awt.*; @@ -56,25 +58,26 @@ public class DiffPanelOptions { myShowSourcePolicy = showSourcePolicy; } - public void showSource(OpenFileDescriptor descriptor) { + public void showSource(@Nullable OpenFileDescriptor descriptor) { + if (descriptor == null || myDiffPanel.getProject() == null) return; myShowSourcePolicy.showSource(descriptor, myDiffPanel); } public interface ShowSourcePolicy { - void showSource(OpenFileDescriptor descriptor, DiffPanelImpl diffPanel); + void showSource(@NotNull OpenFileDescriptor descriptor, @NotNull DiffPanelImpl diffPanel); ShowSourcePolicy DONT_SHOW = new ShowSourcePolicy() { - public void showSource(OpenFileDescriptor descriptor, DiffPanelImpl diffPanel) {} + public void showSource(@NotNull OpenFileDescriptor descriptor, @NotNull DiffPanelImpl diffPanel) {} }; ShowSourcePolicy OPEN_EDITOR = new ShowSourcePolicy() { - public void showSource(OpenFileDescriptor descriptor, DiffPanelImpl diffPanel) { + public void showSource(@NotNull OpenFileDescriptor descriptor, @NotNull DiffPanelImpl diffPanel) { FileEditorManager.getInstance(diffPanel.getProject()).openTextEditor(descriptor, true); } }; ShowSourcePolicy OPEN_EDITOR_AND_CLOSE_DIFF = new ShowSourcePolicy() { - public void showSource(OpenFileDescriptor descriptor, DiffPanelImpl diffPanel) { + public void showSource(@NotNull OpenFileDescriptor descriptor, @NotNull DiffPanelImpl diffPanel) { OPEN_EDITOR.showSource(descriptor, diffPanel); if (diffPanel.getOwnerWindow() == null) return; Disposer.dispose(diffPanel); @@ -97,7 +100,7 @@ public class DiffPanelOptions { }; ShowSourcePolicy DEFAULT = new ShowSourcePolicy() { - public void showSource(OpenFileDescriptor descriptor, DiffPanelImpl diffPanel) { + public void showSource(@NotNull OpenFileDescriptor descriptor, @NotNull DiffPanelImpl diffPanel) { Window window = diffPanel.getOwnerWindow(); if (window == null) return; else if (window instanceof Frame) OPEN_EDITOR.showSource(descriptor, diffPanel); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java index 5f169146145c..8ea3242d9361 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java @@ -73,7 +73,6 @@ import com.intellij.util.LineSeparator; import com.intellij.util.containers.CacheOneStepIterator; import com.intellij.util.diff.FilesTooBigForDiffException; import com.intellij.util.ui.PlatformColors; -import com.intellij.util.ui.UIUtil; import gnu.trove.TIntFunction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -160,8 +159,8 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid myOwnerWindow = owner; myIsSyncScroll = true; final boolean v = !horizontal; - myLeftSide = new DiffSideView(this, new CustomLineBorder(UIUtil.getBorderColor(), 1, 0, v ? 0 : 1, v ? 0 : 1)); - myRightSide = new DiffSideView(this, new CustomLineBorder(UIUtil.getBorderColor(), v ? 0 : 1, v ? 0 : 1, 1, 0)); + myLeftSide = new DiffSideView(this, new CustomLineBorder(1, 0, v ? 0 : 1, v ? 0 : 1)); + myRightSide = new DiffSideView(this, new CustomLineBorder(v ? 0 : 1, v ? 0 : 1, 1, 0)); myLeftSide.becomeMaster(); myDiffUpdater = new Rediffers(this); @@ -298,13 +297,11 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid myData.setContents(content1, content2); Project project = myData.getProject(); FileType[] types = DiffUtil.chooseContentTypes(new DiffContent[]{content1, content2}); - VirtualFile baseFile = content1.getFile(); - if (baseFile == null && myDiffRequest != null) { - String path = myDiffRequest.getWindowTitle(); - if (path != null) baseFile = LocalFileSystem.getInstance().findFileByPath(path); - } - myLeftSide.setHighlighterFactory(createHighlighter(types[0], baseFile, project)); - myRightSide.setHighlighterFactory(createHighlighter(types[1], baseFile, project)); + VirtualFile beforeFile = content1.getFile(); + VirtualFile afterFile = content2.getFile(); + String path = myDiffRequest == null ? null : myDiffRequest.getWindowTitle(); + myLeftSide.setHighlighterFactory(createHighlighter(types[0], beforeFile, afterFile, path, project)); + myRightSide.setHighlighterFactory(createHighlighter(types[1], afterFile, beforeFile, path, project)); setSplitterProportion(content1, content2); rediff(); if (myIsRequestFocus) { @@ -344,8 +341,16 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid } } // todo pay attention here - private static DiffHighlighterFactory createHighlighter(FileType contentType, VirtualFile file, Project project) { - return new DiffHighlighterFactoryImpl(contentType, file, project); + private static DiffHighlighterFactory createHighlighter(FileType contentType, + VirtualFile file, + VirtualFile otherFile, + String path, + Project project) { + VirtualFile baseFile = file; + if (baseFile == null) baseFile = otherFile; + if (baseFile == null && path != null) baseFile = LocalFileSystem.getInstance().findFileByPath(path); + + return new DiffHighlighterFactoryImpl(contentType, baseFile, project); } void rediff() { @@ -607,7 +612,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid return myData.getProject(); } - public void showSource(OpenFileDescriptor descriptor) { + public void showSource(@Nullable OpenFileDescriptor descriptor) { myOptions.showSource(descriptor); } @@ -1004,10 +1009,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid @Override public void navigate(boolean requestFocus) { - final OpenFileDescriptor descriptor = mySide.getCurrentOpenFileDescriptor(); - if (descriptor != null) { - showSource(descriptor); - } + showSource(mySide.getCurrentOpenFileDescriptor()); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffSplitter.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffSplitter.java index 5f4507dcfad3..4b6a289d476e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffSplitter.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffSplitter.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,6 +19,7 @@ import com.intellij.openapi.diff.impl.highlighting.DiffPanelState; import com.intellij.openapi.diff.impl.splitter.DiffDividerPaint; import com.intellij.openapi.editor.event.VisibleAreaEvent; import com.intellij.openapi.editor.event.VisibleAreaListener; +import com.intellij.openapi.ui.Divider; import com.intellij.openapi.ui.Splitter; import javax.swing.*; @@ -43,8 +44,8 @@ class DiffSplitter extends Splitter implements DiffSplitterI { setHonorComponentsMinimumSize(false); } - protected Splitter.Divider createDivider() { - return new Divider(){ + protected Divider createDivider() { + return new DividerImpl(){ public void paint(Graphics g) { super.paint(g); myPaint.paint(g, this); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index 10362cc8b304..cbad541fe129 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -27,9 +27,7 @@ import com.intellij.openapi.diff.*; import com.intellij.openapi.diff.actions.NextDiffAction; import com.intellij.openapi.diff.actions.PreviousDiffAction; import com.intellij.openapi.diff.actions.ToggleAutoScrollAction; -import com.intellij.openapi.diff.impl.DiffUtil; -import com.intellij.openapi.diff.impl.EditingSides; -import com.intellij.openapi.diff.impl.GenericDataProvider; +import com.intellij.openapi.diff.impl.*; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.diff.impl.incrementalMerge.ChangeCounter; import com.intellij.openapi.diff.impl.incrementalMerge.ChangeList; @@ -49,16 +47,20 @@ import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.EditorMarkupModel; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogBuilder; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.LabeledComponent; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.EditorNotificationPanel; -import com.intellij.util.diff.FilesTooBigForDiffException; +import com.intellij.util.containers.Convertor; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -141,9 +143,6 @@ public class MergePanel2 implements DiffViewer { private DiffRequest.ToolbarAddons createToolbar() { return new DiffRequest.ToolbarAddons() { public void customize(DiffToolbar toolbar) { - ActionManager actionManager = ActionManager.getInstance(); - toolbar.addAction(actionManager.getAction(IdeActions.ACTION_COPY)); - toolbar.addAction(actionManager.getAction(IdeActions.ACTION_FIND)); toolbar.addAction(PreviousDiffAction.find()); toolbar.addAction(NextDiffAction.find()); toolbar.addSeparator(); @@ -235,6 +234,8 @@ public class MergePanel2 implements DiffViewer { Editor base = getEditor(1); Editor right = getEditor(2); + setupHighlighterSettings(left, base, right); + myMergeList.setMarkups(left, base, right); EditingSides[] sides = {getFirstEditingSide(), getSecondEditingSide()}; myScrollSupport.install(sides); @@ -271,6 +272,37 @@ public class MergePanel2 implements DiffViewer { return myScrollSupport.isEnabled(); } + private void setupHighlighterSettings(Editor left, Editor base, Editor right) { + Editor[] editors = new Editor[]{left, base, right}; + DiffContent[] contents = myData.getContents(); + FileType[] types = DiffUtil.chooseContentTypes(contents); + + VirtualFile fallbackFile = contents[1].getFile(); + FileType fallbackType = contents[1].getContentType(); + + for (int i = 0; i < 3; i++) { + Editor editor = editors[i]; + DiffContent content = contents[i]; + + EditorHighlighter highlighter = + createHighlighter(types[i], content.getFile(), fallbackFile, fallbackType, myData.getProject()).createHighlighter(); + if (highlighter != null) { + ((EditorEx)editor).setHighlighter(highlighter); + } + } + } + + private static DiffHighlighterFactory createHighlighter(FileType contentType, + VirtualFile file, + VirtualFile otherFile, + FileType otherType, + Project project) { + if (file == null) file = otherFile; + if (contentType == null) contentType = otherType; + + return new DiffHighlighterFactoryImpl(contentType, file, project); + } + public void setHighlighterSettings(@Nullable EditorColorsScheme settings) { for (EditorPlace place : getEditorPlaces()) { setHighlighterSettings(settings, place); @@ -337,7 +369,20 @@ public class MergePanel2 implements DiffViewer { data.customizeToolbar(myPanel.resetToolbar()); myPanel.registerToolbarActions(); if ( data instanceof MergeRequestImpl && myBuilder != null){ - ((MergeRequestImpl)data).setActions(myBuilder, this); + Convertor<DialogWrapper, Boolean> preOkHook = new Convertor<DialogWrapper, Boolean>() { + @Override + public Boolean convert(DialogWrapper dialog) { + ChangeCounter counter = ChangeCounter.getOrCreate(myMergeList); + int changes = counter.getChangeCounter(); + int conflicts = counter.getConflictCounter(); + if (changes == 0 && conflicts == 0) return true; + return Messages.showYesNoDialog(dialog.getRootPane(), + DiffBundle.message("merge.dialog.apply.partially.resolved.changes.confirmation.message", changes, conflicts), + DiffBundle.message("apply.partially.resolved.merge.dialog.title"), + Messages.getQuestionIcon()) == Messages.YES; + } + }; + ((MergeRequestImpl)data).setActions(myBuilder, this, preOkHook); } } finally { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/DiffRequestFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/DiffRequestFactoryImpl.java index 3b13543e6972..962a0712465b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/DiffRequestFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/DiffRequestFactoryImpl.java @@ -20,6 +20,7 @@ import com.intellij.openapi.diff.DiffRequestFactory; import com.intellij.openapi.diff.MergeRequest; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -27,11 +28,11 @@ import org.jetbrains.annotations.Nullable; public class DiffRequestFactoryImpl extends DiffRequestFactory { - public MergeRequest createMergeRequest(String leftText, - String rightText, - String originalContent, + public MergeRequest createMergeRequest(@NotNull String leftText, + @NotNull String rightText, + @NotNull String originalContent, @NotNull VirtualFile file, - Project project, + @Nullable Project project, @Nullable final ActionButtonPresentation okButtonPresentation, @Nullable final ActionButtonPresentation cancelButtonPresentation) { final Document document = FileDocumentManager.getInstance().getDocument(file); @@ -41,16 +42,26 @@ public class DiffRequestFactoryImpl extends DiffRequestFactory { cancelButtonPresentation); } else { - return create3WayDiffRequest(leftText, rightText, originalContent, project, okButtonPresentation, cancelButtonPresentation); + return create3WayDiffRequest(leftText, rightText, originalContent, file.getFileType(), project, okButtonPresentation, cancelButtonPresentation); } } - public MergeRequest create3WayDiffRequest(final String leftText, - final String rightText, - final String originalContent, - final Project project, + public MergeRequest create3WayDiffRequest(@NotNull String leftText, + @NotNull String rightText, + @NotNull String originalContent, + @Nullable FileType type, + @Nullable Project project, @Nullable final ActionButtonPresentation okButtonPresentation, @Nullable final ActionButtonPresentation cancelButtonPresentation) { - return new MergeRequestImpl(leftText, originalContent, rightText, project, okButtonPresentation, cancelButtonPresentation); + return new MergeRequestImpl(leftText, originalContent, rightText, type, project, okButtonPresentation, cancelButtonPresentation); + } + + public MergeRequest create3WayDiffRequest(@NotNull String leftText, + @NotNull String rightText, + @NotNull String originalContent, + @Nullable Project project, + @Nullable final ActionButtonPresentation okButtonPresentation, + @Nullable final ActionButtonPresentation cancelButtonPresentation) { + return create3WayDiffRequest(leftText, rightText, originalContent, null, project, okButtonPresentation, cancelButtonPresentation); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/MergeRequestImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/MergeRequestImpl.java index 3b1385eb7a59..938c1ee7a42d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/MergeRequestImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/mergeTool/MergeRequestImpl.java @@ -28,6 +28,7 @@ import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.Convertor; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,39 +47,51 @@ public class MergeRequestImpl extends MergeRequest { @Nullable private final ActionButtonPresentation myOkButtonPresentation; @Nullable private final ActionButtonPresentation myCancelButtonPresentation; - public MergeRequestImpl(String left, - MergeVersion base, - String right, - Project project, + public MergeRequestImpl(@NotNull String left, + @NotNull MergeVersion base, + @NotNull String right, + @Nullable Project project, @Nullable final ActionButtonPresentation okButtonPresentation, @Nullable final ActionButtonPresentation cancelButtonPresentation) { this(new SimpleContent(left), new MergeContent(base, project), new SimpleContent(right), project, okButtonPresentation, cancelButtonPresentation); } - public MergeRequestImpl(DiffContent left, - MergeVersion base, - DiffContent right, - Project project, + public MergeRequestImpl(@NotNull DiffContent left, + @NotNull MergeVersion base, + @NotNull DiffContent right, + @Nullable Project project, @Nullable final ActionButtonPresentation okButtonPresentation, @Nullable final ActionButtonPresentation cancelButtonPresentation) { this(left, new MergeContent(base, project), right, project, okButtonPresentation, cancelButtonPresentation); } - public MergeRequestImpl(String left, - String base, - String right, - Project project, + public MergeRequestImpl(@NotNull String left, + @NotNull String base, + @NotNull String right, + @Nullable Project project, @Nullable final ActionButtonPresentation okButtonPresentation, @Nullable final ActionButtonPresentation cancelButtonPresentation) { - this(new SimpleContent(left), new SimpleContent(base), new SimpleContent(right), project, okButtonPresentation, - cancelButtonPresentation); + this(left, base, right, null, project, okButtonPresentation, cancelButtonPresentation); } - private MergeRequestImpl(DiffContent left, - DiffContent base, - DiffContent right, - Project project, + public MergeRequestImpl(@NotNull String left, + @NotNull String base, + @NotNull String right, + @Nullable FileType type, + @Nullable Project project, + @Nullable final ActionButtonPresentation okButtonPresentation, + @Nullable final ActionButtonPresentation cancelButtonPresentation) { + this(new SimpleContent(left, type), + new SimpleContent(base, type), + new SimpleContent(right, type), + project, okButtonPresentation, cancelButtonPresentation); + } + + private MergeRequestImpl(@NotNull DiffContent left, + @NotNull DiffContent base, + @NotNull DiffContent right, + @Nullable Project project, @Nullable final ActionButtonPresentation okButtonPresentation, @Nullable final ActionButtonPresentation cancelButtonPresentation) { super(project); @@ -176,6 +189,10 @@ public class MergeRequestImpl extends MergeRequest { } public void setActions(final DialogBuilder builder, MergePanel2 mergePanel) { + setActions(builder, mergePanel, null); + } + + public void setActions(final DialogBuilder builder, MergePanel2 mergePanel, final Convertor<DialogWrapper, Boolean> preOkHook) { builder.removeAllActions(); // otherwise dialog will get default actions (OK, Cancel) if (myOkButtonPresentation != null) { @@ -187,6 +204,7 @@ public class MergeRequestImpl extends MergeRequest { builder.setOkOperation(new Runnable() { @Override public void run() { + if (preOkHook != null && !preOkHook.convert(builder.getDialogWrapper())) return; myOkButtonPresentation.run(builder.getDialogWrapper()); } }); @@ -221,11 +239,11 @@ public class MergeRequestImpl extends MergeRequest { } public static class MergeContent extends DiffContent { - private final MergeVersion myTarget; + @NotNull private final MergeVersion myTarget; private final Document myWorkingDocument; private final Project myProject; - public MergeContent(MergeVersion target, Project project) { + public MergeContent(@NotNull MergeVersion target, Project project) { myTarget = target; myProject = project; myWorkingDocument = myTarget.createWorkingDocument(project); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/CaretStateTransferableData.java b/platform/platform-impl/src/com/intellij/openapi/editor/CaretStateTransferableData.java new file mode 100644 index 000000000000..f0d2085cbb6b --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/CaretStateTransferableData.java @@ -0,0 +1,56 @@ +/* + * 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.editor; + +import com.intellij.codeInsight.editorActions.TextBlockTransferableData; + +import java.awt.datatransfer.DataFlavor; + +public class CaretStateTransferableData implements TextBlockTransferableData { + public static final DataFlavor FLAVOR = new DataFlavor(CaretStateTransferableData.class, "Caret state"); + + public final int[] startOffsets; + public final int[] endOffsets; + + public CaretStateTransferableData(int[] startOffsets, int[] endOffsets) { + this.startOffsets = startOffsets; + this.endOffsets = endOffsets; + } + + @Override + public DataFlavor getFlavor() { + return FLAVOR; + } + + @Override + public int getOffsetCount() { + return startOffsets.length + endOffsets.length; + } + + @Override + public int getOffsets(int[] offsets, int index) { + System.arraycopy(startOffsets, 0, offsets, index, startOffsets.length); + System.arraycopy(endOffsets, 0, offsets, index + startOffsets.length, endOffsets.length); + return index + getOffsetCount(); + } + + @Override + public int setOffsets(int[] offsets, int index) { + System.arraycopy(offsets, index, startOffsets, 0, startOffsets.length); + System.arraycopy(offsets, index + startOffsets.length, endOffsets, 0, endOffsets.length); + return index + getOffsetCount(); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ClipboardTextPerCaretSplitter.java b/platform/platform-impl/src/com/intellij/openapi/editor/ClipboardTextPerCaretSplitter.java new file mode 100644 index 000000000000..a3e62412f4a4 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ClipboardTextPerCaretSplitter.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.editor; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ClipboardTextPerCaretSplitter { + @NotNull + public List<String> split(@NotNull String input, @Nullable CaretStateTransferableData caretData, int caretCount) { + if (caretCount <= 0) { + throw new IllegalArgumentException("Caret count must be positive"); + } + if (caretCount == 1) { + return Collections.singletonList(input); + } + List<String> result = new ArrayList<String>(caretCount); + int sourceCaretCount = caretData == null ? -1 : caretData.startOffsets.length; + String[] lines = sourceCaretCount == 1 || sourceCaretCount == caretCount ? null : input.split("\n", -1); + for (int i = 0; i < caretCount; i++) { + if (sourceCaretCount == 1) { + result.add(input); + } + else if (sourceCaretCount == caretCount) { + //noinspection ConstantConditions + result.add(new String(input.substring(caretData.startOffsets[i], caretData.endOffsets[i]))); + } + else if (lines.length == 0) { + result.add(""); + } + else if (lines.length == 1) { + result.add(lines[0]); + } + else if (lines.length % caretCount == 0) { + StringBuilder b = new StringBuilder(); + int linesPerSegment = lines.length / caretCount; + for (int j = 0; j < linesPerSegment; j++) { + if (j > 0) { + b.append('\n'); + } + b.append(lines[i * linesPerSegment + j]); + } + result.add(b.toString()); + } + else { + result.add(i < lines.length ? lines[i] : ""); + } + } + return result; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java index f8b3cf7bc83a..c42cd6533e1c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java @@ -15,31 +15,10 @@ */ package com.intellij.openapi.editor.actions; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.editor.Caret; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorAction; -import com.intellij.openapi.editor.actionSystem.EditorActionHandler; -import org.jetbrains.annotations.NotNull; public class CloneCaretAbove extends EditorAction { public CloneCaretAbove() { - super(new Handler()); - } - - private static class Handler extends EditorActionHandler { - public Handler() { - super(true); - } - - @Override - public void doExecute(Editor editor, @NotNull Caret caret, DataContext dataContext) { - caret.clone(true); - } - - @Override - public boolean isEnabled(Editor editor, DataContext dataContext) { - return editor.getCaretModel().supportsMultipleCarets(); - } + super(new CloneCaretActionHandler(true)); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretActionHandler.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretActionHandler.java new file mode 100644 index 000000000000..5c5528a38153 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretActionHandler.java @@ -0,0 +1,113 @@ +/* + * 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.editor.actions; + +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.editor.Caret; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorLastActionTracker; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.actionSystem.EditorActionHandler; +import com.intellij.openapi.util.Key; +import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +public class CloneCaretActionHandler extends EditorActionHandler { + private static final Key<Integer> LEVEL = Key.create("CloneCaretActionHandler.level"); + + private static final Set<String> OUR_ACTIONS = new HashSet<String>(Arrays.asList( + IdeActions.ACTION_EDITOR_CLONE_CARET_ABOVE, + IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW, + IdeActions.ACTION_EDITOR_MOVE_CARET_LEFT_WITH_SELECTION, + IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION + )); + + private final boolean myCloneAbove; + + public CloneCaretActionHandler(boolean above) { + myCloneAbove = above; + } + + @Override + public boolean isEnabled(Editor editor, DataContext dataContext) { + return editor.getCaretModel().supportsMultipleCarets(); + } + + @Override + protected void doExecute(Editor editor, @Nullable Caret targetCaret, DataContext dataContext) { + if (targetCaret != null) { + targetCaret.clone(myCloneAbove); + return; + } + int currentLevel = 0; + List<Caret> currentCarets = new ArrayList<Caret>(); + for (Caret caret : editor.getCaretModel().getAllCarets()) { + int level = getLevel(caret); + if (Math.abs(level) > Math.abs(currentLevel)) { + currentLevel = level; + currentCarets.clear(); + } + if (Math.abs(level) == Math.abs(currentLevel)) { + currentCarets.add(caret); + } + } + boolean removeCarets = currentLevel > 0 && myCloneAbove || currentLevel < 0 && !myCloneAbove; + Integer newLevel = myCloneAbove ? currentLevel - 1 : currentLevel + 1; + for (Caret caret : currentCarets) { + if (removeCarets) { + editor.getCaretModel().removeCaret(caret); + } + else { + Caret clone = caret; + do { + Caret original = clone; + clone = clone.clone(myCloneAbove); + if (original != caret) { + editor.getCaretModel().removeCaret(original); + } + } while (clone != null && caret.hasSelection() && !clone.hasSelection()); + if (clone != null) { + clone.putUserData(LEVEL, newLevel); + } + } + } + if (removeCarets) { + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + } + + private static int getLevel(Caret caret) { + if (isRepeatedActionInvocation()) { + Integer value = caret.getUserData(LEVEL); + return value == null ? 0 : value; + } + else { + caret.putUserData(LEVEL, null); + return 0; + } + } + + private static boolean isRepeatedActionInvocation() { + String lastActionId = EditorLastActionTracker.getInstance().getLastActionId(); + return OUR_ACTIONS.contains(lastActionId); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java index 17d17708b900..974087796e08 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java @@ -15,35 +15,10 @@ */ package com.intellij.openapi.editor.actions; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.editor.Caret; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorAction; -import com.intellij.openapi.editor.actionSystem.EditorActionHandler; -import org.jetbrains.annotations.NotNull; public class CloneCaretBelow extends EditorAction { public CloneCaretBelow() { - super(new Handler()); - } - - private static class Handler extends EditorActionHandler { - public Handler() { - super(true); - } - - @Override - public void doExecute(Editor editor, @NotNull Caret caret, DataContext dataContext) { - CaretModel caretModel = editor.getCaretModel(); - if (caretModel.supportsMultipleCarets()) { - caret.clone(false); - } - } - - @Override - public boolean isEnabled(Editor editor, DataContext dataContext) { - return editor.getCaretModel().supportsMultipleCarets(); - } + super(new CloneCaretActionHandler(false)); } } 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 cfd039a5d504..c769f92fff78 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 @@ -62,7 +62,13 @@ public class CaretImpl extends UserDataHolderBase implements Caret { private int myVisualLineEnd; private RangeMarker savedBeforeBulkCaretMarker; private boolean mySkipChangeRequests; + /** + * Initial horizontal caret position during vertical navigation. + * Similar to {@link #myDesiredX}, but represents logical caret position (<code>getLogicalPosition().column</code>) rather than visual. + */ private int myLastColumnNumber = 0; + private int myDesiredSelectionStartColumn = -1; + private int myDesiredSelectionEndColumn = -1; /** * We check that caret is located at the target offset at the end of {@link #moveToOffset(int, boolean)} method. However, * it's possible that the following situation occurs: @@ -82,11 +88,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret { */ private boolean myReportCaretMoves; /** - * There is a possible case that user defined non-monospaced font for editor. That means that various symbols have different - * visual widths. That means that if we move caret vertically it may deviate to the left/right. However, we can try to preserve - * its initial visual position when possible. + * This field holds initial horizontal caret position during vertical navigation. It's used to determine target position when + * moving to the new line. It is stored in pixels, not in columns, to account for non-monospaced fonts as well. * <p/> - * This field holds desired value for visual <code>'x'</code> caret coordinate (negative value if no coordinate should be preserved). + * Negative value means no coordinate should be preserved. */ private int myDesiredX = -1; @@ -255,10 +260,11 @@ public class CaretImpl extends UserDataHolderBase implements Caret { EditorSettings editorSettings = myEditor.getSettings(); VisualPosition visualCaret = getVisualPosition(); + int lastColumnNumber = myLastColumnNumber; int desiredX = myDesiredX; if (columnShift == 0) { if (myDesiredX < 0) { - desiredX = myEditor.visualPositionToXY(visualCaret).x; + desiredX = getCurrentX(); } } else { @@ -267,15 +273,12 @@ public class CaretImpl extends UserDataHolderBase implements Caret { int newLineNumber = visualCaret.line + lineShift; int newColumnNumber = visualCaret.column + columnShift; - if (desiredX >= 0 && !ApplicationManager.getApplication().isUnitTestMode()) { + if (desiredX >= 0) { newColumnNumber = myEditor.xyToVisualPosition(new Point(desiredX, Math.max(0, newLineNumber) * myEditor.getLineHeight())).column; } Document document = myEditor.getDocument(); - if (!editorSettings.isVirtualSpace() && columnShift == 0 && getLogicalPosition().softWrapLinesOnCurrentLogicalLine <= 0) { - newColumnNumber = supportsMultipleCarets() ? myLastColumnNumber : myEditor.getLastColumnNumber(); - } - else if (!editorSettings.isVirtualSpace() && lineShift == 0 && columnShift == 1) { + if (!editorSettings.isVirtualSpace() && lineShift == 0 && columnShift == 1) { int lastLine = document.getLineCount() - 1; if (lastLine < 0) lastLine = 0; if (EditorModificationUtil.calcAfterLineEnd(myEditor) >= 0 && @@ -303,18 +306,21 @@ public class CaretImpl extends UserDataHolderBase implements Caret { // We want to move caret to the first column if it's already located at the first line and 'Up' is pressed. newColumnNumber = 0; desiredX = -1; + lastColumnNumber = -1; } VisualPosition pos = new VisualPosition(newLineNumber, newColumnNumber); - int lastColumnNumber = newColumnNumber; if (!myEditor.getSoftWrapModel().isInsideSoftWrap(pos)) { LogicalPosition log = myEditor.visualToLogicalPosition(new VisualPosition(newLineNumber, newColumnNumber)); int offset = myEditor.logicalPositionToOffset(log); if (offset >= document.getTextLength()) { int lastOffsetColumn = myEditor.offsetToVisualPosition(document.getTextLength()).column; // We want to move caret to the last column if if it's located at the last line and 'Down' is pressed. - newColumnNumber = lastColumnNumber = Math.max(lastOffsetColumn, newColumnNumber); - desiredX = -1; + if (lastOffsetColumn > newColumnNumber) { + newColumnNumber = lastOffsetColumn; + desiredX = -1; + lastColumnNumber = -1; + } } if (!editorSettings.isCaretInsideTabs()) { CharSequence text = document.getCharsSequence(); @@ -353,7 +359,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret { } else { moveToVisualPosition(pos); - if (!editorSettings.isVirtualSpace() && columnShift == 0) { + if (!editorSettings.isVirtualSpace() && columnShift == 0 && lastColumnNumber >=0) { setLastColumnNumber(lastColumnNumber); } } @@ -550,6 +556,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret { } setLastColumnNumber(myLogicalCaret.column); + myDesiredSelectionStartColumn = myDesiredSelectionEndColumn = -1; myVisibleCaret = myEditor.logicalToVisualPosition(myLogicalCaret); updateOffsetsFromLogicalPosition(); @@ -705,7 +712,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret { myEditor.getFoldingModel().flushCaretPosition(); - setLastColumnNumber(column); + setLastColumnNumber(myLogicalCaret.column); + myDesiredSelectionStartColumn = myDesiredSelectionEndColumn = -1; myEditor.updateCaretCursor(); requestRepaint(oldInfo); @@ -962,6 +970,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret { clone.myLastColumnNumber = this.myLastColumnNumber; clone.myReportCaretMoves = this.myReportCaretMoves; clone.myDesiredX = this.myDesiredX; + clone.myDesiredSelectionStartColumn = -1; + clone.myDesiredSelectionEndColumn = -1; return clone; } @@ -971,10 +981,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret { assertIsDispatchThread(); int lineShift = above ? -1 : 1; final CaretImpl clone = cloneWithoutSelection(); - final int newSelectionStartOffset, newSelectionEndOffset; + final int newSelectionStartOffset, newSelectionEndOffset, newSelectionStartColumn, newSelectionEndColumn; final VisualPosition newSelectionStartPosition, newSelectionEndPosition; final boolean hasNewSelection; - if (hasSelection()) { + if (hasSelection() || myDesiredSelectionStartColumn >=0 || myDesiredSelectionEndColumn >= 0) { VisualPosition startPosition = getSelectionStartPosition(); VisualPosition endPosition = getSelectionEndPosition(); VisualPosition leadPosition = getLeadSelectionPosition(); @@ -982,8 +992,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret { boolean leadIsEnd = leadPosition.equals(endPosition); LogicalPosition selectionStart = myEditor.visualToLogicalPosition(leadIsStart || leadIsEnd ? leadPosition : startPosition); LogicalPosition selectionEnd = myEditor.visualToLogicalPosition(leadIsEnd ? startPosition : endPosition); - LogicalPosition newSelectionStart = truncate(new LogicalPosition(selectionStart.line + lineShift, selectionStart.column)); - LogicalPosition newSelectionEnd = truncate(new LogicalPosition(selectionEnd.line + lineShift, selectionEnd.column)); + newSelectionStartColumn = myDesiredSelectionStartColumn < 0 ? selectionStart.column : myDesiredSelectionStartColumn; + newSelectionEndColumn = myDesiredSelectionEndColumn < 0 ? selectionEnd.column : myDesiredSelectionEndColumn; + LogicalPosition newSelectionStart = truncate(selectionStart.line + lineShift, newSelectionStartColumn); + LogicalPosition newSelectionEnd = truncate(selectionEnd.line + lineShift, newSelectionEndColumn); newSelectionStartOffset = myEditor.logicalPositionToOffset(newSelectionStart); newSelectionEndOffset = myEditor.logicalPositionToOffset(newSelectionEnd); newSelectionStartPosition = myEditor.logicalToVisualPosition(newSelectionStart); @@ -996,6 +1008,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret { newSelectionStartPosition = null; newSelectionEndPosition = null; hasNewSelection = false; + newSelectionStartColumn = -1; + newSelectionEndColumn = -1; } LogicalPosition oldPosition = getLogicalPosition(); int newLine = oldPosition.line + lineShift; @@ -1003,7 +1017,12 @@ public class CaretImpl extends UserDataHolderBase implements Caret { Disposer.dispose(clone); return null; } - clone.moveToLogicalPosition(new LogicalPosition(newLine, oldPosition.column), false, null, false); + clone.moveToLogicalPosition(new LogicalPosition(newLine, myLastColumnNumber), false, null, false); + clone.myLastColumnNumber = myLastColumnNumber; + clone.myDesiredX = myDesiredX >= 0 ? myDesiredX : getCurrentX(); + clone.myDesiredSelectionStartColumn = newSelectionStartColumn; + clone.myDesiredSelectionEndColumn = newSelectionEndColumn; + if (myEditor.getCaretModel().addCaret(clone)) { if (hasNewSelection) { myEditor.getCaretModel().doWithCaretMerging(new Runnable() { @@ -1025,15 +1044,15 @@ public class CaretImpl extends UserDataHolderBase implements Caret { } } - private LogicalPosition truncate(LogicalPosition position) { - if (position.line < 0) { + private LogicalPosition truncate(int line, int column) { + if (line < 0) { return new LogicalPosition(0, 0); } - else if (position.line >= myEditor.getDocument().getLineCount()) { + else if (line >= myEditor.getDocument().getLineCount()) { return myEditor.offsetToLogicalPosition(myEditor.getDocument().getTextLength()); } else { - return position; + return new LogicalPosition(line, column); } } @@ -1396,9 +1415,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret { } try { - EditorActionHandler handler = EditorActionManager.getInstance().getActionHandler( - IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET); - handler.execute(myEditor, myEditor.getDataContext()); + EditorActionHandler handler = EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET); + handler.execute(myEditor, CaretImpl.this, myEditor.getDataContext()); } finally { if (needOverrideSetting) { @@ -1453,6 +1471,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret { return marker != null && marker.isValid() && isVirtualSelectionEnabled() && myEndVirtualOffset > myStartVirtualOffset; } + private int getCurrentX() { + return myEditor.visualPositionToXY(myVisibleCaret).x; + } + @Override @NotNull public EditorImpl getEditor() { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorCopyPasteHelperImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorCopyPasteHelperImpl.java new file mode 100644 index 000000000000..c26c7e99ba63 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorCopyPasteHelperImpl.java @@ -0,0 +1,150 @@ +/* + * 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.editor.impl; + +import com.intellij.codeInsight.editorActions.TextBlockTransferable; +import com.intellij.codeInsight.editorActions.TextBlockTransferableData; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.*; +import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.LineTokenizer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +public class EditorCopyPasteHelperImpl extends EditorCopyPasteHelper { + private static final Logger LOG = Logger.getInstance(EditorCopyPasteHelperImpl.class); + + @Override + public void copySelectionToClipboard(@NotNull Editor editor) { + ApplicationManager.getApplication().assertIsDispatchThread(); + List<TextBlockTransferableData> extraData = new ArrayList<TextBlockTransferableData>(); + String s = editor.getCaretModel().supportsMultipleCarets() ? getSelectedTextForClipboard(editor, extraData) + : editor.getSelectionModel().getSelectedText(); + if (s == null) return; + + s = TextBlockTransferable.convertLineSeparators(s, "\n", extraData); + Transferable contents = editor.getCaretModel().supportsMultipleCarets() ? new TextBlockTransferable(s, extraData, null) : new StringSelection(s); + CopyPasteManager.getInstance().setContents(contents); + } + + public static String getSelectedTextForClipboard(@NotNull Editor editor, @NotNull Collection<TextBlockTransferableData> extraDataCollector) { + final StringBuilder buf = new StringBuilder(); + String separator = ""; + List<Caret> carets = editor.getCaretModel().getAllCarets(); + int[] startOffsets = new int[carets.size()]; + int[] endOffsets = new int[carets.size()]; + for (int i = 0; i < carets.size(); i++) { + buf.append(separator); + String caretSelectedText = carets.get(i).getSelectedText(); + startOffsets[i] = buf.length(); + if (caretSelectedText != null) { + buf.append(caretSelectedText); + } + endOffsets[i] = buf.length(); + separator = "\n"; + } + extraDataCollector.add(new CaretStateTransferableData(startOffsets, endOffsets)); + return buf.toString(); + } + + @Nullable + @Override + public TextRange[] pasteFromClipboard(@NotNull Editor editor) { + CopyPasteManager manager = CopyPasteManager.getInstance(); + if (manager.areDataFlavorsAvailable(DataFlavor.stringFlavor)) { + Transferable clipboardContents = manager.getContents(); + if (clipboardContents != null) { + return pasteTransferable(editor, clipboardContents); + } + } + return null; + } + + @Nullable + @Override + public TextRange[] pasteTransferable(final @NotNull Editor editor, @NotNull Transferable content) { + String text = getStringContent(content); + if (text == null) return null; + + if (editor.getCaretModel().supportsMultipleCarets()) { + int caretCount = editor.getCaretModel().getCaretCount(); + if (caretCount == 1 && editor.isColumnMode()) { + int pastedLineCount = LineTokenizer.calcLineCount(text, true); + EditorModificationUtil.deleteSelectedText(editor); + Caret caret = editor.getCaretModel().getPrimaryCaret(); + for (int i = 0; i < pastedLineCount - 1; i++) { + caret = caret.clone(false); + if (caret == null) { + break; + } + } + caretCount = editor.getCaretModel().getCaretCount(); + } + CaretStateTransferableData caretData = null; + try { + caretData = content.isDataFlavorSupported(CaretStateTransferableData.FLAVOR) + ? (CaretStateTransferableData)content.getTransferData(CaretStateTransferableData.FLAVOR) : null; + } + catch (Exception e) { + LOG.error(e); + } + final TextRange[] ranges = new TextRange[caretCount]; + final Iterator<String> segments = new ClipboardTextPerCaretSplitter().split(text, caretData, caretCount).iterator(); + final int[] index = {0}; + editor.getCaretModel().runForEachCaret(new CaretAction() { + @Override + public void perform(Caret caret) { + String segment = segments.next(); + int caretOffset = caret.getOffset(); + ranges[index[0]++] = new TextRange(caretOffset, caretOffset + segment.length()); + EditorModificationUtil.insertStringAtCaret(editor, segment, false, true); + } + }); + return ranges; + } + else { + int caretOffset = editor.getCaretModel().getOffset(); + EditorModificationUtil.insertStringAtCaret(editor, text, false, true); + return new TextRange[] { new TextRange(caretOffset, caretOffset + text.length())}; + } + } + + @Nullable + private static String getStringContent(@NotNull Transferable content) { + RawText raw = RawText.fromTransferable(content); + if (raw != null) return raw.rawText; + + try { + return (String)content.getTransferData(DataFlavor.stringFlavor); + } + catch (UnsupportedFlavorException ignore) { } + catch (IOException ignore) { } + + return null; + } +} 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 f600c606a98d..39706f016adc 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 @@ -992,7 +992,16 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse } public void setLineNumberAreaWidth(@NotNull TIntFunction calculator) { - final int lineNumberAreaWidth = calculator.execute(myLineNumberConvertor.execute(endLineNumber())); + int maxLineNumber = 0; + for (int i = endLineNumber(); i >= 0; i--) { + int number = myLineNumberConvertor.execute(i); + if (number >= 0) { + maxLineNumber = number; + break; + } + } + + final int lineNumberAreaWidth = calculator.execute(maxLineNumber); if (myLineNumberAreaWidth != lineNumberAreaWidth) { myLineNumberAreaWidth = lineNumberAreaWidth; fireResized(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java index c1499a71a71b..8be2284e8360 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java @@ -15,10 +15,19 @@ */ package com.intellij.openapi.editor.impl; +import com.intellij.lexer.Lexer; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighter; +import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.cache.impl.id.PlatformIdTableBuilding; +import com.intellij.psi.impl.search.LexerEditorHighlighterLexer; import com.intellij.reference.SoftReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -55,4 +64,28 @@ public class EditorHighlighterCache { return null; } + @Nullable + public static Lexer getLexerBasedOnLexerHighlighter(CharSequence text, VirtualFile virtualFile, Project project) { + EditorHighlighter highlighter = null; + + PsiFile psiFile = virtualFile != null ? PsiManager.getInstance(project).findFile(virtualFile) : null; + final Document document = psiFile != null ? PsiDocumentManager.getInstance(project).getDocument(psiFile) : null; + final EditorHighlighter cachedEditorHighlighter; + boolean alreadyInitializedHighlighter = false; + + if (document != null && + (cachedEditorHighlighter = getEditorHighlighterForCachesBuilding(document)) != null && + PlatformIdTableBuilding.checkCanUseCachedEditorHighlighter(text, cachedEditorHighlighter)) { + highlighter = cachedEditorHighlighter; + alreadyInitializedHighlighter = true; + } + else if (virtualFile != null) { + highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, virtualFile); + } + + if (highlighter != null) { + return new LexerEditorHighlighterLexer(highlighter, alreadyInitializedHighlighter); + } + return null; + } } 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 b708f38a9190..c0ffbd10aca4 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 @@ -33,7 +33,7 @@ import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.impl.MouseGestureManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.application.ex.ApplicationManagerEx; +import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.diagnostic.Logger; @@ -147,7 +147,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi @NotNull private final EditorComponentImpl myEditorComponent; @NotNull private final EditorGutterComponentImpl myGutterComponent; private final TraceableDisposable myTraceableDisposable = new TraceableDisposable(new Throwable()); - private volatile boolean hasTabs; // optimisation flag: when editor contains no tabs it is dramatically easier to calculate positions static { ComplementaryFontsRegistry.getFontAbleToDisplay(' ', 0, 0, UIManager.getFont("Label.font").getFamily()); // load costly font info @@ -168,6 +167,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi @NotNull private final CaretCursor myCaretCursor; private final ScrollingTimer myScrollingTimer = new ScrollingTimer(); + @SuppressWarnings("RedundantStringConstructorCall") private final Object MOUSE_DRAGGED_GROUP = new String("MouseDraggedGroup"); @NotNull private final SettingsImpl mySettings; @@ -246,8 +246,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi @Nullable private Color myForcedBackground = null; @Nullable private Dimension myPreferredSize; private int myVirtualPageHeight; - @Nullable private Runnable myGutterSizeUpdater = null; - private boolean myGutterNeedsUpdate = false; private Alarm myAppleRepaintAlarm; private final Alarm myMouseSelectionStateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); @@ -317,8 +315,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } EditorImpl(@NotNull Document document, boolean viewer, @Nullable Project project) { + assertIsDispatchThread(); myProject = project; myDocument = (DocumentEx)document; + if (myDocument instanceof DocumentImpl) { + ((DocumentImpl)myDocument).requestTabTracking(); + } myScheme = createBoundColorSchemeDelegate(null); initTabPainter(); myIsViewer = viewer; @@ -433,13 +435,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (myPrimaryCaret != null) { myPrimaryCaret.updateVisualPosition(); // repainting old primary caret's row background } - ((CaretImpl)e.getCaret()).updateVisualPosition(); // repainting caret region + updateCaretVisualPosition(e); myPrimaryCaret = myCaretModel.getPrimaryCaret(); } @Override public void caretRemoved(CaretEvent e) { - ((CaretImpl)e.getCaret()).updateVisualPosition(); // repainting caret region + updateCaretVisualPosition(e); myPrimaryCaret = myCaretModel.getPrimaryCaret(); // repainting new primary caret's row background myPrimaryCaret.updateVisualPosition(); } @@ -548,7 +550,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } }); } - updateHasTabsFlag(document.getImmutableCharSequence()); + } + + private static void updateCaretVisualPosition(CaretEvent e) { + CaretImpl caretImpl = ((CaretImpl)e.getCaret()); + if (caretImpl != null) { + caretImpl.updateVisualPosition(); // repainting caret region + } } @NotNull @@ -575,7 +583,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myPrefixWidthInPixels = 0; if (myPrefixText != null) { for (char c : myPrefixText) { - myPrefixWidthInPixels += EditorUtil.charWidth(c, myPrefixAttributes.getFontType(), this); + LOG.assertTrue(myPrefixAttributes != null); + if (myPrefixAttributes != null) { + myPrefixWidthInPixels += EditorUtil.charWidth(c, myPrefixAttributes.getFontType(), this); + } } } mySoftWrapModel.recalculate(); @@ -791,6 +802,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (myConnection != null) { myConnection.disconnect(); } + if (myDocument instanceof DocumentImpl) { + ((DocumentImpl)myDocument).giveUpTabTracking(); + } Disposer.dispose(myDisposable); } @@ -898,9 +912,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi new UiNotifyConnector(myEditorComponent, new Activatable.Adapter(){ @Override public void showNotify() { - if (myGutterNeedsUpdate) { - updateGutterSize(); - } + myGutterComponent.updateSize(); } }); @@ -1505,7 +1517,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private final int[] myLastXOffsets = new int[myLastStartOffsets.length]; private final int[] myLastXs = new int[myLastStartOffsets.length]; private int myCurrentCachePosition; - private int myLastCacheHits, myTotalRequests; // todo remove + private int myLastCacheHits; + private int myTotalRequests; // todo remove private int getTabbedTextWidth(int startOffset, int targetColumn, int xOffset) { int x = xOffset; @@ -1621,7 +1634,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi @Override public void repaint(final int startOffset, int endOffset) { - if (!isShowing() || myScrollPane == null || myDocument.isInBulkUpdate()) { + if (!isShowing() || myDocument.isInBulkUpdate()) { return; } @@ -1638,7 +1651,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } private boolean isShowing() { - return myGutterComponent != null && myGutterComponent.isShowing(); + return myGutterComponent.isShowing(); } private void repaintToScreenBottom(int startLine) { @@ -1679,9 +1692,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } private void bulkUpdateFinished() { - if (myScrollPane == null) { - return; - } clearTextWidthCache(); @@ -1700,7 +1710,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (isStickySelection()) { setStickySelection(false); } - if (myDocument.isInBulkUpdate() || myScrollingModel == null) { + if (myDocument.isInBulkUpdate()) { // Assuming that the job is done at bulk listener callback methods. return; } @@ -1716,7 +1726,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } private void changedUpdate(DocumentEvent e) { - if (myScrollPane == null || myDocument.isInBulkUpdate()) return; + if (myDocument.isInBulkUpdate()) return; clearTextWidthCache(); mySelectionModel.removeBlockSelection(); @@ -1754,16 +1764,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi Point caretLocation = visualPositionToXY(getCaretModel().getVisualPosition()); int scrollOffset = caretLocation.y - myCaretUpdateVShift; getScrollingModel().scrollVertically(scrollOffset); - updateHasTabsFlag(e.getNewFragment()); } - private void updateHasTabsFlag(@NotNull CharSequence newChars) { - if (!hasTabs) { - hasTabs = StringUtil.contains(newChars, 0, newChars.length(), '\t'); - } - } public boolean hasTabs() { - return hasTabs; + return !(myDocument instanceof DocumentImpl) || ((DocumentImpl)myDocument).mightContainTabs(); } public boolean isScrollToCaret() { @@ -1784,24 +1788,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } private void updateGutterSize() { - if (myGutterSizeUpdater != null) return; - myGutterSizeUpdater = new Runnable() { + LaterInvocator.invokeLater(new Runnable() { @Override public void run() { - if (!isDisposed()) { - if (isShowing()) { - myGutterComponent.updateSize(); - myGutterNeedsUpdate = false; - } - else { - myGutterNeedsUpdate = true; - } - } - myGutterSizeUpdater = null; + myGutterComponent.updateSize(); } - }; - - SwingUtilities.invokeLater(myGutterSizeUpdater); + }); } void validateSize() { @@ -1945,7 +1937,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } if (isReleased) { - g.setColor(new Color(128, 255, 128)); + g.setColor(new JBColor(new Color(128, 255, 128), new Color(128, 255, 128))); g.fillRect(clip.x, clip.y, clip.width, clip.height); return; } @@ -2239,7 +2231,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myLastBackgroundColor = null; int start = clipStartOffset; - int end = clipEndOffset; + if (!myPurePaintingMode) { getSoftWrapModel().registerSoftWrapsIfNecessary(); } @@ -2250,7 +2242,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi return; } - IterationState iterationState = new IterationState(this, start, end, isPaintSelection()); + IterationState iterationState = new IterationState(this, start, clipEndOffset, isPaintSelection()); TextAttributes attributes = iterationState.getMergedAttributes(); Color backColor = getBackgroundColor(attributes); int fontType = attributes.getFontType(); @@ -2322,7 +2314,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi position.y += lineHeight; start = lEnd; } - else if (collapsedFolderAt.getEndOffset() == end) { + else if (collapsedFolderAt.getEndOffset() == clipEndOffset) { softWrap = mySoftWrapModel.getSoftWrap(collapsedFolderAt.getStartOffset()); if (softWrap != null) { position.x = drawSoftWrapAwareBackground( @@ -3677,19 +3669,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi return logicalToVisualPosition(logicalPos, true); } - // TODO den remove as soon as the problem is fixed. - private final ThreadLocal<Integer> stackDepth = new ThreadLocal<Integer>(); - - // TODO den remove as soon as the problem is fixed. @Override @NotNull public VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logicalPos, boolean softWrapAware) { - stackDepth.set(0); - return doLogicalToVisualPosition(logicalPos, softWrapAware); + return doLogicalToVisualPosition(logicalPos, softWrapAware,0); } @NotNull - private VisualPosition doLogicalToVisualPosition(@NotNull LogicalPosition logicalPos, boolean softWrapAware) { + private VisualPosition doLogicalToVisualPosition(@NotNull LogicalPosition logicalPos, boolean softWrapAware, + // TODO den remove as soon as the problem is fixed. + int stackDepth) { assertReadAccess(); if (!myFoldingModel.isFoldingEnabled() && !mySoftWrapModel.isSoftWrappingEnabled()) { return new VisualPosition(logicalPos.line, logicalPos.column); @@ -3703,25 +3692,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi offset = outermostCollapsed.getStartOffset(); LogicalPosition foldStart = offsetToLogicalPosition(offset); // TODO den remove as soon as the problem is fixed. - Integer depth = stackDepth.get(); - if (depth >= 0) { - stackDepth.set(depth + 1); - if (depth > 15) { - LOG.error("Detected potential StackOverflowError at logical->visual position mapping. Given logical position: '" + - logicalPos + "'. State: " + dumpState()); - stackDepth.set(-1); - } - } - // TODO den remove as soon as the problem is fixed. - try { - return doLogicalToVisualPosition(foldStart, true); - } - finally { - depth = stackDepth.get(); - if (depth > 0) { - stackDepth.set(depth - 1); - } + if (stackDepth > 15) { + LOG.error("Detected potential StackOverflowError at logical->visual position mapping. Given logical position: '" + + logicalPos + "'. State: " + dumpState()); + stackDepth = -1; } + return doLogicalToVisualPosition(foldStart, true, stackDepth+1); } else { offset = outermostCollapsed.getEndOffset() + 3; // WTF? @@ -3988,7 +3964,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi return false; } - if (e.getComponent() != myInitialMouseEvent.getComponent() || !e.getPoint().equals(myInitialMouseEvent.getPoint())) { + if (myInitialMouseEvent!= null && (e.getComponent() != myInitialMouseEvent.getComponent() || !e.getPoint().equals(myInitialMouseEvent.getPoint()))) { myIgnoreMouseEventsConsecutiveToInitial = false; myInitialMouseEvent = null; return false; @@ -4197,7 +4173,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } else { final LogicalPosition blockStart = selectionModel.hasBlockSelection() ? selectionModel.getBlockStart() : oldLogicalCaret; - setBlockSelectionAndBlockActions(e, blockStart, getCaretModel().getLogicalPosition()); + if (blockStart != null) { + setBlockSelectionAndBlockActions(e, blockStart, getCaretModel().getLogicalPosition()); + } } } else { @@ -5002,7 +4980,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } void assertIsDispatchThread() { - ApplicationManagerEx.getApplicationEx().assertIsDispatchThread(); + ApplicationManager.getApplication().assertIsDispatchThread(); } private static void assertReadAccess() { @@ -5392,7 +5370,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi public void run() { int docLength = doc.getTextLength(); ProperTextRange range = composedTextRange.intersection(new TextRange(0, docLength)); - doc.deleteString(range.getStartOffset(), range.getEndOffset()); + if (range != null) { + doc.deleteString(range.getStartOffset(), range.getEndOffset()); + } } }); } @@ -6745,7 +6725,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private class TablessBorder extends SideBorder { private TablessBorder() { - super(UIUtil.getBorderColor(), SideBorder.ALL); + super(JBColor.border(), SideBorder.ALL); } @Override @@ -6769,7 +6749,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi public Insets getBorderInsets(Component c) { Container splitters = SwingUtilities.getAncestorOfClass(EditorsSplitters.class, c); boolean thereIsSomethingAbove = !SystemInfo.isMac || UISettings.getInstance().SHOW_MAIN_TOOLBAR || UISettings.getInstance().SHOW_NAVIGATION_BAR || - EditorImpl.this.myProject != null && !ToolWindowManagerEx.getInstanceEx(EditorImpl.this.myProject).getIdsOn(ToolWindowAnchor.TOP).isEmpty(); + myProject != null && !ToolWindowManagerEx.getInstanceEx(myProject).getIdsOn(ToolWindowAnchor.TOP).isEmpty(); return splitters == null ? super.getBorderInsets(c) : new Insets(thereIsSomethingAbove ? 1 : 0, 0, 0, 0); } 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 a2f45fe33857..6ec29e58dc0b 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 @@ -36,6 +36,7 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actionSystem.DocCommandGroupId; +import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorFontType; import com.intellij.openapi.editor.ex.*; import com.intellij.openapi.editor.markup.ErrorStripeRenderer; @@ -45,7 +46,7 @@ import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.ProperTextRange; -import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; import com.intellij.ui.*; @@ -428,7 +429,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void paint(Graphics g) { + public void paint(@NotNull Graphics g) { ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart(); final Rectangle bounds = getBounds(); @@ -437,7 +438,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark errorIconBounds.y = bounds.height / 2 - errorIconBounds.height / 2; try { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) { + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) { g.setColor(getEditor().getColorsScheme().getDefaultBackground()); g.fillRect(0, 0, bounds.width, bounds.height); @@ -493,7 +494,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void uninstallUI(JComponent c) { + public void uninstallUI(@NotNull JComponent c) { super.uninstallUI(c); myCachedTrack = null; } @@ -528,7 +529,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark @Override protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) { + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) { super.paintThumb(g, c, thumbBounds); return; } @@ -565,13 +566,13 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark @Override protected int getThickness() { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) return super.getThickness(); + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) return super.getThickness(); return super.getThickness() + (isMacOverlayScrollbar() ? 2 : 7); } @Override protected void doPaintTrack(Graphics g, JComponent c, Rectangle bounds) { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) { + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) { g.setColor(getEditor().getColorsScheme().getDefaultBackground()); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); //return; @@ -609,7 +610,9 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } private void paintTrackBasement(Graphics g, Rectangle bounds) { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) { + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) { + g.setColor(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground()); + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); return; } @@ -647,13 +650,13 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark private void drawMarkup(final Graphics g, final int width, int startOffset, int endOffset, MarkupModelEx markup) { final Queue<PositionedStripe> thinEnds = new PriorityQueue<PositionedStripe>(5, new Comparator<PositionedStripe>() { @Override - public int compare(PositionedStripe o1, PositionedStripe o2) { + public int compare(@NotNull PositionedStripe o1, @NotNull PositionedStripe o2) { return o1.yEnd - o2.yEnd; } }); final Queue<PositionedStripe> wideEnds = new PriorityQueue<PositionedStripe>(5, new Comparator<PositionedStripe>() { @Override - public int compare(PositionedStripe o1, PositionedStripe o2) { + public int compare(@NotNull PositionedStripe o1, @NotNull PositionedStripe o2) { return o1.yEnd - o2.yEnd; } }); @@ -757,12 +760,21 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark boolean drawBottomDecoration) { int x = isMirrored() ? 3 : 5; int paintWidth = width; + boolean flatStyle = Registry.is("ide.new.markup.markers"); if (thinErrorStripeMark) { paintWidth /= 2; - paintWidth += 1; + paintWidth += flatStyle ? 0 : 1; x = isMirrored() ? width + 2 : 0; } if (color == null) return; + Color darker = UIUtil.isUnderDarcula()? color : ColorUtil.shift(color, 0.75); + + if (flatStyle) { + g.setColor(darker); + g.fillRect(x, yStart, paintWidth, yEnd - yStart + 1); + return; + } + g.setColor(color); g.fillRect(x + 1, yStart, paintWidth - 2, yEnd - yStart + 1); @@ -774,7 +786,6 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark //top decoration UIUtil.drawLine(g, x + 1, yStart, x + paintWidth - 2, yStart); } - Color darker = ColorUtil.shift(color, 0.75); g.setColor(darker); if (drawBottomDecoration) { @@ -787,7 +798,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark // mouse events @Override - public void mouseClicked(final MouseEvent e) { + public void mouseClicked(@NotNull final MouseEvent e) { CommandProcessor.getInstance().executeCommand(myEditor.getProject(), new Runnable() { @Override public void run() { @@ -801,11 +812,11 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void mousePressed(MouseEvent e) { + public void mousePressed(@NotNull MouseEvent e) { } @Override - public void mouseReleased(MouseEvent e) { + public void mouseReleased(@NotNull MouseEvent e) { } private int getWidth() { @@ -824,7 +835,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void mouseMoved(MouseEvent e) { + public void mouseMoved(@NotNull MouseEvent e) { EditorImpl.MyScrollBar scrollBar = myEditor.getVerticalScrollBar(); int buttonHeight = scrollBar.getDecScrollButtonHeight(); int lineCount = getDocument().getLineCount() + myEditor.getSettings().getAdditionalLinesCount(); @@ -897,16 +908,16 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void mouseEntered(MouseEvent e) { + public void mouseEntered(@NotNull MouseEvent e) { } @Override - public void mouseExited(MouseEvent e) { + public void mouseExited(@NotNull MouseEvent e) { cancelMyToolTips(e, true); } @Override - public void mouseDragged(MouseEvent e) { + public void mouseDragged(@NotNull MouseEvent e) { cancelMyToolTips(e, true); } @@ -943,13 +954,15 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } public void markDirtied(@NotNull ProperTextRange yPositions) { - int start = Math.max(0, yPositions.getStartOffset() - myEditor.getLineHeight()); - int end = myEditorScrollbarTop + myEditorTargetHeight == 0 ? yPositions.getEndOffset() + myEditor.getLineHeight() - : Math - .min(myEditorScrollbarTop + myEditorTargetHeight, yPositions.getEndOffset() + myEditor.getLineHeight()); - ProperTextRange adj = new ProperTextRange(start, Math.max(end, start)); + if (myDirtyYPositions != WHOLE_DOCUMENT) { + int start = Math.max(0, yPositions.getStartOffset() - myEditor.getLineHeight()); + int end = myEditorScrollbarTop + myEditorTargetHeight == 0 ? yPositions.getEndOffset() + myEditor.getLineHeight() + : Math + .min(myEditorScrollbarTop + myEditorTargetHeight, yPositions.getEndOffset() + myEditor.getLineHeight()); + ProperTextRange adj = new ProperTextRange(start, Math.max(end, start)); - myDirtyYPositions = myDirtyYPositions == null ? adj : myDirtyYPositions.union(adj); + myDirtyYPositions = myDirtyYPositions == null ? adj : myDirtyYPositions.union(adj); + } myEditorScrollbarTop = 0; myEditorSourceHeight = 0; @@ -1137,7 +1150,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark myHighlighters.add(rangeHighlighter); } Collections.sort(myHighlighters, new Comparator<RangeHighlighterEx>() { - public int compare(RangeHighlighterEx ex1, RangeHighlighterEx ex2) { + public int compare(@NotNull RangeHighlighterEx ex1, @NotNull RangeHighlighterEx ex2) { LogicalPosition startPos1 = myEditor.offsetToLogicalPosition(ex1.getAffectedAreaStartOffset()); LogicalPosition startPos2 = myEditor.offsetToLogicalPosition(ex2.getAffectedAreaStartOffset()); if (startPos1.line != startPos2.line) return 0; @@ -1167,7 +1180,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { if (myVisualLine ==-1) return; Dimension size = getPreferredSize(); EditorGutterComponentEx gutterComponentEx = myEditor.getGutterComponentEx(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java new file mode 100644 index 000000000000..5a9c704173aa --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java @@ -0,0 +1,323 @@ +/* + * 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.editor.impl; + +import com.intellij.codeStyle.CodeStyleFacade; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.LazyRangeMarkerFactory; +import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.WeakList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class LazyRangeMarkerFactoryImpl extends LazyRangeMarkerFactory { + private final Project myProject; + private static final Key<WeakList<LazyMarker>> LAZY_MARKERS_KEY = Key.create("LAZY_MARKERS_KEY"); + + public LazyRangeMarkerFactoryImpl(@NotNull Project project, @NotNull final FileDocumentManager fileDocumentManager) { + myProject = project; + + EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentAdapter() { + @Override + public void beforeDocumentChange(DocumentEvent e) { + transformRangeMarkers(e); + } + + @Override + public void documentChanged(DocumentEvent e) { + transformRangeMarkers(e); + } + + private void transformRangeMarkers(@NotNull DocumentEvent e) { + Document document = e.getDocument(); + VirtualFile file = fileDocumentManager.getFile(document); + if (file == null) { + return; + } + + WeakList<LazyMarker> lazyMarkers = getMarkers(file); + if (lazyMarkers == null) { + return; + } + + List<LazyMarker> markers = lazyMarkers.toStrongList(); + for (LazyMarker marker : markers) { + if (file.equals(marker.getFile())) { + marker.getOrCreateDelegate(); + } + } + } + }, project); + } + + static WeakList<LazyMarker> getMarkers(@NotNull VirtualFile file) { + return file.getUserData(LazyRangeMarkerFactoryImpl.LAZY_MARKERS_KEY); + } + + private static void addToLazyMarkersList(@NotNull LazyMarker marker, @NotNull VirtualFile file) { + WeakList<LazyMarker> markers = getMarkers(file); + + if (markers == null) { + markers = file.putUserDataIfAbsent(LAZY_MARKERS_KEY, new WeakList<LazyMarker>()); + } + markers.add(marker); + } + + private static void removeFromLazyMarkersList(@NotNull LazyMarker marker, @NotNull VirtualFile file) { + WeakList<LazyMarker> markers = getMarkers(file); + + if (markers != null) { + markers.remove(marker); + } + } + + @Override + @NotNull + public RangeMarker createRangeMarker(@NotNull final VirtualFile file, final int offset) { + return ApplicationManager.getApplication().runReadAction(new Computable<RangeMarker>() { + @Override + public RangeMarker compute() { + // even for already loaded document do not create range marker yet - wait until it really needed when e.g. user clicked to jump to OpenFileDescriptor + final LazyMarker marker = new OffsetLazyMarker(file, offset); + addToLazyMarkersList(marker, file); + return marker; + } + }); + } + + @Override + @NotNull + public RangeMarker createRangeMarker(@NotNull final VirtualFile file, final int line, final int column, final boolean persistent) { + return ApplicationManager.getApplication().runReadAction(new Computable<RangeMarker>() { + @Override + public RangeMarker compute() { + final Document document = FileDocumentManager.getInstance().getCachedDocument(file); + if (document != null) { + final int offset = calculateOffset(myProject, file, document, line, column); + return document.createRangeMarker(offset, offset, persistent); + } + + final LazyMarker marker = new LineColumnLazyMarker(file, line, column); + addToLazyMarkersList(marker, file); + return marker; + } + }); + } + + abstract static class LazyMarker extends UserDataHolderBase implements RangeMarker { + protected RangeMarker myDelegate; // the real range marker which is created only when document is opened, or (this) which means it's disposed + protected final VirtualFile myFile; + protected final int myInitialOffset; + + private LazyMarker(@NotNull VirtualFile file, int offset) { + myFile = file; + myInitialOffset = offset; + } + + boolean isDelegated() { + return myDelegate != null; + } + + @NotNull + public VirtualFile getFile() { + return myFile; + } + + @Nullable + protected final RangeMarker getOrCreateDelegate() { + if (myDelegate == null) { + Document document = FileDocumentManager.getInstance().getDocument(myFile); + if (document == null) { + return null; + } + myDelegate = createDelegate(myFile, document); + removeFromLazyMarkersList(this, myFile); + } + return isDisposed() ? null : myDelegate; + } + + @Nullable + protected abstract RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull Document document); + + @Override + @NotNull + public Document getDocument() { + RangeMarker delegate = getOrCreateDelegate(); + if (delegate == null) { + //noinspection ConstantConditions + return FileDocumentManager.getInstance().getDocument(myFile); + } + return delegate.getDocument(); + } + + @Override + public int getStartOffset() { + return myDelegate == null || isDisposed() ? myInitialOffset : myDelegate.getStartOffset(); + } + + public boolean isDisposed() { + return myDelegate == this; + } + + + @Override + public int getEndOffset() { + return myDelegate == null || isDisposed() ? myInitialOffset : myDelegate.getEndOffset(); + } + + @Override + public boolean isValid() { + RangeMarker delegate = getOrCreateDelegate(); + return delegate != null && !isDisposed() && delegate.isValid(); + } + + @Override + public void setGreedyToLeft(boolean greedy) { + getOrCreateDelegate().setGreedyToLeft(greedy); + } + + @Override + public void setGreedyToRight(boolean greedy) { + getOrCreateDelegate().setGreedyToRight(greedy); + } + + @Override + public boolean isGreedyToRight() { + return getOrCreateDelegate().isGreedyToRight(); + } + + @Override + public boolean isGreedyToLeft() { + return getOrCreateDelegate().isGreedyToLeft(); + } + + @Override + public void dispose() { + assert !isDisposed(); + RangeMarker delegate = myDelegate; + if (delegate == null) { + removeFromLazyMarkersList(this, myFile); + myDelegate = this; // mark of disposed marker + } + else { + delegate.dispose(); + } + } + } + + private static class OffsetLazyMarker extends LazyMarker { + private OffsetLazyMarker(@NotNull VirtualFile file, int offset) { + super(file, offset); + } + + @Override + public boolean isValid() { + RangeMarker delegate = myDelegate; + if (delegate == null) { + Document document = FileDocumentManager.getInstance().getDocument(myFile); + return document != null; + } + + return super.isValid(); + } + + @Override + @NotNull + public RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull final Document document) { + final int offset = Math.min(myInitialOffset, document.getTextLength()); + return document.createRangeMarker(offset, offset); + } + } + + private class LineColumnLazyMarker extends LazyMarker { + private final int myLine; + private final int myColumn; + + private LineColumnLazyMarker(@NotNull VirtualFile file, int line, int column) { + super(file, -1); + myLine = line; + myColumn = column; + } + + @Override + @Nullable + public RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull Document document) { + if (document.getTextLength() == 0 && !(myLine == 0 && myColumn == 0)) { + return null; + } + + int offset = calculateOffset(myProject, file, document, myLine, myColumn); + return document.createRangeMarker(offset, offset); + } + + @Override + public boolean isValid() { + RangeMarker delegate = myDelegate; + if (delegate == null) { + Document document = FileDocumentManager.getInstance().getDocument(myFile); + return document != null && (document.getTextLength() != 0 || myLine == 0 && myColumn == 0); + } + + return super.isValid(); + } + + @Override + public int getStartOffset() { + getOrCreateDelegate(); + return super.getStartOffset(); + } + + @Override + public int getEndOffset() { + getOrCreateDelegate(); + return super.getEndOffset(); + } + } + + private static int calculateOffset(@NotNull Project project, @NotNull VirtualFile file, @NotNull Document document, final int line, final int column) { + int offset; + if (line < document.getLineCount()) { + final int lineStart = document.getLineStartOffset(line); + final int lineEnd = document.getLineEndOffset(line); + final CharSequence docText = document.getCharsSequence(); + final int tabSize = CodeStyleFacade.getInstance(project).getTabSize(file.getFileType()); + + offset = lineStart; + int col = 0; + while (offset < lineEnd && col < column) { + col += docText.charAt(offset) == '\t' ? tabSize : 1; + offset++; + } + } + else { + offset = document.getTextLength(); + } + return offset; + } + +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java index 2f5a81153579..731d1e26a289 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java @@ -567,7 +567,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi @Override public void copySelectionToClipboard() { - CopyPasteSupport.copySelectionToClipboard(myEditor); + EditorCopyPasteHelper.getInstance().copySelectionToClipboard(myEditor); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java index 6f1b9820132b..d8b6697dc8d1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java @@ -303,14 +303,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedDocumentLi if (!isSoftWrappingEnabled()) { return 0; } - int result = 0; - FoldingModel foldingModel = myEditor.getFoldingModel(); - for (SoftWrap softWrap : myStorage.getSoftWraps()) { - if (!foldingModel.isOffsetCollapsed(softWrap.getStart())) { - result++; // Assuming that soft wrap has single line feed all the time - } - } - return result; + return myStorage.getSoftWraps().size(); // Assuming that soft wrap has single line feed all the time } /** diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java index a5c477facfb3..165ff5776756 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.fileEditor.impl; +package com.intellij.openapi.editor.impl; import com.intellij.ide.DataManager; import com.intellij.injected.editor.DocumentWindow; @@ -23,7 +23,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; -import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter; import com.intellij.openapi.project.Project; @@ -37,11 +36,12 @@ import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import java.awt.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Set; public final class TrailingSpacesStripper extends FileDocumentManagerAdapter { - public static final Key<String> OVERRIDE_STRIP_TRAILING_SPACES_KEY = Key.create("OVERRIDE_TRIM_TRAILING_SPACES_KEY"); public static final Key<Boolean> OVERRIDE_ENSURE_NEWLINE_KEY = Key.create("OVERRIDE_ENSURE_NEWLINE_KEY"); @@ -61,7 +61,7 @@ public final class TrailingSpacesStripper extends FileDocumentManagerAdapter { strip(document); } - private void strip(final Document document) { + private void strip(@NotNull final Document document) { if (!document.isWritable()) return; FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); VirtualFile file = fileDocumentManager.getFile(document); @@ -149,7 +149,7 @@ public final class TrailingSpacesStripper extends FileDocumentManagerAdapter { ((DocumentImpl)document).clearLineModificationFlagsExcept(caretLines); } - public static boolean stripIfNotCurrentLine(Document document, boolean inChangedLinesOnly) { + public static boolean stripIfNotCurrentLine(@NotNull Document document, boolean inChangedLinesOnly) { if (document instanceof DocumentWindow) { document = ((DocumentWindow)document).getDelegate(); } @@ -168,10 +168,11 @@ public final class TrailingSpacesStripper extends FileDocumentManagerAdapter { if (activeEditor != null && activeEditor.getCaretModel().supportsMultipleCarets()) { List<Caret> carets = activeEditor.getCaretModel().getAllCarets(); List<VisualPosition> visualCarets = new ArrayList<VisualPosition>(carets.size()); - List<Integer> caretOffsets = new ArrayList<Integer>(carets.size()); - for (Caret caret : carets) { + int[] caretOffsets = new int[carets.size()]; + for (int i = 0; i < carets.size(); i++) { + Caret caret = carets.get(i); visualCarets.add(caret.getVisualPosition()); - caretOffsets.add(caret.getOffset()); + caretOffsets[i] = caret.getOffset(); } markAsNeedsStrippingLater = ((DocumentImpl)document).stripTrailingSpaces(activeEditor.getProject(), inChangedLinesOnly, isVirtualSpaceEnabled, caretOffsets); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index c2353d5db207..03b03faffe03 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -277,6 +277,8 @@ public final class EditorTabbedContainer implements Disposable, CloseAction.Clos myTabs.getTabAt(index).setTooltipText(text); } + public boolean isTitleShortened(int index) { return myTabs.getTabAt(index).isTitleShortened(); } + public void setBackgroundColorAt(final int index, final Color color) { myTabs.getTabAt(index).setTabColor(color); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index 3519ab6a1af4..06a3966ade1d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -34,6 +34,7 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; +import com.intellij.openapi.ui.ThreeComponentsSplitter; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; @@ -319,6 +320,16 @@ public class EditorWindow { if (disposeIfNeeded && getTabCount() == 0) { removeFromSplitter(); + if (UISettings.getInstance().EDITOR_TAB_PLACEMENT == UISettings.TABS_NONE) { + final EditorsSplitters owner = getOwner(); + if (owner != null) { + final ThreeComponentsSplitter splitter = UIUtil.getParentOfType(ThreeComponentsSplitter.class, owner); + if (splitter != null) { + splitter.revalidate(); + splitter.repaint(); + } + } + } } else { myPanel.revalidate(); @@ -453,6 +464,10 @@ public class EditorWindow { } } + private boolean isTitleShortenedAt(int index) { + return myTabbedPane != null && myTabbedPane.isTitleShortened(index); + } + private void setBackgroundColorAt(final int index, final Color color) { if (myTabbedPane != null) { myTabbedPane.setBackgroundColorAt(index, color); @@ -897,7 +912,9 @@ public class EditorWindow { final int index = findEditorIndex(findFileComposite(file)); if (index != -1) { setTitleAt(index, EditorTabbedContainer.calcTabTitle(getManager().getProject(), file)); - setToolTipTextAt(index, UISettings.getInstance().SHOW_TABS_TOOLTIPS ? getManager().getFileTooltipText(file) : null); + setToolTipTextAt(index, UISettings.getInstance().SHOW_TABS_TOOLTIPS || isTitleShortenedAt(index) + ? getManager().getFileTooltipText(file) + : null); } } 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 f396b1eed399..091ff1171157 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 @@ -37,6 +37,7 @@ import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; import com.intellij.openapi.editor.impl.EditorFactoryImpl; +import com.intellij.openapi.editor.impl.TrailingSpacesStripper; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.fileEditor.impl.text.TextEditorImpl; 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 e36a120d609b..c7fa51f770d1 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 @@ -227,7 +227,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec fm.doWhenFocusSettlesDown(run); } else { - run.run(); + UIUtil.invokeAndWaitIfNeeded(run); } return result; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/NonProjectFileWritingAccessProvider.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/NonProjectFileWritingAccessProvider.java index 69a91ec31970..b19b2aa3e943 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/NonProjectFileWritingAccessProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/NonProjectFileWritingAccessProvider.java @@ -16,7 +16,6 @@ package com.intellij.openapi.fileEditor.impl; import com.intellij.ProjectTopics; -import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.StorageScheme; import com.intellij.openapi.components.impl.stores.IProjectStore; @@ -30,6 +29,7 @@ import com.intellij.openapi.roots.ModuleRootEvent; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NotNullLazyKey; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.*; import com.intellij.util.NotNullFunction; import com.intellij.util.NullableFunction; @@ -114,9 +114,7 @@ public class NonProjectFileWritingAccessProvider extends WritingAccessProvider { if (deniedFiles.isEmpty()) return Collections.emptyList(); - final int savedEventCount = IdeEventQueue.getInstance().getEventCount(); UnlockOption unlockOption = askToUnlock(deniedFiles); - IdeEventQueue.getInstance().setEventCount(savedEventCount); if (unlockOption == null) return deniedFiles; @@ -144,7 +142,9 @@ public class NonProjectFileWritingAccessProvider extends WritingAccessProvider { } private boolean isProjectFile(@NotNull VirtualFile file) { - if (ProjectFileIndex.SERVICE.getInstance(myProject).isInContent(file)) return true; + ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(myProject); + if (fileIndex.isInContent(file)) return true; + if (!Registry.is("ide.hide.excluded.files") && fileIndex.isExcluded(file) && !fileIndex.isUnderIgnored(file)) return true; if (myProject instanceof ProjectEx) { IProjectStore store = ((ProjectEx)myProject).getStateStore(); 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 60b42f75c22c..690e7c7d2820 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 @@ -278,19 +278,22 @@ public class TextEditorProvider implements FileEditorProvider, DumbAware { } protected void setStateImpl(final Project project, final Editor editor, final TextEditorState state){ - if (editor.getCaretModel().supportsMultipleCarets()) { - CaretModel caretModel = editor.getCaretModel(); - List<CaretState> states = new ArrayList<CaretState>(state.CARETS.length); - for (TextEditorState.CaretState caretState : state.CARETS) { - states.add(new CaretState(new LogicalPosition(caretState.LINE, caretState.COLUMN), - new LogicalPosition(caretState.SELECTION_START_LINE, caretState.SELECTION_START_COLUMN), - new LogicalPosition(caretState.SELECTION_END_LINE, caretState.SELECTION_END_COLUMN))); + if (state.CARETS != null) { + if (editor.getCaretModel().supportsMultipleCarets()) { + CaretModel caretModel = editor.getCaretModel(); + List<CaretState> states = new ArrayList<CaretState>(state.CARETS.length); + for (TextEditorState.CaretState caretState : state.CARETS) { + states.add(new CaretState(new LogicalPosition(caretState.LINE, caretState.COLUMN), + new LogicalPosition(caretState.SELECTION_START_LINE, caretState.SELECTION_START_COLUMN), + new LogicalPosition(caretState.SELECTION_END_LINE, caretState.SELECTION_END_COLUMN))); + } + caretModel.setCaretsAndSelections(states); + } + else { + LogicalPosition pos = new LogicalPosition(state.CARETS[0].LINE, state.CARETS[0].COLUMN); + editor.getCaretModel().moveToLogicalPosition(pos); + editor.getSelectionModel().removeSelection(); } - caretModel.setCaretsAndSelections(states); - } else { - LogicalPosition pos = new LogicalPosition(state.CARETS[0].LINE, state.CARETS[0].COLUMN); - editor.getCaretModel().moveToLogicalPosition(pos); - editor.getSelectionModel().removeSelection(); } EditorEx editorEx = editor instanceof EditorEx ? (EditorEx)editor : null; boolean preciselyScrollVertically = diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java index 6c78ec12f9df..4b4d19136f01 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java @@ -30,6 +30,7 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.impl.FocusManagerImpl; import com.intellij.openapi.wm.impl.IdeGlassPaneImpl; +import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; @@ -38,7 +39,6 @@ import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -230,12 +230,7 @@ public final class IdeMouseEventDispatcher { } private static void resetPopupTrigger(final MouseEvent e) { - try { - final Field popupTrigger = e.getClass().getDeclaredField("popupTrigger"); - popupTrigger.setAccessible(true); - popupTrigger.set(e, false); - } - catch (Exception ignored) { } + ReflectionUtil.setField(MouseEvent.class, e, boolean.class, "popupTrigger", false); } /** @@ -247,14 +242,7 @@ public final class IdeMouseEventDispatcher { */ public static boolean patchClickCount(final MouseEvent e) { if (e.getClickCount() != 1 && e.getButton() > 3) { - try { - final Field clickCount = e.getClass().getDeclaredField("clickCount"); - clickCount.setAccessible(true); - clickCount.set(e, 1); - return true; - } - catch (Exception ignored) { - } + ReflectionUtil.setField(MouseEvent.class, e, int.class, "clickCount", 1); } return false; } 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 7b4f5a0371ba..7c65a805ee6c 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 @@ -99,8 +99,8 @@ public class KeymapManagerImpl extends KeymapManagerEx implements PersistentStat if (Registry.is("editor.add.carets.on.double.control.arrows")) { ModifierKeyDoubleClickHandler.getInstance().registerAction(IdeActions.ACTION_EDITOR_CLONE_CARET_ABOVE, KeyEvent.VK_CONTROL, KeyEvent.VK_UP); ModifierKeyDoubleClickHandler.getInstance().registerAction(IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW, KeyEvent.VK_CONTROL, KeyEvent.VK_DOWN); - ModifierKeyDoubleClickHandler.getInstance().registerAction(IdeActions.ACTION_EDITOR_MOVE_CARET_LEFT, KeyEvent.VK_CONTROL, KeyEvent.VK_LEFT); - ModifierKeyDoubleClickHandler.getInstance().registerAction(IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT, KeyEvent.VK_CONTROL, KeyEvent.VK_RIGHT); + ModifierKeyDoubleClickHandler.getInstance().registerAction(IdeActions.ACTION_EDITOR_MOVE_CARET_LEFT_WITH_SELECTION, KeyEvent.VK_CONTROL, KeyEvent.VK_LEFT); + ModifierKeyDoubleClickHandler.getInstance().registerAction(IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION, KeyEvent.VK_CONTROL, KeyEvent.VK_RIGHT); } ourKeymapManagerInitialized = true; diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java index 28536453b713..13a8aaecb724 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ModifierKeyDoubleClickHandler.java @@ -17,10 +17,10 @@ package com.intellij.openapi.keymap.impl; import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; -import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.util.Clock; import com.intellij.openapi.util.Couple; @@ -39,6 +39,10 @@ import java.util.concurrent.atomic.AtomicLong; /** * Support for keyboard shortcuts like Control-double-click or Control-double-click+A + * + * Timings that are used in the implementation to detect double click were tuned for SearchEverywhere + * functionality (invoked on double Shift), so if you need to change them, please make sure + * SearchEverywhere behaviour remains intact. */ public class ModifierKeyDoubleClickHandler { private static final ModifierKeyDoubleClickHandler INSTANCE = new ModifierKeyDoubleClickHandler(); @@ -121,7 +125,7 @@ public class ModifierKeyDoubleClickHandler { return false; } else if (ourPressed.first.get() && ourReleased.first.get() && ourPressed.second.get() && myActionKeyCode != -1) { if (keyCode == myActionKeyCode) { - if (event.getID() == KeyEvent.KEY_RELEASED) { + if (event.getID() == KeyEvent.KEY_PRESSED) { run(keyEvent); } return true; @@ -192,7 +196,7 @@ public class ModifierKeyDoubleClickHandler { } private void run(KeyEvent event) { - final ActionManager actionManager = ActionManager.getInstance(); + final ActionManagerEx actionManager = ActionManagerEx.getInstanceEx(); final AnAction action = actionManager.getAction(myActionId); final AnActionEvent anActionEvent = new AnActionEvent(event, DataManager.getInstance().getDataContext(IdeFocusManager.findInstance().getFocusOwner()), @@ -200,7 +204,9 @@ public class ModifierKeyDoubleClickHandler { action.getTemplatePresentation(), actionManager, 0); + actionManager.fireBeforeActionPerformed(action, anActionEvent.getDataContext(), anActionEvent); action.actionPerformed(anActionEvent); + actionManager.fireAfterActionPerformed(action, anActionEvent.getDataContext(), anActionEvent); } private boolean isActionBound() { diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java index b99f19242fa9..ebb3686969f0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java @@ -393,7 +393,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi group.add(commonActionsManager.createExpandAllAction(treeExpander, myActionsTree.getTree())); group.add(commonActionsManager.createCollapseAllAction(treeExpander, myActionsTree.getTree())); - group.add(new AnAction("Edit Shortcut", "Edit Shortcut", AllIcons.Actions.Properties) { + group.add(new AnAction("Edit Shortcut", "Edit Shortcut", AllIcons.ToolbarDecorator.Edit) { { registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)), myActionsTree.getTree()); } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java b/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java index 104dc7b30ec7..d7c7dca2f19b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java @@ -1,18 +1,64 @@ +/* + * 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 diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java b/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java index e454099318ea..0ecb19a7ce8e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java @@ -1,3 +1,18 @@ +/* + * 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; diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java new file mode 100644 index 000000000000..9812165932a2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java @@ -0,0 +1,52 @@ +/* + * 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/TabbedConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/TabbedConfigurable.java index 35b75fe7c8d2..47d3881ee815 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/TabbedConfigurable.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/TabbedConfigurable.java @@ -33,6 +33,7 @@ public abstract class TabbedConfigurable extends CompositeConfigurable<Configura myParent = parent; } + @Override public JComponent createComponent() { myTabbedPane = new TabbedPaneWrapper(myParent); createConfigurableTabs(); 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 6ee4a1b947ef..07e5a93670e6 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 @@ -36,6 +36,7 @@ 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.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeGlassPaneUtil; import com.intellij.ui.DocumentAdapter; @@ -366,6 +367,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myOwnDetails.setContent(myContentWrapper); myOwnDetails.setBannerMinHeight(mySearchWrapper.getHeight()); myOwnDetails.setText(getBannerText(configurable)); + if (Registry.is("ide.file.settings.order.new")) { + myOwnDetails.forProject(myTree.getConfigurableProject(configurable)); + } final ConfigurableContent content = myConfigurable2Content.get(current); 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 5524df2bb2cb..e0ab24815ece 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 @@ -308,7 +308,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl myHandle.setOpaque(false); content.add(myHandle, BorderLayout.WEST); content.add(myComponent, BorderLayout.CENTER); - myProjectIcon = new JLabel(" ", AllIcons.General.ProjectConfigurable, SwingConstants.LEFT); + myProjectIcon = new JLabel(" ", SwingConstants.LEFT); myProjectIcon.setOpaque(true); content.add(myProjectIcon, BorderLayout.EAST); myRendererComponent.add(content, BorderLayout.CENTER); @@ -407,32 +407,17 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl Project project = getConfigurableProject(base); if (project != null && Registry.is("ide.file.settings.order.new")) { myProjectIcon.setBackground(selected ? getSelectionBackground() : getBackground()); + myProjectIcon.setIcon(selected ? AllIcons.General.ProjectConfigurableSelected : AllIcons.General.ProjectConfigurable); myProjectIcon.setVisible(true); - tree.setToolTipText(OptionsBundle.message(project.isDefault() + myProjectIcon.setToolTipText(OptionsBundle.message(project.isDefault() ? "configurable.default.project.tooltip" : "configurable.current.project.tooltip")); } else { myProjectIcon.setVisible(false); - tree.setToolTipText(null); } return result; } - private Project getConfigurableProject(SimpleNode node) { - if (node == null) { - return null; - } - if (node instanceof EditorNode) { - EditorNode editor = (EditorNode)node; - Configurable configurable = editor.getConfigurable(); - if (configurable instanceof ConfigurableWrapper) { - ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; - return wrapper.getExtensionPoint().getProject(); - } - } - return getConfigurableProject(node.getParent()); - } - protected JComponent createItemComponent() { myTextLabel = new ErrorLabel(); return myTextLabel; @@ -642,6 +627,24 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } @Override + public final String getToolTipText(MouseEvent event) { + if (event != null) { + Point point = event.getPoint(); + Component component = getDeepestRendererComponentAt(point.x, point.y); + if (component instanceof JLabel) { + JLabel label = (JLabel)component; + if (label.getIcon() != null) { + String text = label.getToolTipText(); + if (text != null) { + return text; + } + } + } + } + return super.getToolTipText(event); + } + + @Override protected boolean paintNodes() { return false; } @@ -877,4 +880,27 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } } + + Project getConfigurableProject(Configurable configurable) { + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + return wrapper.getExtensionPoint().getProject(); + } + return getConfigurableProject(myConfigurable2Node.get(configurable)); + } + + private static Project getConfigurableProject(SimpleNode node) { + if (node == null) { + return null; + } + if (node instanceof EditorNode) { + EditorNode editor = (EditorNode)node; + Configurable configurable = editor.getConfigurable(); + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + return wrapper.getExtensionPoint().getProject(); + } + } + return getConfigurableProject(node.getParent()); + } } 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 a393d937dcfb..27bb8eb5f245 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 @@ -56,7 +56,10 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ private final ScheduledFuture<?> myCheckCancelledFuture; public ProgressManagerImpl(Application application) { - if (/*!application.isUnitTestMode() && */!DISABLED) { + if (DISABLED) { + myCheckCancelledFuture = null; + } + else { myCheckCancelledFuture = JobScheduler.getScheduler().scheduleWithFixedDelay(new Runnable() { @Override public void run() { @@ -65,9 +68,6 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ } }, 0, 10, TimeUnit.MILLISECONDS); } - else { - myCheckCancelledFuture = null; - } } @Override @@ -105,8 +105,8 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ private static class NonCancelableIndicator extends EmptyProgressIndicator implements NonCancelableSection { private final ProgressIndicator myOld; - private NonCancelableIndicator(ProgressIndicator old) { - myOld = old; + private NonCancelableIndicator() { + myOld = myThreadIndicator.get(); } @Override @@ -124,16 +124,17 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{ } } + @NotNull @Override public final NonCancelableSection startNonCancelableSection() { - NonCancelableIndicator nonCancelor = new NonCancelableIndicator(myThreadIndicator.get()); + NonCancelableIndicator nonCancelor = new NonCancelableIndicator(); myThreadIndicator.set(nonCancelor); return nonCancelor; } @Override public void executeNonCancelableSection(@NotNull Runnable runnable) { - executeProcessUnderProgress(runnable, new NonCancelableIndicator(getProgressIndicator())); + executeProcessUnderProgress(runnable, new NonCancelableIndicator()); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index 6ffa084931b4..d1abc0816845 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -49,6 +49,7 @@ import com.intellij.ui.components.JBLayeredPane; import com.intellij.ui.mac.foundation.Foundation; import com.intellij.ui.mac.foundation.ID; import com.intellij.ui.mac.foundation.MacUtil; +import com.intellij.util.ReflectionUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -59,7 +60,6 @@ import java.awt.*; import java.awt.event.*; import java.awt.image.BufferStrategy; import java.lang.ref.WeakReference; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -887,18 +887,12 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra if (rootPane != null) { // Workaround for bug in native code to hold rootPane try { - Field field = rootPane.getClass().getDeclaredField("glassPane"); - field.setAccessible(true); - field.set(rootPane, null); + ReflectionUtil.resetField(rootPane.getClass(), null, "glassPane"); + ReflectionUtil.resetField(rootPane.getClass(), null, "contentPane"); - field = rootPane.getClass().getDeclaredField("contentPane"); - field.setAccessible(true); - field.set(rootPane, null); rootPane = null; - field = Window.class.getDeclaredField("windowListener"); - field.setAccessible(true); - field.set(this, null); + ReflectionUtil.resetField(Window.class, null, "windowListener"); } catch (Exception ignored) { } @@ -906,9 +900,7 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra // http://bugs.sun.com/view_bug.do?bug_id=6614056 try { - final Field field = Dialog.class.getDeclaredField("modalDialogs"); - field.setAccessible(true); - final List<?> list = (List<?>)field.get(null); + final List<?> list = ReflectionUtil.getField(Dialog.class, null, null, "modalDialogs"); list.remove(this); } catch (final Exception ignored) { diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java index 9dd3dd21f879..7e1c81ad2fbc 100755 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java @@ -618,12 +618,21 @@ public final class UpdateChecker { Future<?> downloadThreadFuture = ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { public void run() { try { - URL requestUrl = new URL(url); - if (!StandardFileSystems.FILE_PROTOCOL.equals(requestUrl.getProtocol())) { - HttpConfigurable.getInstance().prepareURL(url); - requestUrl = new URL(url + (url.contains("?") ? "&" : "?") + "build=" + ApplicationInfo.getInstance().getBuild().asString()); + final String urlToCheck; + if (!StandardFileSystems.FILE_PROTOCOL.equals(new URL(url).getProtocol())) { + urlToCheck = url + (url.contains("?") ? "&" : "?") + "build=" + ApplicationInfo.getInstance().getBuild().asString(); + } else { + urlToCheck = url; } - inputStreams[0] = requestUrl.openStream(); + + HttpURLConnection connection = ApplicationManager.getApplication() != null ? + HttpConfigurable.getInstance().openHttpConnection(urlToCheck) : + (HttpURLConnection)new URL(urlToCheck).openConnection(); + connection.setReadTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + connection.setConnectTimeout(HttpConfigurable.CONNECTION_TIMEOUT); + connection.connect(); + + inputStreams[0] = connection.getInputStream(); } catch (IOException e) { exception[0] = e; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/HttpFileSystemBase.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/HttpFileSystemBase.java index 7177138d1d9a..a2d32fd1307f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/HttpFileSystemBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/HttpFileSystemBase.java @@ -67,7 +67,9 @@ public abstract class HttpFileSystemBase extends HttpFileSystem { @Override @NotNull public VirtualFile createChild(@NotNull VirtualFile parent, @NotNull String name, boolean isDirectory) { - return getRemoteFileManager().getOrCreateFile((VirtualFileImpl)parent, Urls.newFromIdea(parent.getUrl() + '/' + name), parent.getPath() + '/' + name, isDirectory); + String parentPath = parent.getPath(); + boolean hasEndSlash = parentPath.charAt(parentPath.length() - 1) == '/'; + return getRemoteFileManager().getOrCreateFile((HttpVirtualFileImpl)parent, Urls.newFromIdea(parent.getUrl() + (hasEndSlash ? "" : '/') + name), parentPath + (hasEndSlash ? "" : '/') + name, isDirectory); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/VirtualFileImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/HttpVirtualFileImpl.java index ba49cfb5a690..a8a1a447d66c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/VirtualFileImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/HttpVirtualFileImpl.java @@ -35,7 +35,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.List; -class VirtualFileImpl extends HttpVirtualFile { +class HttpVirtualFileImpl extends HttpVirtualFile { private final HttpFileSystemBase myFileSystem; @Nullable private final RemoteFileInfoImpl myFileInfo; private FileType myInitialFileType; @@ -45,7 +45,7 @@ class VirtualFileImpl extends HttpVirtualFile { private List<VirtualFile> myChildren; - VirtualFileImpl(@NotNull HttpFileSystemBase fileSystem, @Nullable VirtualFileImpl parent, String path, @Nullable RemoteFileInfoImpl fileInfo) { + HttpVirtualFileImpl(@NotNull HttpFileSystemBase fileSystem, @Nullable HttpVirtualFileImpl parent, String path, @Nullable RemoteFileInfoImpl fileInfo) { if (parent != null) { if (parent.myChildren == null) { parent.myChildren = new SmartList<VirtualFile>(); @@ -63,7 +63,7 @@ class VirtualFileImpl extends HttpVirtualFile { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - VirtualFileImpl file = VirtualFileImpl.this; + HttpVirtualFileImpl file = HttpVirtualFileImpl.this; FileDocumentManager.getInstance().reloadFiles(file); if (!localFile.getFileType().equals(myInitialFileType)) { FileContentUtilCore.reparseFiles(file); @@ -135,8 +135,7 @@ class VirtualFileImpl extends HttpVirtualFile { @Override public VirtualFile getParent() { - if (myParentPath == null) return null; - return myFileSystem.findFileByPath(myParentPath, true); + return myParentPath == null ? null : myFileSystem.findFileByPath(myParentPath, true); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/RemoteFileManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/RemoteFileManagerImpl.java index b69c0befd376..904233a35cdf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/RemoteFileManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/RemoteFileManagerImpl.java @@ -17,7 +17,6 @@ package com.intellij.openapi.vfs.impl.http; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.ex.http.HttpVirtualFileListener; import com.intellij.util.EventDispatcher; @@ -35,7 +34,10 @@ import java.util.Map; */ public class RemoteFileManagerImpl extends RemoteFileManager implements Disposable { private final LocalFileStorage myStorage; - private final Map<Pair<Boolean, Url>, VirtualFileImpl> myRemoteFiles = new THashMap<Pair<Boolean, Url>, VirtualFileImpl>(); + + private final Map<Url, HttpVirtualFileImpl> remoteFiles = new THashMap<Url, HttpVirtualFileImpl>(); + private final Map<Url, HttpVirtualFileImpl> remoteDirectories = new THashMap<Url, HttpVirtualFileImpl>(); + private final EventDispatcher<HttpVirtualFileListener> myDispatcher = EventDispatcher.create(HttpVirtualFileListener.class); private final List<RemoteContentProvider> myProviders = new ArrayList<RemoteContentProvider>(); private final DefaultRemoteContentProvider myDefaultRemoteContentProvider; @@ -55,19 +57,19 @@ public class RemoteFileManagerImpl extends RemoteFileManager implements Disposab return myDefaultRemoteContentProvider; } - public synchronized VirtualFileImpl getOrCreateFile(@Nullable VirtualFileImpl parent, @NotNull Url url, @NotNull String path, final boolean directory) { - Pair<Boolean, Url> key = Pair.create(directory, url); - VirtualFileImpl file = myRemoteFiles.get(key); + public synchronized HttpVirtualFileImpl getOrCreateFile(@Nullable HttpVirtualFileImpl parent, @NotNull Url url, @NotNull String path, final boolean directory) { + Map<Url, HttpVirtualFileImpl> cache = directory ? remoteDirectories : remoteFiles; + HttpVirtualFileImpl file = cache.get(url); if (file == null) { if (directory) { - file = new VirtualFileImpl(getHttpFileSystem(url), parent, path, null); + file = new HttpVirtualFileImpl(getHttpFileSystem(url), parent, path, null); } else { RemoteFileInfoImpl fileInfo = new RemoteFileInfoImpl(url, this); - file = new VirtualFileImpl(getHttpFileSystem(url), parent, path, fileInfo); + file = new HttpVirtualFileImpl(getHttpFileSystem(url), parent, path, fileInfo); fileInfo.addDownloadingListener(new MyDownloadingListener(file)); } - myRemoteFiles.put(key, file); + cache.put(url, file); } return file; } @@ -133,9 +135,9 @@ public class RemoteFileManagerImpl extends RemoteFileManager implements Disposab } private class MyDownloadingListener extends FileDownloadingAdapter { - private final VirtualFileImpl myFile; + private final HttpVirtualFileImpl myFile; - public MyDownloadingListener(final VirtualFileImpl file) { + public MyDownloadingListener(final HttpVirtualFileImpl file) { myFile = file; } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandler.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandler.java index efaf84d08b8e..98c3884cc993 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandler.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandler.java @@ -22,6 +22,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.NotNullLazyValue; +import com.intellij.openapi.util.ShutDownTracker; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; @@ -30,6 +31,7 @@ import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VfsBundle; import com.intellij.openapi.vfs.impl.ZipHandler; import com.intellij.openapi.vfs.newvfs.persistent.FSRecords; +import com.intellij.openapi.vfs.newvfs.persistent.FlushingDaemon; import com.intellij.util.io.DataExternalizer; import com.intellij.util.io.EnumeratorStringDescriptor; import com.intellij.util.io.IOUtil; @@ -169,7 +171,6 @@ public class JarHandler extends ZipHandler { info = new CacheLibraryInfo(mirrorFile.getName(), originalAttributes.lastModified, originalAttributes.length); CacheLibraryInfo.ourCachedLibraryInfo.put(path, info); - CacheLibraryInfo.ourCachedLibraryInfo.force(); return mirrorFile; } catch (IOException ex) { @@ -260,6 +261,23 @@ public class JarHandler extends ZipHandler { } assert info != null; ourCachedLibraryInfo = info; + FlushingDaemon.everyFiveSeconds(new Runnable() { + @Override + public void run() { + flushCachedLibraryInfos(); + } + }); + + ShutDownTracker.getInstance().registerShutdownTask(new Runnable() { + @Override + public void run() { + flushCachedLibraryInfos(); + } + }); + } + + private static void flushCachedLibraryInfos() { + if (ourCachedLibraryInfo.isDirty()) ourCachedLibraryInfo.force(); } private CacheLibraryInfo(@NotNull String path, long time, long length) { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/ex/IdeFocusTraversalPolicy.java b/platform/platform-impl/src/com/intellij/openapi/wm/ex/IdeFocusTraversalPolicy.java index ba44d2b21d10..2ecb28327857 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/ex/IdeFocusTraversalPolicy.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/ex/IdeFocusTraversalPolicy.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,17 +19,15 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.impl.EditorComponentImpl; import com.intellij.openapi.fileEditor.impl.EditorWindowHolder; import com.intellij.openapi.util.Computable; -import org.jetbrains.annotations.NonNls; +import com.intellij.util.ReflectionUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.text.JTextComponent; import java.awt.*; -import java.lang.reflect.Field; public class IdeFocusTraversalPolicy extends LayoutFocusTraversalPolicyExt { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy"); - @NonNls private static final String FOCUS_TRAVERSAL_POLICY_FIELD = "focusTraversalPolicy"; protected Component getDefaultComponentImpl(Container focusCycleRoot) { if (!(focusCycleRoot instanceof JComponent)) { @@ -103,15 +101,7 @@ public class IdeFocusTraversalPolicy extends LayoutFocusTraversalPolicyExt { } private static FocusTraversalPolicy getFocusTraversalPolicyAwtImpl(final JComponent component) { - try { - final Field field = Container.class.getDeclaredField(FOCUS_TRAVERSAL_POLICY_FIELD); - field.setAccessible(true); - return (FocusTraversalPolicy)field.get(component); - } - catch (Exception e) { - LOG.error(e); - return null; - } + return ReflectionUtil.getField(Container.class, component, FocusTraversalPolicy.class, "focusTraversalPolicy"); } protected final boolean accept(final Component aComponent) { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java index 83696e07c7f8..71f964504102 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeGlassPaneImpl.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. @@ -20,8 +20,9 @@ import com.intellij.ide.IdeTooltipManager; import com.intellij.ide.dnd.DnDAware; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.ui.Divider; import com.intellij.openapi.ui.Painter; -import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.impl.GlassPaneDialogWrapperPeer; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupFactory; @@ -42,6 +43,8 @@ import java.util.List; public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEventQueue.EventDispatcher, Painter.Listener { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.IdeGlassPaneImpl"); + private final List<EventListener> myMouseListeners = new ArrayList<EventListener>(); private final Set<EventListener> mySortedMouseListeners = new TreeSet<EventListener>(new Comparator<EventListener>() { @Override @@ -360,9 +363,12 @@ public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEvent } else { cursor = Cursor.getDefaultCursor(); - getRootPane().setCursor(cursor); - - + JRootPane rootPane = getRootPane(); + if (rootPane != null) { + rootPane.setCursor(cursor); + } else { + LOG.warn("Root pane is null. Event: " + e); + } restoreLastComponent(null); myLastOriginalCursor = null; myLastCursorComponent = null; @@ -375,7 +381,7 @@ public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEvent private boolean canProcessCursorFor(Component target) { if (target instanceof JMenu || target instanceof JMenuItem || - target instanceof Splitter.Divider || + target instanceof Divider || target instanceof JSeparator || (target instanceof JEditorPane && ((JEditorPane)target).getEditorKit() instanceof HTMLEditorKit)) { return false; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java index d6e1d9994500..8b78b1632f79 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java @@ -27,9 +27,11 @@ import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.*; @@ -41,7 +43,7 @@ import com.intellij.ui.content.ContentFactory; import com.intellij.ui.content.ContentManager; import com.intellij.ui.content.ContentManagerListener; import com.intellij.util.ArrayUtil; -import org.jetbrains.annotations.NonNls; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,12 +52,18 @@ import javax.swing.event.HyperlinkListener; import java.awt.*; import java.awt.event.InputEvent; import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.Collections; +import java.util.*; import java.util.List; @SuppressWarnings({"ConstantConditions"}) public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { + private final Map<String, ToolWindow> myToolWindows = new HashMap<String, ToolWindow>(); + private final Project myProject; + + public ToolWindowHeadlessManagerImpl(Project project) { + myProject = project; + } + @Override public boolean canShowNotification(@NotNull String toolWindowId) { return false; @@ -65,7 +73,184 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) { } - public static final ToolWindow HEADLESS_WINDOW = new ToolWindowEx() { + private ToolWindow doRegisterToolWindow(final String id, @Nullable Disposable parentDisposable) { + MockToolWindow tw = new MockToolWindow(myProject); + myToolWindows.put(id, tw); + if (parentDisposable != null) { + Disposer.register(parentDisposable, new Disposable() { + @Override + public void dispose() { + unregisterToolWindow(id); + } + }); + } + return tw; + } + + @Override + public ToolWindow registerToolWindow(@NotNull String id, + @NotNull JComponent component, + @NotNull ToolWindowAnchor anchor, + Disposable parentDisposable, + boolean canWorkInDumbMode) { + return doRegisterToolWindow(id, parentDisposable); + } + + @Override + public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @NotNull ToolWindowAnchor anchor) { + return doRegisterToolWindow(id, null); + } + + @Override + public ToolWindow registerToolWindow(@NotNull String id, + @NotNull JComponent component, + @NotNull ToolWindowAnchor anchor, + Disposable parentDisposable, + boolean canWorkInDumbMode, + boolean canCloseContents) { + return doRegisterToolWindow(id, parentDisposable); + } + + @Override + public ToolWindow registerToolWindow(@NotNull String id, + @NotNull JComponent component, + @NotNull ToolWindowAnchor anchor, + Disposable parentDisposable) { + return doRegisterToolWindow(id, parentDisposable); + } + + @Override + public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) { + return doRegisterToolWindow(id, null); + } + + @Override + public ToolWindow registerToolWindow(@NotNull final String id, + final boolean canCloseContent, + @NotNull final ToolWindowAnchor anchor, + final boolean secondary) { + return doRegisterToolWindow(id, null); + } + + @Override + public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor, + final Disposable parentDisposable, final boolean dumbAware) { + return doRegisterToolWindow(id, parentDisposable); + } + + @Override + public void unregisterToolWindow(@NotNull String id) { + myToolWindows.remove(id); + } + + @Override + public void activateEditorComponent() { + } + + @Override + public boolean isEditorComponentActive() { + return false; + } + + @Override + public String[] getToolWindowIds() { + return ArrayUtil.EMPTY_STRING_ARRAY; + } + + @Override + public String getActiveToolWindowId() { + return null; + } + + @Override + public ToolWindow getToolWindow(String id) { + return myToolWindows.get(id); + } + + @Override + public void invokeLater(Runnable runnable) { + } + + @Override + public IdeFocusManager getFocusManager() { + return IdeFocusManagerHeadless.INSTANCE; + } + + @Override + public void notifyByBalloon(@NotNull final String toolWindowId, + @NotNull final MessageType type, + @NotNull final String text, + @Nullable final Icon icon, + @Nullable final HyperlinkListener listener) { + } + + @Override + public Balloon getToolWindowBalloon(String id) { + return null; + } + + @Override + public void initToolWindow(ToolWindowEP bean) { + + } + + @Override + public void addToolWindowManagerListener(@NotNull ToolWindowManagerListener l) { + + } + + @Override + public void removeToolWindowManagerListener(@NotNull ToolWindowManagerListener l) { + } + + @Override + public String getLastActiveToolWindowId() { + return null; + } + + @Override + public String getLastActiveToolWindowId(Condition<JComponent> condition) { + return null; + } + + @Override + public DesktopLayout getLayout() { + return new DesktopLayout(); + } + + @Override + public void setLayoutToRestoreLater(DesktopLayout layout) { + } + + @Override + public DesktopLayout getLayoutToRestoreLater() { + return new DesktopLayout(); + } + + @Override + public void setLayout(@NotNull DesktopLayout layout) { + } + + @Override + public void clearSideStack() { + } + + @Override + public void hideToolWindow(@NotNull final String id, final boolean hideSide) { + } + + @Override + public List<String> getIdsOn(@NotNull final ToolWindowAnchor anchor) { + return new ArrayList<String>(); + } + + public static class MockToolWindow implements ToolWindowEx { + ContentManager myContentManager = new MockContentManager(); + + public MockToolWindow(@NotNull Project project) { + Disposer.register(project, myContentManager); + } + @Override public boolean isActive() { return false; @@ -206,7 +391,7 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { @Override public ContentManager getContentManager() { - return MOCK_CONTENT_MANAGER; + return myContentManager; } @Override @@ -270,9 +455,9 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { public boolean isUseLastFocusedOnActivation() { return false; } - }; + } - @NonNls private static final ContentManager MOCK_CONTENT_MANAGER = new ContentManager() { + private static class MockContentManager implements ContentManager { private final List<Content> myContents = new ArrayList<Content>(); private Content mySelected; @@ -284,27 +469,33 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { @Override public void addContent(@NotNull final Content content) { + myContents.add(content); + if (mySelected == null) mySelected = content; } @Override public void addContent(@NotNull Content content, int order) { myContents.add(order, content); + if (mySelected == null) mySelected = content; } @Override public void addContent(@NotNull final Content content, final Object constraints) { + addContent(content); } @Override - public void addContentManagerListener(@NotNull final ContentManagerListener l) { + public void addSelectedContent(@NotNull final Content content) { + addContent(content); + setSelectedContent(content); } @Override - public void addDataProvider(@NotNull final DataProvider provider) { + public void addContentManagerListener(@NotNull final ContentManagerListener l) { } @Override - public void addSelectedContent(@NotNull final Content content) { + public void addDataProvider(@NotNull final DataProvider provider) { } @Override @@ -319,6 +510,9 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { @Override public Content findContent(final String displayName) { + for (Content each : myContents) { + if (each.getDisplayName().equals(displayName)) return each; + } return null; } @@ -359,18 +553,24 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { @Override public Content getContent(final JComponent component) { + Content[] contents = getContents(); + for (Content content : contents) { + if (Comparing.equal(component, content.getComponent())) { + return content; + } + } return null; } @Override @Nullable public Content getContent(final int index) { - return null; + return myContents.get(index); } @Override public int getContentCount() { - return 0; + return myContents.size(); } @Override @@ -381,7 +581,7 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { @Override public int getIndexOfContent(final Content content) { - return -1; + return myContents.indexOf(content); } @Override @@ -393,35 +593,37 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { @Override @NotNull public Content[] getSelectedContents() { - return new Content[0]; + return mySelected != null ? new Content[]{mySelected} : new Content[0]; } @Override public boolean isSelected(@NotNull final Content content) { - return false; + return content == mySelected; } @Override public void removeAllContents(final boolean dispose) { for (int i = myContents.size() - 1; i >= 0; i--) { Content content = myContents.get(i); - removeContent(content, true); + removeContent(content, dispose); } mySelected = null; } @Override public boolean removeContent(@NotNull final Content content, final boolean dispose) { - Disposer.dispose(content); + if (dispose) Disposer.dispose(content); + boolean result = myContents.remove(content); if (mySelected == content) { - mySelected = null; + mySelected = ContainerUtil.getFirstItem(myContents); } - return myContents.remove(content); + return result; } @NotNull @Override public ActionCallback removeContent(@NotNull Content content, boolean dispose, boolean trackFocus, boolean implicitFocus) { + removeContent(content, dispose); return new ActionCallback.Done(); } @@ -451,33 +653,36 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { @NotNull @Override public ActionCallback setSelectedContentCB(@NotNull Content content) { + setSelectedContent(content); return new ActionCallback.Done(); } @Override public void setSelectedContent(@NotNull final Content content, final boolean requestFocus) { + setSelectedContent(content); } @NotNull @Override public ActionCallback setSelectedContentCB(@NotNull final Content content, final boolean requestFocus) { - return new ActionCallback.Done(); + return setSelectedContentCB(content); } @Override public void setSelectedContent(@NotNull Content content, boolean requestFocus, boolean forcedFocus) { + setSelectedContent(content); } @NotNull @Override public ActionCallback setSelectedContentCB(@NotNull final Content content, final boolean requestFocus, final boolean forcedFocus) { - return new ActionCallback.Done(); + return setSelectedContentCB(content); } @NotNull @Override public ActionCallback setSelectedContent(@NotNull Content content, boolean requestFocus, boolean forcedFocus, boolean implicit) { - return new ActionCallback.Done(); + return setSelectedContentCB(content); } @NotNull @@ -506,161 +711,4 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { public ContentFactory getFactory() { return ServiceManager.getService(ContentFactory.class); } - }; - - @Override - public ToolWindow registerToolWindow(@NotNull String id, - @NotNull JComponent component, - @NotNull ToolWindowAnchor anchor, - Disposable parentDisposable, - boolean canWorkInDumbMode) { - return HEADLESS_WINDOW; - } - - @Override - public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @NotNull ToolWindowAnchor anchor) { - return HEADLESS_WINDOW; - } - - @Override - public ToolWindow registerToolWindow(@NotNull String id, - @NotNull JComponent component, - @NotNull ToolWindowAnchor anchor, - Disposable parentDisposable, - boolean canWorkInDumbMode, - boolean canCloseContents) { - return HEADLESS_WINDOW; - } - - @Override - public ToolWindow registerToolWindow(@NotNull String id, - @NotNull JComponent component, - @NotNull ToolWindowAnchor anchor, - Disposable parentDisposable) { - return HEADLESS_WINDOW; - } - - @Override - public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) { - return HEADLESS_WINDOW; - } - - @Override - public ToolWindow registerToolWindow(@NotNull final String id, - final boolean canCloseContent, - @NotNull final ToolWindowAnchor anchor, - final boolean secondary) { - return HEADLESS_WINDOW; - } - - @Override - public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor, - final Disposable parentDisposable, final boolean dumbAware) { - return HEADLESS_WINDOW; - } - - @Override - public void unregisterToolWindow(@NotNull String id) { - } - - @Override - public void activateEditorComponent() { - } - - @Override - public boolean isEditorComponentActive() { - return false; - } - - @Override - public String[] getToolWindowIds() { - return ArrayUtil.EMPTY_STRING_ARRAY; - } - - @Override - public String getActiveToolWindowId() { - return null; - } - - @Override - public ToolWindow getToolWindow(String id) { - return HEADLESS_WINDOW; - } - - @Override - public void invokeLater(Runnable runnable) { - } - - @Override - public IdeFocusManager getFocusManager() { - return IdeFocusManagerHeadless.INSTANCE; - } - - @Override - public void notifyByBalloon(@NotNull final String toolWindowId, - @NotNull final MessageType type, - @NotNull final String text, - @Nullable final Icon icon, - @Nullable final HyperlinkListener listener) { - } - - @Override - public Balloon getToolWindowBalloon(String id) { - return null; - } - - @Override - public void initToolWindow(ToolWindowEP bean) { - - } - - @Override - public void addToolWindowManagerListener(@NotNull ToolWindowManagerListener l) { - - } - - @Override - public void removeToolWindowManagerListener(@NotNull ToolWindowManagerListener l) { - } - - @Override - public String getLastActiveToolWindowId() { - return null; - } - - @Override - public String getLastActiveToolWindowId(Condition<JComponent> condition) { - return null; - } - - @Override - public DesktopLayout getLayout() { - return new DesktopLayout(); - } - - @Override - public void setLayoutToRestoreLater(DesktopLayout layout) { - } - - @Override - public DesktopLayout getLayoutToRestoreLater() { - return new DesktopLayout(); - } - - @Override - public void setLayout(@NotNull DesktopLayout layout) { - } - - @Override - public void clearSideStack() { - } - - @Override - public void hideToolWindow(@NotNull final String id, final boolean hideSide) { - } - - @Override - public List<String> getIdsOn(@NotNull final ToolWindowAnchor anchor) { - return new ArrayList<String>(); - } -} + }} diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/X11UiUtil.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/X11UiUtil.java index 6c6bcd137949..120579bcb809 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/X11UiUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/X11UiUtil.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. @@ -16,9 +16,11 @@ package com.intellij.openapi.wm.impl; import com.intellij.Patches; +import com.intellij.execution.util.ExecUtil; import com.intellij.openapi.diagnostic.Logger; 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.IdeFrame; import com.intellij.openapi.wm.WindowManager; import org.jetbrains.annotations.Nullable; @@ -29,6 +31,8 @@ import java.awt.*; import java.awt.peer.ComponentPeer; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static com.intellij.util.ArrayUtil.newLongArray; import static com.intellij.util.containers.ContainerUtil.newHashSet; @@ -254,11 +258,17 @@ public class X11UiUtil { setWM("MARCO_WM", "METACITY_WM"); } else if ("awesome".equals(wmName)) { - setWM("SAWFISH_WM"); + String version = getAwesomeWMVersion(); + if (StringUtil.compareVersionNumbers(version, "3.5") >= 0) { + setWM("SAWFISH_WM"); + } + else if (version != null) { + setWM("OTHER_NONREPARENTING_WM", "LG3D_WM"); + } } } - catch (Throwable e) { - LOG.warn(e); + catch (Throwable t) { + LOG.warn(t); } } @@ -273,6 +283,7 @@ public class X11UiUtil { if (id != null) { field(xwmClass, "awt_wmgr").set(null, id); field(xwmClass, "WMID").set(xwm, id); + LOG.info("impersonated WM: " + wmConstant); break; } } @@ -281,6 +292,23 @@ public class X11UiUtil { } } + @Nullable + private static String getAwesomeWMVersion() { + try { + String version = ExecUtil.execAndReadLine("awesome", "--version"); + if (version != null) { + Matcher m = Pattern.compile("awesome v([0-9.]+)").matcher(version); + if (m.find()) { + return m.group(1); + } + } + } + catch (Throwable t) { + LOG.warn(t); + } + return null; + } + // full-screen support public static boolean isFullScreenSupported() { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInEditorComponentCmd.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInEditorComponentCmd.java index 7a772e7aed34..2c681856a328 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInEditorComponentCmd.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInEditorComponentCmd.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. @@ -19,11 +19,13 @@ */ package com.intellij.openapi.wm.impl.commands; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.fileEditor.impl.EditorWithProviderComposite; import com.intellij.openapi.fileEditor.impl.EditorsSplitters; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Expirable; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.impl.FloatingDecorator; import com.intellij.openapi.wm.impl.IdeFrameImpl; @@ -44,10 +46,16 @@ public final class RequestFocusInEditorComponentCmd extends FinalizableCommand{ private final IdeFocusManager myFocusManager; private final Expirable myTimestamp; + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.commands.RequestFocusInEditorComponentCmd"); + public RequestFocusInEditorComponentCmd(@NotNull final EditorsSplitters splitters, IdeFocusManager focusManager, final Runnable finishCallBack, boolean forced){ super(finishCallBack); + boolean shouldLogFocuses = Registry.is("ide.log.focuses"); + if (shouldLogFocuses) { + LOG.info(new Exception()); + } myComponent = null; final EditorWindow window = splitters.getCurrentWindow(); if (window != null) { @@ -94,7 +102,8 @@ public final class RequestFocusInEditorComponentCmd extends FinalizableCommand{ } if(myComponent != null){ - myFocusManager.requestFocus(myComponent, myForced).notifyWhenDone(myDoneCallback).doWhenDone(new Runnable() { + final boolean forced = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == null; + myFocusManager.requestFocus(myComponent, myForced || forced).notifyWhenDone(myDoneCallback).doWhenDone(new Runnable() { public void run() { // if owner is active window or it has active child window which isn't floating decorator then // don't bring owner window to font. If we will make toFront every time then it's possible diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectPanel.java index 9140fc200a77..82dba7430827 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/RecentProjectPanel.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. @@ -186,14 +186,11 @@ public class RecentProjectPanel extends JPanel { private String getTitle2Text(ReopenProjectAction action, JComponent pathLabel) { String fullText = action.getProjectPath(); - int labelWidth = pathLabel.getWidth(); if (fullText == null || fullText.length() == 0) return " "; - String home = SystemProperties.getUserHome(); - if (FileUtil.startsWith(fullText, home)) { - fullText = "~" + fullText.substring(home.length()); - } + fullText = FileUtil.getLocationRelativeToUserHome(fullText, false); + int labelWidth = pathLabel.getWidth(); if (pathLabel.getFontMetrics(pathLabel.getFont()).stringWidth(fullText) > labelWidth) { return myPathShortener.getShortPath(action); } diff --git a/platform/platform-impl/src/com/intellij/remote/RemoteConnectionCredentialsWrapper.java b/platform/platform-impl/src/com/intellij/remote/RemoteConnectionCredentialsWrapper.java index e31b067e5d79..8d85ae106c44 100644 --- a/platform/platform-impl/src/com/intellij/remote/RemoteConnectionCredentialsWrapper.java +++ b/platform/platform-impl/src/com/intellij/remote/RemoteConnectionCredentialsWrapper.java @@ -116,11 +116,24 @@ public class RemoteConnectionCredentialsWrapper { return new IllegalStateException("Unknown connection type"); //TODO } - public void copyTo(RemoteConnectionCredentialsWrapper copy) { + public void copyTo(final RemoteConnectionCredentialsWrapper copy) { copy.myCredentialsTypeHolder = new UserDataHolderBase(); - copy.setPlainSshCredentials(getPlainSshCredentials()); - copy.setVagrantConnectionType(getVagrantCredentials()); - copy.setWebDeploymentCredentials(getWebDeploymentCredentials()); + switchType(new RemoteSdkConnectionAcceptor() { + @Override + public void ssh(@NotNull RemoteCredentialsHolder cred) { + copy.setPlainSshCredentials(getPlainSshCredentials()); + } + + @Override + public void vagrant(@NotNull VagrantBasedCredentialsHolder cred) { + copy.setVagrantConnectionType(getVagrantCredentials()); + } + + @Override + public void deployment(@NotNull WebDeploymentCredentialsHolder cred) { + copy.setWebDeploymentCredentials(getWebDeploymentCredentials()); + } + }); } public String getId() { diff --git a/platform/platform-impl/src/com/intellij/remote/VagrantSupport.java b/platform/platform-impl/src/com/intellij/remote/VagrantSupport.java index a5ebae6fe567..9e2b60a54416 100644 --- a/platform/platform-impl/src/com/intellij/remote/VagrantSupport.java +++ b/platform/platform-impl/src/com/intellij/remote/VagrantSupport.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,6 +44,10 @@ public abstract class VagrantSupport { @NotNull public abstract RemoteCredentials getVagrantSettings(@NotNull Project project, String vagrantFolder); + public abstract void getVagrantSettingsAsync(@Nullable Project project, + @NotNull String vagrantFolder, + @NotNull Consumer<RemoteCredentials> onSuccess); + @NotNull public abstract RemoteCredentials getCredentials(@NotNull String folder) throws IOException; diff --git a/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java b/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java index 4e65063e7939..d8b204cfd76c 100644 --- a/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java +++ b/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java @@ -223,6 +223,7 @@ public abstract class AbstractExpandableItemsHandler<KeyType, ComponentType exte if (selected == null || !myComponent.isEnabled() || !myComponent.isShowing() + || !myComponent.getVisibleRect().intersects(getVisibleRect(selected)) || !myComponent.isFocusOwner() && !processIfUnfocused || isPopup()) { hideHint(); diff --git a/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java b/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java index 50a7ebbca92b..2a7d44d017bd 100644 --- a/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java +++ b/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java @@ -29,6 +29,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.PlatformUtils; +import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -38,7 +39,6 @@ import javax.swing.*; import java.awt.*; import java.io.File; import java.io.InputStream; -import java.lang.reflect.Field; import java.net.URL; import java.util.List; import java.util.Locale; @@ -111,9 +111,7 @@ public class AppUIUtil { final Toolkit toolkit = Toolkit.getDefaultToolkit(); final Class<? extends Toolkit> aClass = toolkit.getClass(); if ("sun.awt.X11.XToolkit".equals(aClass.getName())) { - final Field awtAppClassName = aClass.getDeclaredField("awtAppClassName"); - awtAppClassName.setAccessible(true); - awtAppClassName.set(toolkit, getFrameClass()); + ReflectionUtil.setField(aClass, toolkit, null, "awtAppClassName", getFrameClass()); } } catch (Exception ignore) { } diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index c6c209ed4b88..513d5cedb2ab 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -200,8 +200,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { if (cmp == myCloseRec) return true; if (UIUtil.isDescendingFrom(cmp, myComp)) return true; if (myComp == null || !myComp.isShowing()) return false; - Rectangle rectangleOnScreen = new Rectangle(myComp.getLocationOnScreen(), myComp.getSize()); - return rectangleOnScreen.contains(target.getScreenPoint()); + return myComp.contains(target.getScreenPoint().x, target.getScreenPoint().y); } public boolean isMovingForward(RelativePoint target) { @@ -614,7 +613,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { myComp.setBorder(new EmptyBorder(borderSize, borderSize, borderSize, borderSize)); myLayeredPane.add(myComp); - myLayeredPane.setLayer(myComp, getLayer()); + myLayeredPane.setLayer(myComp, getLayer(), 0); // the second balloon must be over the first one myPosition.updateBounds(this); if (myBlockClicks) { myComp.addMouseListener(new MouseAdapter() { diff --git a/platform/platform-impl/src/com/intellij/ui/CheckboxTree.java b/platform/platform-impl/src/com/intellij/ui/CheckboxTree.java index c91be65978c1..1443a9303e39 100644 --- a/platform/platform-impl/src/com/intellij/ui/CheckboxTree.java +++ b/platform/platform-impl/src/com/intellij/ui/CheckboxTree.java @@ -15,8 +15,6 @@ */ package com.intellij.ui; -import java.awt.event.KeyEvent; - /** * User: lex * Date: Sep 18, 2003 @@ -56,11 +54,4 @@ public class CheckboxTree extends CheckboxTreeBase { protected void installSpeedSearch() { new TreeSpeedSearch(this); } - - - protected boolean isToggleEvent(KeyEvent e) { - return super.isToggleEvent(e) && !SpeedSearchBase.hasActiveSpeedSearch(this); - } - - } diff --git a/platform/platform-impl/src/com/intellij/ui/CheckboxTreeTable.java b/platform/platform-impl/src/com/intellij/ui/CheckboxTreeTable.java new file mode 100644 index 000000000000..369dd0cc1803 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/CheckboxTreeTable.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 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.ui; + +import com.intellij.ui.dualView.TreeTableView; +import com.intellij.ui.treeStructure.treetable.ListTreeTableModelOnColumns; +import com.intellij.ui.treeStructure.treetable.TreeTableTree; +import com.intellij.util.EventDispatcher; +import com.intellij.util.ui.ColumnInfo; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public class CheckboxTreeTable extends TreeTableView { + private final EventDispatcher<CheckboxTreeListener> myEventDispatcher; + + public CheckboxTreeTable(CheckedTreeNode root, CheckboxTree.CheckboxTreeCellRenderer renderer, final ColumnInfo[] columns) { + super(new ListTreeTableModelOnColumns(root, columns)); + final TreeTableTree tree = getTree(); + myEventDispatcher = EventDispatcher.create(CheckboxTreeListener.class); + CheckboxTreeHelper helper = new CheckboxTreeHelper(CheckboxTreeHelper.DEFAULT_POLICY, myEventDispatcher); + helper.initTree(tree, this, renderer); + tree.setSelectionRow(0); + } + + public void addCheckboxTreeListener(@NotNull CheckboxTreeListener listener) { + myEventDispatcher.addListener(listener); + } + + public <T> T[] getCheckedNodes(final Class<T> nodeType) { + return CheckboxTreeHelper.getCheckedNodes(nodeType, null, getTree().getModel()); + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/JBTabsPaneImpl.java b/platform/platform-impl/src/com/intellij/ui/JBTabsPaneImpl.java index 39c8aaf41ace..d5fc8dcca1ba 100644 --- a/platform/platform-impl/src/com/intellij/ui/JBTabsPaneImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/JBTabsPaneImpl.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. @@ -20,7 +20,8 @@ import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.tabs.*; -import com.intellij.ui.tabs.impl.JBTabsImpl; +import com.intellij.ui.tabs.impl.JBEditorTabs; +import com.intellij.ui.tabs.impl.TabLabel; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,7 +39,45 @@ public class JBTabsPaneImpl implements TabbedPane, SwingConstants { private final CopyOnWriteArraySet<ChangeListener> myListeners = new CopyOnWriteArraySet<ChangeListener>(); public JBTabsPaneImpl(@Nullable Project project, int tabPlacement, @NotNull Disposable parent) { - myTabs = new JBTabsImpl(project, ActionManager.getInstance(), project == null ? null : IdeFocusManager.getInstance(project), parent); + myTabs = new JBEditorTabs(project, ActionManager.getInstance(), project == null ? null : IdeFocusManager.getInstance(project), parent) { + @Override + public boolean isAlphabeticalMode() { + return false; + } + + @Override + protected void doPaintBackground(Graphics2D g2d, Rectangle clip) { + super.doPaintBackground(g2d, clip); + if (getTabsPosition() == JBTabsPosition.top && isSingleRow()) { + int maxOffset = 0; + int maxLength = 0; + + for (int i = getVisibleInfos().size() - 1; i >= 0; i--) { + TabInfo visibleInfo = getVisibleInfos().get(i); + TabLabel tabLabel = myInfo2Label.get(visibleInfo); + Rectangle r = tabLabel.getBounds(); + if (r.width == 0 || r.height == 0) continue; + maxOffset = r.x + r.width; + maxLength = r.height; + break; + } + + maxOffset++; + g2d.setPaint(UIUtil.getPanelBackground()); + g2d.fillRect(clip.x + maxOffset, clip.y, clip.width - maxOffset, clip.y + maxLength - TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT); + g2d.setPaint(new JBColor(Gray._181, UIUtil.getPanelBackground())); + g2d.drawLine(clip.x + maxOffset, clip.y + maxLength - TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT, clip.x + clip.width, clip.y + maxLength - TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT); + g2d.setPaint(UIUtil.getPanelBackground()); + g2d.drawLine(clip.x, clip.y + maxLength, clip.width, clip.y + maxLength); + } + } + + @Override + protected void paintSelectionAndBorder(Graphics2D g2d) { + super.paintSelectionAndBorder(g2d); + } + }; + myTabs.addListener(new TabsListener.Adapter() { @Override public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) { diff --git a/platform/platform-impl/src/com/intellij/ui/SplitterWithSecondHideable.java b/platform/platform-impl/src/com/intellij/ui/SplitterWithSecondHideable.java index 99c41b52cbea..e219b778f44d 100644 --- a/platform/platform-impl/src/com/intellij/ui/SplitterWithSecondHideable.java +++ b/platform/platform-impl/src/com/intellij/ui/SplitterWithSecondHideable.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,6 +16,7 @@ package com.intellij.ui; import com.intellij.icons.AllIcons; +import com.intellij.openapi.ui.Divider; import com.intellij.openapi.ui.PseudoSplitter; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.vcs.changes.RefreshablePanel; @@ -41,7 +42,7 @@ public abstract class SplitterWithSecondHideable { private final boolean myVertical; private final OnOffListener<Integer> myListener; private final JPanel myFictivePanel; - private Splitter.Divider mySuperDivider; + private Splitter.DividerImpl mySuperDivider; private float myPreviousProportion; public SplitterWithSecondHideable(final boolean vertical, @@ -185,7 +186,7 @@ public abstract class SplitterWithSecondHideable { return vertical ? myTitledSeparator.getHeight() : myTitledSeparator.getWidth(); } - class MyDivider extends Divider { + class MyDivider extends DividerImpl { @Override public void processMouseMotionEvent(MouseEvent e) { super.processMouseMotionEvent(e); diff --git a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java index 78802ccdf6bb..e50403abef45 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java @@ -162,6 +162,25 @@ public class AbstractPopup implements JBPopup { private UiActivity myActivityKey; private Disposable myProjectDisposable; + private volatile State myState = State.NEW; + + private enum State {NEW, INIT, SHOWING, SHOWN, CANCEL, DISPOSE} + + private void debugState(String message, State... states) { + if (LOG.isDebugEnabled()) { + LOG.debug(hashCode() + " - " + message); + if (!ApplicationManager.getApplication().isDispatchThread()) { + LOG.debug("unexpected thread"); + } + for (State state : states) { + if (state == myState) { + return; + } + } + LOG.debug(new IllegalStateException("myState=" + myState)); + } + } + AbstractPopup() { } AbstractPopup init(Project project, @@ -302,6 +321,8 @@ public class AbstractPopup implements JBPopup { } myKeyEventHandler = keyEventHandler; + debugState("popup initialized", State.NEW); + myState = State.INIT; return this; } @@ -582,10 +603,18 @@ public class AbstractPopup implements JBPopup { @Override public void cancel(InputEvent e) { + if (myState == State.CANCEL || myState == State.DISPOSE) { + return; + } + debugState("cancel popup", State.SHOWN); + myState = State.CANCEL; + if (isDisposed()) return; if (myPopup != null) { if (!canClose()) { + debugState("cannot cancel popup", State.CANCEL); + myState = State.SHOWN; return; } storeDimensionSize(myContent.getSize()); @@ -610,8 +639,13 @@ public class AbstractPopup implements JBPopup { } if (myInStack) { - myFocusTrackback.setForcedRestore(!myOk && myFocusable); - myFocusTrackback.restoreFocus(); + if (myFocusTrackback != null) { + myFocusTrackback.setForcedRestore(!myOk && myFocusable); + myFocusTrackback.restoreFocus(); + } + else if (LOG.isDebugEnabled()) { + LOG.debug("cancel before show @ " + Thread.currentThread()); + } } @@ -664,6 +698,9 @@ public class AbstractPopup implements JBPopup { assert ApplicationManager.getApplication().isDispatchThread(); + debugState("show popup", State.INIT); + myState = State.SHOWING; + installWindowHook(this); installProjectDisposer(); addActivity(); @@ -673,6 +710,8 @@ public class AbstractPopup implements JBPopup { final boolean shouldShow = beforeShow(); if (!shouldShow) { removeActivity(); + debugState("rejected to show popup", State.SHOWING); + myState = State.INIT; return; } @@ -758,10 +797,11 @@ public class AbstractPopup implements JBPopup { PopupComponent.Factory factory = getFactory(myForcedHeavyweight || myResizable, forcedDialog); myNativePopup = factory.isNativePopup(); Component popupOwner = myOwner; - if (popupOwner instanceof RootPaneContainer) { + if (popupOwner instanceof RootPaneContainer && !(popupOwner instanceof IdeFrame && !Registry.is("popup.fix.ide.frame.owner"))) { // JDK uses cached heavyweight popup for a window ancestor RootPaneContainer root = (RootPaneContainer)popupOwner; popupOwner = root.getRootPane(); + LOG.debug("popup owner fixed for JDK cache"); } if (LOG.isDebugEnabled()) { LOG.debug("expected preferred size: " + myContent.getPreferredSize()); @@ -957,6 +997,8 @@ public class AbstractPopup implements JBPopup { } }); } + debugState("popup shown", State.SHOWING); + myState = State.SHOWN; } public void focusPreferredComponent() { @@ -1232,6 +1274,16 @@ public class AbstractPopup implements JBPopup { @Override public void dispose() { + if (myState == State.SHOWN) { + LOG.debug("shown popup must be cancelled"); + cancel(); + } + if (myState == State.DISPOSE) { + return; + } + debugState("dispose popup", State.INIT, State.CANCEL); + myState = State.DISPOSE; + if (myDisposed) { return; } @@ -1480,8 +1532,8 @@ public class AbstractPopup implements JBPopup { @Override public Dimension getSize() { if (myPopup != null) { - final Window popupWindow = SwingUtilities.windowForComponent(myContent); - return popupWindow.getSize(); + final Window popupWindow = getContentWindow(myContent); + return (popupWindow == null) ? myForcedSize : popupWindow.getSize(); } else { return myForcedSize; } @@ -1491,7 +1543,8 @@ public class AbstractPopup implements JBPopup { public void moveToFitScreen() { if (myPopup == null) return; - final Window popupWindow = SwingUtilities.windowForComponent(myContent); + final Window popupWindow = getContentWindow(myContent); + if (popupWindow == null) return; Rectangle bounds = popupWindow.getBounds(); ScreenUtil.moveRectangleToFitTheScreen(bounds); @@ -1501,7 +1554,8 @@ public class AbstractPopup implements JBPopup { public static Window setSize(JComponent content, final Dimension size) { - final Window popupWindow = SwingUtilities.windowForComponent(content); + final Window popupWindow = getContentWindow(content); + if (popupWindow == null) return null; Insets insets = content.getInsets(); if (insets != null) { size.width += insets.left + insets.right; diff --git a/platform/platform-impl/src/com/intellij/ui/popup/OurHeavyWeightPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/OurHeavyWeightPopup.java new file mode 100644 index 000000000000..2a79368f65bb --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/popup/OurHeavyWeightPopup.java @@ -0,0 +1,35 @@ +/* + * 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.ui.popup; + +import com.intellij.openapi.util.registry.Registry; + +import javax.swing.Popup; +import java.awt.Component; +import java.awt.GraphicsEnvironment; + +/** + * @author Sergey Malenkov + */ +public final class OurHeavyWeightPopup extends Popup { + public OurHeavyWeightPopup(Component owner, Component content, int x, int y) { + super(owner, content, x, y); + } + + public static boolean isEnabled() { + return !GraphicsEnvironment.isHeadless() && Registry.is("our.heavy.weight.popup"); + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/popup/PopupComponent.java b/platform/platform-impl/src/com/intellij/ui/popup/PopupComponent.java index 301758511612..1670491b91bf 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/PopupComponent.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/PopupComponent.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. @@ -58,6 +58,9 @@ public interface PopupComponent { class AwtHeavyweight implements Factory { public PopupComponent getPopup(Component owner, Component content, int x, int y, JBPopup jbPopup) { + if (OurHeavyWeightPopup.isEnabled()) { + return new AwtPopupWrapper(new OurHeavyWeightPopup(owner, content, x, y), jbPopup); + } final PopupFactory factory = PopupFactory.getSharedInstance(); final int oldType = PopupUtil.getPopupType(factory); @@ -172,7 +175,7 @@ public interface PopupComponent { myJBPopup = jbPopup; if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) { - final Component c = (Component)ReflectionUtil.getField(Popup.class, myPopup, Component.class, "component"); + final Component c = ReflectionUtil.getField(Popup.class, myPopup, Component.class, "component"); c.setBackground(UIUtil.getPanelBackground()); } } @@ -220,7 +223,7 @@ public interface PopupComponent { } public Window getWindow() { - final Component c = (Component)ReflectionUtil.getField(Popup.class, myPopup, Component.class, "component"); + final Component c = ReflectionUtil.getField(Popup.class, myPopup, Component.class, "component"); return c instanceof JWindow ? (JWindow)c : null; } diff --git a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java index ac4c1b12373c..35719ca9366b 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java +++ b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java @@ -1,3 +1,18 @@ +/* + * 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.util.ui; import com.intellij.ide.BrowserUtil; @@ -48,7 +63,7 @@ public class SwingHelper { * stacked vertically each on another in a given order. * * @param childAlignmentX Component.LEFT_ALIGNMENT, Component.CENTER_ALIGNMENT or Component.RIGHT_ALIGNMENT - * @param children children components + * @param children children components * @return created panel */ @NotNull @@ -76,7 +91,7 @@ public class SwingHelper { * stacked each on another in a given order. * * @param childAlignmentY Component.TOP_ALIGNMENT, Component.CENTER_ALIGNMENT or Component.BOTTOM_ALIGNMENT - * @param children children components + * @param children children components * @return created panel */ @NotNull @@ -98,10 +113,11 @@ public class SwingHelper { for (Component child : children) { panel.add(child, childAlignment); if (child instanceof JComponent) { - JComponent jChild = (JComponent) child; + JComponent jChild = (JComponent)child; if (verticalOrientation) { jChild.setAlignmentX(childAlignment); - } else { + } + else { jChild.setAlignmentY(childAlignment); } } @@ -259,20 +275,7 @@ public class SwingHelper { textFieldWithHistory.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - List<String> newHistory = historyProvider.produce(); - Set<String> newHistorySet = ContainerUtil.newHashSet(newHistory); - List<String> oldHistory = textFieldWithHistory.getHistory(); - List<String> mergedHistory = ContainerUtil.newArrayList(); - for (String item : oldHistory) { - if (!newHistorySet.contains(item)) { - mergedHistory.add(item); - } - } - mergedHistory.addAll(newHistory); - textFieldWithHistory.setHistory(mergedHistory); - - setLongestAsPrototype(textFieldWithHistory, mergedHistory); - + setHistory(textFieldWithHistory, historyProvider.produce(), true); // one-time initialization textFieldWithHistory.removePopupMenuListener(this); } @@ -287,11 +290,35 @@ public class SwingHelper { }); } + public static void setHistory(@NotNull TextFieldWithHistory textFieldWithHistory, + @NotNull List<String> history, + boolean mergeWithPrevHistory) { + Set<String> newHistorySet = ContainerUtil.newHashSet(history); + List<String> prevHistory = textFieldWithHistory.getHistory(); + List<String> mergedHistory = ContainerUtil.newArrayList(); + if (mergeWithPrevHistory) { + for (String item : prevHistory) { + if (!newHistorySet.contains(item)) { + mergedHistory.add(item); + } + } + } + else { + String currentText = textFieldWithHistory.getText(); + if (StringUtil.isNotEmpty(currentText) && !newHistorySet.contains(currentText)) { + mergedHistory.add(currentText); + } + } + mergedHistory.addAll(history); + textFieldWithHistory.setHistory(mergedHistory); + setLongestAsPrototype(textFieldWithHistory, mergedHistory); + } + private static void setLongestAsPrototype(@NotNull JComboBox comboBox, @NotNull List<String> variants) { Object prototypeDisplayValue = comboBox.getPrototypeDisplayValue(); String prototypeDisplayValueStr = null; if (prototypeDisplayValue instanceof String) { - prototypeDisplayValueStr = (String) prototypeDisplayValue; + prototypeDisplayValueStr = (String)prototypeDisplayValue; } else if (prototypeDisplayValue != null) { return; @@ -413,15 +440,38 @@ public class SwingHelper { return textPane; } - public static void setHtml(@NotNull JEditorPane editorPane, @NotNull String bodyInnerHtml) { + public static void setHtml(@NotNull JEditorPane editorPane, + @NotNull String bodyInnerHtml, + @Nullable Color foregroundColor) { String html = String.format( "<html><head>%s</head><body>%s</body></html>", - UIUtil.getCssFontDeclaration(editorPane.getFont(), null, null, null), + UIUtil.getCssFontDeclaration(editorPane.getFont(), foregroundColor, null, null), bodyInnerHtml ); editorPane.setText(html); } + @NotNull + public static TextFieldWithHistoryWithBrowseButton createTextFieldWithHistoryWithBrowseButton(@Nullable Project project, + @NotNull String browseDialogTitle, + @NotNull FileChooserDescriptor fileChooserDescriptor, + @Nullable NotNullProducer<List<String>> historyProvider) { + TextFieldWithHistoryWithBrowseButton textFieldWithHistoryWithBrowseButton = new TextFieldWithHistoryWithBrowseButton(); + TextFieldWithHistory textFieldWithHistory = textFieldWithHistoryWithBrowseButton.getChildComponent(); + textFieldWithHistory.setHistorySize(-1); + textFieldWithHistory.setMinimumAndPreferredWidth(0); + if (historyProvider != null) { + addHistoryOnExpansion(textFieldWithHistory, historyProvider); + } + installFileCompletionAndBrowseDialog( + project, + textFieldWithHistoryWithBrowseButton, + browseDialogTitle, + fileChooserDescriptor + ); + return textFieldWithHistoryWithBrowseButton; + } + private static class CopyLinkAction extends AnAction { private final String myUrl; |