diff options
Diffstat (limited to 'platform/platform-impl/src/com')
129 files changed, 3519 insertions, 2410 deletions
diff --git a/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java b/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java index b33611da8866..876474b9124b 100644 --- a/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java +++ b/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java @@ -146,19 +146,11 @@ public class JobLauncherImpl extends JobLauncher { // This implementation is not really async @NotNull @Override - public <T> AsyncFutureResult<Boolean> invokeConcurrentlyUnderProgressAsync(@NotNull List<? extends T> things, + public <T> AsyncFuture<Boolean> invokeConcurrentlyUnderProgressAsync(@NotNull List<? extends T> things, ProgressIndicator progress, boolean failFastOnAcquireReadAction, @NotNull Processor<T> thingProcessor) { - final AsyncFutureResult<Boolean> asyncFutureResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - try { - final boolean result = invokeConcurrentlyUnderProgress(things, progress, failFastOnAcquireReadAction, thingProcessor); - asyncFutureResult.set(result); - } - catch (Throwable t) { - asyncFutureResult.setException(t); - } - return asyncFutureResult; + return AsyncUtil.wrapBoolean(invokeConcurrentlyUnderProgress(things, progress, failFastOnAcquireReadAction, thingProcessor)); } @NotNull diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/RoamingTypeExtensionPointBean.java b/platform/platform-impl/src/com/intellij/designer/DesignerEditorPanelFacade.java index ff5d16361228..fa9ecf8610ac 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/RoamingTypeExtensionPointBean.java +++ b/platform/platform-impl/src/com/intellij/designer/DesignerEditorPanelFacade.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. @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.components.impl.stores; +package com.intellij.designer; -import com.intellij.util.xmlb.annotations.Attribute; +import com.intellij.openapi.ui.ThreeComponentsSplitter; /** - * @deprecated use {@link com.intellij.openapi.components.RoamingType#DISABLED} + * @author Alexander Lobas */ -@Deprecated -public class RoamingTypeExtensionPointBean { - @Attribute("component") - public String componentName; - @Attribute("type") - public String roamingType; -} +public interface DesignerEditorPanelFacade { + Object getClientProperty(Object key); + + void putClientProperty(Object key, Object value); + + ThreeComponentsSplitter getContentSplitter(); +}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/LightFillLayout.java b/platform/platform-impl/src/com/intellij/designer/LightFillLayout.java new file mode 100644 index 000000000000..af0ca87f1e05 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/LightFillLayout.java @@ -0,0 +1,122 @@ +/* + * 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.designer; + +import com.intellij.util.Function; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Alexander Lobas + */ +public class LightFillLayout implements LayoutManager2 { + @Override + public void addLayoutComponent(Component comp, Object constraints) { + } + + @Override + public float getLayoutAlignmentX(Container target) { + return 0.5f; + } + + @Override + public float getLayoutAlignmentY(Container target) { + return 0.5f; + } + + @Override + public void invalidateLayout(Container target) { + } + + @Override + public void addLayoutComponent(String name, Component comp) { + } + + @Override + public void removeLayoutComponent(Component comp) { + } + + @Override + public Dimension maximumLayoutSize(Container target) { + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + @Override + public Dimension preferredLayoutSize(Container parent) { + return layoutSize(parent, new Function<Component, Dimension>() { + @Override + public Dimension fun(Component component) { + return component.getPreferredSize(); + } + }); + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + return layoutSize(parent, new Function<Component, Dimension>() { + @Override + public Dimension fun(Component component) { + return component.getMinimumSize(); + } + }); + } + + private static Dimension layoutSize(Container parent, Function<Component, Dimension> getSize) { + Component toolbar = parent.getComponent(0); + Dimension toolbarSize = toolbar.isVisible() ? getSize.fun(toolbar) : new Dimension(); + Dimension contentSize = getSize.fun(parent.getComponent(1)); + int extraWidth = 0; + JComponent jParent = (JComponent)parent; + if (jParent.getClientProperty(LightToolWindow.LEFT_MIN_KEY) != null) { + extraWidth += LightToolWindow.MINIMIZE_WIDTH; + } + if (jParent.getClientProperty(LightToolWindow.RIGHT_MIN_KEY) != null) { + extraWidth += LightToolWindow.MINIMIZE_WIDTH; + } + return new Dimension(Math.max(toolbarSize.width, contentSize.width + extraWidth), toolbarSize.height + contentSize.height); + } + + @Override + public void layoutContainer(Container parent) { + int leftWidth = 0; + int rightWidth = 0; + JComponent jParent = (JComponent)parent; + JComponent left = (JComponent)jParent.getClientProperty(LightToolWindow.LEFT_MIN_KEY); + if (left != null) { + leftWidth = LightToolWindow.MINIMIZE_WIDTH; + } + JComponent right = (JComponent)jParent.getClientProperty(LightToolWindow.RIGHT_MIN_KEY); + if (right != null) { + rightWidth = LightToolWindow.MINIMIZE_WIDTH; + } + int extraWidth = leftWidth + rightWidth; + + int width = parent.getWidth() - extraWidth; + int height = parent.getHeight(); + Component toolbar = parent.getComponent(0); + Dimension toolbarSize = toolbar.isVisible() ? toolbar.getPreferredSize() : new Dimension(); + toolbar.setBounds(leftWidth, 0, width, toolbarSize.height); + parent.getComponent(1).setBounds(leftWidth, toolbarSize.height, width, height - toolbarSize.height); + + if (left != null) { + left.setBounds(0, 0, leftWidth, height); + } + if (right != null) { + right.setBounds(width + leftWidth, 0, rightWidth, height); + } + } +}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/LightToolWindow.java b/platform/platform-impl/src/com/intellij/designer/LightToolWindow.java new file mode 100644 index 000000000000..ce3cb33deb90 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/LightToolWindow.java @@ -0,0 +1,562 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.designer; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.impl.ActionManagerImpl; +import com.intellij.openapi.actionSystem.impl.MenuItemPresentationFactory; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ThreeComponentsSplitter; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.wm.*; +import com.intellij.openapi.wm.ex.ToolWindowEx; +import com.intellij.openapi.wm.impl.AnchoredButton; +import com.intellij.openapi.wm.impl.InternalDecorator; +import com.intellij.openapi.wm.impl.StripeButtonUI; +import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; +import com.intellij.ui.*; +import com.intellij.ui.components.panels.Wrapper; +import com.intellij.ui.tabs.TabsUtil; +import com.intellij.util.ui.EmptyIcon; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.BufferedImage; + +/** + * @author Alexander Lobas + */ +public class LightToolWindow extends JPanel { + public static final String LEFT_MIN_KEY = "left"; + public static final String RIGHT_MIN_KEY = "right"; + public static final int MINIMIZE_WIDTH = 25; + private static final String IGNORE_WIDTH_KEY = "ignore_width"; + + private final LightToolWindowContent myContent; + private final JComponent myFocusedComponent; + private final ThreeComponentsSplitter myContentSplitter; + private ToolWindowAnchor myAnchor; + private final Project myProject; + private final LightToolWindowManager myManager; + private final PropertiesComponent myPropertiesComponent; + private boolean myShowContent; + private final String myShowStateKey; + private int myCurrentWidth; + private final String myWidthKey; + private final JPanel myMinimizeComponent; + private final AnchoredButton myMinimizeButton; + + private final TogglePinnedModeAction myToggleAutoHideModeAction = new TogglePinnedModeAction(); + private final ToggleDockModeAction myToggleDockModeAction = new ToggleDockModeAction(); + private final ToggleFloatingModeAction myToggleFloatingModeAction = new ToggleFloatingModeAction(); + private final ToggleSideModeAction myToggleSideModeAction = new ToggleSideModeAction(); + + private final ComponentListener myWidthListener = new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + int width = isLeft() ? myContentSplitter.getFirstSize() : myContentSplitter.getLastSize(); + if (width > 0 && width != myCurrentWidth && myContentSplitter.getInnerComponent().getClientProperty(IGNORE_WIDTH_KEY) == null) { + myCurrentWidth = width; + myPropertiesComponent.setValue(myWidthKey, Integer.toString(width)); + } + } + }; + + public LightToolWindow(@NotNull LightToolWindowContent content, + @NotNull String title, + @NotNull Icon icon, + @NotNull JComponent component, + @NotNull JComponent focusedComponent, + @NotNull ThreeComponentsSplitter contentSplitter, + @Nullable ToolWindowAnchor anchor, + @NotNull LightToolWindowManager manager, + @NotNull Project project, + @NotNull PropertiesComponent propertiesComponent, + @NotNull String key, + int defaultWidth, + @Nullable AnAction[] actions) { + super(new BorderLayout()); + myContent = content; + myFocusedComponent = focusedComponent; + myContentSplitter = contentSplitter; + myAnchor = anchor; + myProject = project; + myManager = manager; + myPropertiesComponent = propertiesComponent; + + myShowStateKey = LightToolWindowManager.EDITOR_MODE + key + ".SHOW"; + myWidthKey = LightToolWindowManager.EDITOR_MODE + key + ".WIDTH"; + + HeaderPanel header = new HeaderPanel(); + header.setLayout(new BorderLayout()); + add(header, BorderLayout.NORTH); + + JLabel titleLabel = new JLabel(title); + titleLabel.setBorder(IdeBorderFactory.createEmptyBorder(2, 5, 2, 10)); + titleLabel.setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL)); + header.add(titleLabel, BorderLayout.CENTER); + + JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); + actionPanel.setBorder(IdeBorderFactory.createEmptyBorder(3, 0, 2, 0)); + actionPanel.setOpaque(false); + header.add(actionPanel, BorderLayout.EAST); + + if (actions != null) { + for (AnAction action : actions) { + addAction(actionPanel, action); + } + + actionPanel.add(new JLabel(AllIcons.General.Divider)); + } + + addAction(actionPanel, new GearAction()); + addAction(actionPanel, new HideAction()); + + JPanel contentWrapper = new JPanel(new BorderLayout()); + contentWrapper.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); + contentWrapper.add(component, BorderLayout.CENTER); + + add(contentWrapper, BorderLayout.CENTER); + + addMouseListener(new MouseAdapter() { + public void mouseReleased(final MouseEvent e) { + IdeFocusManager.getInstance(myProject).requestFocus(myFocusedComponent, true); + } + }); + + addMouseListener(new PopupHandler() { + public void invokePopup(Component component, int x, int y) { + showGearPopup(component, x, y); + } + }); + + myMinimizeButton = new AnchoredButton(title, icon) { + @Override + public void updateUI() { + setUI(StripeButtonUI.createUI(this)); + setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL)); + } + + @Override + public int getMnemonic2() { + return 0; + } + + @Override + public ToolWindowAnchor getAnchor() { + return myAnchor; + } + }; + myMinimizeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + myMinimizeButton.setSelected(false); + updateContent(true, true); + } + }); + myMinimizeButton.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); + myMinimizeButton.setFocusable(false); + + myMinimizeButton.setRolloverEnabled(true); + myMinimizeButton.setOpaque(false); + + myMinimizeComponent = new JPanel() { + @Override + public void doLayout() { + Dimension size = myMinimizeButton.getPreferredSize(); + myMinimizeButton.setBounds(0, 0, getWidth(), size.height); + } + }; + myMinimizeComponent.add(myMinimizeButton); + + configureBorder(); + configureWidth(defaultWidth); + updateContent(myPropertiesComponent.getBoolean(myShowStateKey, true), false); + } + + private void configureBorder() { + int borderStyle = isLeft() ? SideBorder.RIGHT : SideBorder.LEFT; + setBorder(IdeBorderFactory.createBorder(borderStyle)); + myMinimizeComponent.setBorder(IdeBorderFactory.createBorder(borderStyle)); + } + + private void configureWidth(int defaultWidth) { + myCurrentWidth = myPropertiesComponent.getOrInitInt(myWidthKey, defaultWidth); + updateWidth(); + myContentSplitter.getInnerComponent().addComponentListener(myWidthListener); + } + + private void updateWidth() { + if (isLeft()) { + myContentSplitter.setFirstSize(myCurrentWidth); + } + else { + myContentSplitter.setLastSize(myCurrentWidth); + } + } + + public void updateAnchor(ToolWindowAnchor newAnchor) { + JComponent minimizeParent = myContentSplitter.getInnerComponent(); + minimizeParent.putClientProperty(IGNORE_WIDTH_KEY, Boolean.TRUE); + + if (myShowContent) { + Object oldWindow = isLeft() ? myContentSplitter.getFirstComponent() : myContentSplitter.getLastComponent(); + if (oldWindow == this) { + setContentComponent(null); + } + } + else { + String key = getMinKey(); + if (minimizeParent.getClientProperty(key) == myMinimizeComponent) { + minimizeParent.putClientProperty(key, null); + } + minimizeParent.putClientProperty(isLeft() ? RIGHT_MIN_KEY : LEFT_MIN_KEY, myMinimizeComponent); + minimizeParent.revalidate(); + } + + myAnchor = newAnchor; + configureBorder(); + updateWidth(); + + if (myShowContent) { + setContentComponent(this); + } + + minimizeParent.putClientProperty(IGNORE_WIDTH_KEY, null); + } + + private void updateContent(boolean show, boolean flag) { + myShowContent = show; + + String key = getMinKey(); + + JComponent minimizeParent = myContentSplitter.getInnerComponent(); + + if (show) { + minimizeParent.putClientProperty(key, null); + minimizeParent.remove(myMinimizeComponent); + } + + setContentComponent(show ? this : null); + + if (!show) { + minimizeParent.putClientProperty(key, myMinimizeComponent); + minimizeParent.add(myMinimizeComponent); + } + + minimizeParent.revalidate(); + + if (flag) { + myPropertiesComponent.setValue(myShowStateKey, Boolean.toString(show)); + } + } + + private void setContentComponent(JComponent component) { + if (isLeft()) { + myContentSplitter.setFirstComponent(component); + } + else { + myContentSplitter.setLastComponent(component); + } + } + + public void dispose() { + JComponent minimizeParent = myContentSplitter.getInnerComponent(); + minimizeParent.removeComponentListener(myWidthListener); + + setContentComponent(null); + myContent.dispose(); + + if (!myShowContent) { + minimizeParent.putClientProperty(getMinKey(), null); + minimizeParent.remove(myMinimizeComponent); + minimizeParent.revalidate(); + } + } + + private String getMinKey() { + return isLeft() ? LEFT_MIN_KEY : RIGHT_MIN_KEY; + } + + public Object getContent() { + return myContent; + } + + private boolean isLeft() { + return myAnchor == ToolWindowAnchor.LEFT; + } + + private boolean isActive() { + IdeFocusManager fm = IdeFocusManager.getInstance(myProject); + Component component = fm.getFocusedDescendantFor(this); + if (component != null) { + return true; + } + Component owner = fm.getLastFocusedFor(WindowManager.getInstance().getIdeFrame(myProject)); + return owner != null && SwingUtilities.isDescendingFrom(owner, this); + } + + private void addAction(JPanel actionPanel, AnAction action) { + actionPanel.add(new ActionButton(action)); + } + + private DefaultActionGroup createGearPopupGroup() { + DefaultActionGroup group = new DefaultActionGroup(); + + group.add(myManager.createGearActions()); + group.addSeparator(); + + ToolWindowType type = myManager.getToolWindow().getType(); + if (type == ToolWindowType.DOCKED) { + group.add(myToggleAutoHideModeAction); + group.add(myToggleDockModeAction); + group.add(myToggleFloatingModeAction); + group.add(myToggleSideModeAction); + } + else if (type == ToolWindowType.FLOATING) { + group.add(myToggleAutoHideModeAction); + group.add(myToggleFloatingModeAction); + } + else if (type == ToolWindowType.SLIDING) { + group.add(myToggleDockModeAction); + group.add(myToggleFloatingModeAction); + } + + return group; + } + + private void showGearPopup(Component component, int x, int y) { + ActionPopupMenu popupMenu = + ((ActionManagerImpl)ActionManager.getInstance()) + .createActionPopupMenu(ToolWindowContentUi.POPUP_PLACE, createGearPopupGroup(), new MenuItemPresentationFactory(true)); + popupMenu.getComponent().show(component, x, y); + } + + private class GearAction extends AnAction { + public GearAction() { + Presentation presentation = getTemplatePresentation(); + presentation.setIcon(AllIcons.General.Gear); + presentation.setHoveredIcon(AllIcons.General.GearHover); + } + + @Override + public void actionPerformed(AnActionEvent e) { + int x = 0; + int y = 0; + InputEvent inputEvent = e.getInputEvent(); + if (inputEvent instanceof MouseEvent) { + x = ((MouseEvent)inputEvent).getX(); + y = ((MouseEvent)inputEvent).getY(); + } + + showGearPopup(inputEvent.getComponent(), x, y); + } + } + + private class HideAction extends AnAction { + public HideAction() { + Presentation presentation = getTemplatePresentation(); + presentation.setText(UIBundle.message("tool.window.hide.action.name")); + if (isLeft()) { + presentation.setIcon(AllIcons.General.HideLeftPart); + presentation.setHoveredIcon(AllIcons.General.HideLeftPartHover); + } + else { + presentation.setIcon(AllIcons.General.HideRightPart); + presentation.setHoveredIcon(AllIcons.General.HideRightPartHover); + } + } + + @Override + public void actionPerformed(AnActionEvent e) { + updateContent(false, true); + } + } + + private class TogglePinnedModeAction extends ToggleAction { + public TogglePinnedModeAction() { + copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_PINNED_MODE_ACTION_ID)); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return !myManager.getToolWindow().isAutoHide(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + ToolWindow window = myManager.getToolWindow(); + window.setAutoHide(!window.isAutoHide()); + myManager.setEditorMode(null); + } + } + + private class ToggleDockModeAction extends ToggleAction { + public ToggleDockModeAction() { + copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_DOCK_MODE_ACTION_ID)); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myManager.getToolWindow().getType() == ToolWindowType.DOCKED; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + ToolWindow window = myManager.getToolWindow(); + ToolWindowType type = window.getType(); + if (type == ToolWindowType.DOCKED) { + window.setType(ToolWindowType.SLIDING, null); + } + else if (type == ToolWindowType.SLIDING) { + window.setType(ToolWindowType.DOCKED, null); + } + myManager.setEditorMode(null); + } + } + + private class ToggleFloatingModeAction extends ToggleAction { + public ToggleFloatingModeAction() { + copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_FLOATING_MODE_ACTION_ID)); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myManager.getToolWindow().getType() == ToolWindowType.FLOATING; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + ToolWindow window = myManager.getToolWindow(); + ToolWindowType type = window.getType(); + if (type == ToolWindowType.FLOATING) { + window.setType(((ToolWindowEx)window).getInternalType(), null); + } + else { + window.setType(ToolWindowType.FLOATING, null); + } + myManager.setEditorMode(null); + } + } + + private class ToggleSideModeAction extends ToggleAction { + public ToggleSideModeAction() { + copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_SIDE_MODE_ACTION_ID)); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myManager.getToolWindow().isSplitMode(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myManager.getToolWindow().setSplitMode(state, null); + myManager.setEditorMode(null); + } + } + + private class ActionButton extends Wrapper implements ActionListener { + private final AnAction myAction; + + public ActionButton(AnAction action) { + myAction = action; + + Presentation presentation = action.getTemplatePresentation(); + InplaceButton button = new InplaceButton(KeymapUtil.createTooltipText(presentation.getText(), action), EmptyIcon.ICON_16, this) { + @Override + public boolean isActive() { + return LightToolWindow.this.isActive(); + } + }; + button.setHoveringEnabled(!SystemInfo.isMac); + setContent(button); + + Icon icon = presentation.getIcon(); + Icon hoveredIcon = presentation.getHoveredIcon(); + button.setIcons(icon, icon, hoveredIcon == null ? icon : hoveredIcon); + } + + @Override + public void actionPerformed(ActionEvent e) { + InputEvent inputEvent = e.getSource() instanceof InputEvent ? (InputEvent)e.getSource() : null; + myAction.actionPerformed(AnActionEvent.createFromInputEvent(myAction, inputEvent, ActionPlaces.UNKNOWN)); + } + } + + private class HeaderPanel extends JPanel { + private BufferedImage myActiveImage; + private BufferedImage myImage; + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(size.width, TabsUtil.getTabsHeight()); + } + + @Override + public Dimension getMinimumSize() { + Dimension size = super.getMinimumSize(); + return new Dimension(size.width, TabsUtil.getTabsHeight()); + } + + protected void _paintComponent(Graphics g) { // XXX: visual artifacts on linux + Rectangle r = getBounds(); + + Image image; + if (isActive()) { + if (myActiveImage == null || myActiveImage.getHeight() != r.height) { + myActiveImage = drawToBuffer(true, r.height); + } + image = myActiveImage; + } + else { + if (myImage == null || myImage.getHeight() != r.height) { + myImage = drawToBuffer(false, r.height); + } + image = myImage; + } + + Graphics2D g2d = (Graphics2D)g; + Rectangle clipBounds = g2d.getClip().getBounds(); + for (int x = clipBounds.x; x < clipBounds.x + clipBounds.width; x += 150) { + g2d.drawImage(image, x, 0, null); + } + } + + protected boolean isActive() { + return LightToolWindow.this.isActive(); + } + } + + private static BufferedImage drawToBuffer(boolean active, int height) { + final int width = 150; + + BufferedImage image = UIUtil.createImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + UIUtil.drawHeader(g, 0, width, height, active, true, false, false); + g.dispose(); + + return image; + } +}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/LightToolWindowContent.java b/platform/platform-impl/src/com/intellij/designer/LightToolWindowContent.java new file mode 100644 index 000000000000..c3e00f7236f5 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/LightToolWindowContent.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.designer; + +/** + * @author Alexander Lobas + */ +public interface LightToolWindowContent { + void dispose(); +}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java b/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java new file mode 100644 index 000000000000..0bfb083dec94 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.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.designer; + +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.FileEditorManagerEvent; +import com.intellij.openapi.fileEditor.FileEditorManagerListener; +import com.intellij.openapi.project.DumbAwareRunnable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowAnchor; +import com.intellij.openapi.wm.ex.ToolWindowEx; +import com.intellij.util.ParameterizedRunnable; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.util.ui.update.MergingUpdateQueue; +import com.intellij.util.ui.update.Update; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * @author Alexander Lobas + */ +public abstract class LightToolWindowManager implements ProjectComponent { + public static final String EDITOR_MODE = "UI_DESIGNER_EDITOR_MODE."; + + private final MergingUpdateQueue myWindowQueue = new MergingUpdateQueue(getComponentName(), 200, true, null); + protected final Project myProject; + protected final FileEditorManager myFileEditorManager; + protected volatile ToolWindow myToolWindow; + private volatile boolean myToolWindowDisposed; + + private final PropertiesComponent myPropertiesComponent; + public final String myEditorModeKey; + private ToggleEditorModeAction myLeftEditorModeAction; + private ToggleEditorModeAction myRightEditorModeAction; + + private MessageBusConnection myConnection; + private final FileEditorManagerListener myListener = new FileEditorManagerListener() { + @Override + public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { + bindToDesigner(getActiveDesigner()); + } + + @Override + public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + bindToDesigner(getActiveDesigner()); + } + }); + } + + @Override + public void selectionChanged(@NotNull FileEditorManagerEvent event) { + bindToDesigner(getDesigner(event.getNewEditor())); + } + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // ToolWindow + // + ////////////////////////////////////////////////////////////////////////////////////////// + + protected LightToolWindowManager(Project project, FileEditorManager fileEditorManager) { + myProject = project; + myFileEditorManager = fileEditorManager; + myPropertiesComponent = PropertiesComponent.getInstance(myProject); + myEditorModeKey = EDITOR_MODE + getComponentName() + ".STATE"; + } + + @Override + public void projectOpened() { + initToolWindow(); + + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() { + public void run() { + if (getEditorMode() == null) { + initListeners(); + bindToDesigner(getActiveDesigner()); + } + } + }); + } + + @Override + public void projectClosed() { + if (!myToolWindowDisposed) { + disposeComponent(); + myToolWindowDisposed = true; + myToolWindow = null; + } + } + + private void initListeners() { + myConnection = myProject.getMessageBus().connect(myProject); + myConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, myListener); + } + + private void removeListeners() { + myConnection.disconnect(); + myConnection = null; + } + + @Nullable + protected abstract DesignerEditorPanelFacade getDesigner(FileEditor editor); + + @Nullable + public DesignerEditorPanelFacade getActiveDesigner() { + for (FileEditor editor : myFileEditorManager.getSelectedEditors()) { + DesignerEditorPanelFacade designer = getDesigner(editor); + if (designer != null) { + return designer; + } + } + + return null; + } + + private void bindToDesigner(final DesignerEditorPanelFacade designer) { + myWindowQueue.cancelAllUpdates(); + myWindowQueue.queue(new Update("update") { + @Override + public void run() { + if (myToolWindowDisposed) { + return; + } + if (myToolWindow == null) { + if (designer == null) { + return; + } + initToolWindow(); + } + updateToolWindow(designer); + } + }); + } + + protected abstract void initToolWindow(); + + protected abstract void updateToolWindow(@Nullable DesignerEditorPanelFacade designer); + + protected final void initGearActions() { + ToolWindowEx toolWindow = (ToolWindowEx)myToolWindow; + toolWindow.setAdditionalGearActions(new DefaultActionGroup(createGearActions())); + } + + protected abstract ToolWindowAnchor getAnchor(); + + @Override + public void initComponent() { + } + + @Override + public void disposeComponent() { + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // LightToolWindow + // + ////////////////////////////////////////////////////////////////////////////////////////// + + public final ActionGroup createGearActions() { + DefaultActionGroup group = new DefaultActionGroup("In Editor Mode", true); + + if (myLeftEditorModeAction == null) { + myLeftEditorModeAction = createToggleAction(ToolWindowAnchor.LEFT); + } + group.add(myLeftEditorModeAction); + + if (myRightEditorModeAction == null) { + myRightEditorModeAction = createToggleAction(ToolWindowAnchor.RIGHT); + } + group.add(myRightEditorModeAction); + + return group; + } + + protected abstract ToggleEditorModeAction createToggleAction(ToolWindowAnchor anchor); + + public final void bind(@NotNull DesignerEditorPanelFacade designer) { + if (isEditorMode()) { + myCreateAction.run(designer); + } + } + + public final void dispose(@NotNull DesignerEditorPanelFacade designer) { + if (isEditorMode()) { + disposeContent(designer); + } + } + + protected final Object getContent(@NotNull DesignerEditorPanelFacade designer) { + LightToolWindow toolWindow = (LightToolWindow)designer.getClientProperty(getComponentName()); + return toolWindow.getContent(); + } + + protected abstract LightToolWindow createContent(@NotNull DesignerEditorPanelFacade designer); + + protected final LightToolWindow createContent(@NotNull DesignerEditorPanelFacade designer, + @NotNull LightToolWindowContent content, + @NotNull String title, + @NotNull Icon icon, + @NotNull JComponent component, + @NotNull JComponent focusedComponent, + int defaultWidth, + @Nullable AnAction[] actions) { + return new LightToolWindow(content, + title, + icon, + component, + focusedComponent, + designer.getContentSplitter(), + getEditorMode(), + this, + myProject, + myPropertiesComponent, + getComponentName(), + defaultWidth, + actions); + } + + protected final void disposeContent(DesignerEditorPanelFacade designer) { + String key = getComponentName(); + LightToolWindow toolWindow = (LightToolWindow)designer.getClientProperty(key); + designer.putClientProperty(key, null); + toolWindow.dispose(); + } + + private final ParameterizedRunnable<DesignerEditorPanelFacade> myCreateAction = new ParameterizedRunnable<DesignerEditorPanelFacade>() { + @Override + public void run(DesignerEditorPanelFacade designer) { + designer.putClientProperty(getComponentName(), createContent(designer)); + } + }; + + private final ParameterizedRunnable<DesignerEditorPanelFacade> myUpdateAnchorAction = + new ParameterizedRunnable<DesignerEditorPanelFacade>() { + @Override + public void run(DesignerEditorPanelFacade designer) { + LightToolWindow toolWindow = (LightToolWindow)designer.getClientProperty(getComponentName()); + toolWindow.updateAnchor(getEditorMode()); + } + }; + + private final ParameterizedRunnable<DesignerEditorPanelFacade> myDisposeAction = new ParameterizedRunnable<DesignerEditorPanelFacade>() { + @Override + public void run(DesignerEditorPanelFacade designer) { + disposeContent(designer); + } + }; + + private void runUpdateContent(ParameterizedRunnable<DesignerEditorPanelFacade> action) { + for (FileEditor editor : myFileEditorManager.getAllEditors()) { + DesignerEditorPanelFacade designer = getDesigner(editor); + if (designer != null) { + action.run(designer); + } + } + } + + protected final boolean isEditorMode() { + return getEditorMode() != null; + } + + @Nullable + final ToolWindowAnchor getEditorMode() { + String value = myPropertiesComponent.getValue(myEditorModeKey); + if (value == null) { + return getAnchor(); + } + return value.equals("ToolWindow") ? null : ToolWindowAnchor.fromText(value); + } + + final void setEditorMode(@Nullable ToolWindowAnchor newState) { + ToolWindowAnchor oldState = getEditorMode(); + myPropertiesComponent.setValue(myEditorModeKey, newState == null ? "ToolWindow" : newState.toString()); + + if (oldState != null && newState != null) { + runUpdateContent(myUpdateAnchorAction); + } + else if (newState != null) { + removeListeners(); + updateToolWindow(null); + runUpdateContent(myCreateAction); + } + else { + runUpdateContent(myDisposeAction); + initListeners(); + bindToDesigner(getActiveDesigner()); + } + } + + final ToolWindow getToolWindow() { + return myToolWindow; + } +}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/ToggleEditorModeAction.java b/platform/platform-impl/src/com/intellij/designer/ToggleEditorModeAction.java new file mode 100644 index 000000000000..668a0405f4b2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/ToggleEditorModeAction.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.designer; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.wm.ToolWindowAnchor; + +/** + * @author Alexander Lobas + */ +public abstract class ToggleEditorModeAction extends ToggleAction { + protected final LightToolWindowManager myManager; + protected final Project myProject; + private final ToolWindowAnchor myAnchor; + + public ToggleEditorModeAction(LightToolWindowManager manager, Project project, ToolWindowAnchor anchor) { + super(StringUtil.capitalize(anchor.toString()), "Pin/unpin tool window to " + anchor + " side UI Designer Editor", null); + myManager = manager; + myProject = project; + myAnchor = anchor; + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myAnchor == myManager.getEditorMode(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + if (state) { + myManager.setEditorMode(myAnchor); + + LightToolWindowManager manager = getOppositeManager(); + if (manager.getEditorMode() == myAnchor) { + manager.setEditorMode(myAnchor == ToolWindowAnchor.LEFT ? ToolWindowAnchor.RIGHT : ToolWindowAnchor.LEFT); + } + } + else { + myManager.setEditorMode(null); + } + } + + protected abstract LightToolWindowManager getOppositeManager(); +}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java b/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java index e430f9fdb513..b951b59ed9dd 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.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,11 +16,11 @@ package com.intellij.diagnostic; import com.intellij.CommonBundle; -import com.intellij.errorreport.ErrorReportSender; import com.intellij.errorreport.bean.ErrorBean; import com.intellij.errorreport.error.InternalEAPException; import com.intellij.errorreport.error.NoSuchEAPUserException; import com.intellij.errorreport.error.UpdateAvailableException; +import com.intellij.errorreport.itn.ITNProxy; import com.intellij.ide.DataManager; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManager; @@ -32,6 +32,7 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.ErrorReportSubmitter; import com.intellij.openapi.diagnostic.IdeaLoggingEvent; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.SubmittedReportInfo; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; @@ -39,7 +40,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Consumer; import com.intellij.xml.util.XmlStringUtil; -import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -49,60 +50,39 @@ import java.awt.*; */ public class ITNReporter extends ErrorReportSubmitter { private static int previousExceptionThreadId = 0; - private static boolean wasException = false; - @NonNls private static final String URL_HEADER = "http://www.intellij.net/tracker/idea/viewSCR?publicId="; + @Override public String getReportActionText() { return DiagnosticBundle.message("error.report.to.jetbrains.action"); } - public SubmittedReportInfo submit(IdeaLoggingEvent[] events, Component parentComponent) { - // obsolete API - return new SubmittedReportInfo(null, "0", SubmittedReportInfo.SubmissionStatus.FAILED); - } - @Override - public boolean trySubmitAsync(IdeaLoggingEvent[] events, - String additionalInfo, - Component parentComponent, - Consumer<SubmittedReportInfo> consumer) { - return sendError(events[0], additionalInfo, parentComponent, consumer); + public boolean submit(@NotNull IdeaLoggingEvent[] events, + String additionalInfo, + @NotNull Component parentComponent, + @NotNull Consumer<SubmittedReportInfo> consumer) { + ErrorBean errorBean = new ErrorBean(events[0].getThrowable(), IdeaLogger.ourLastActionId); + return doSubmit(events[0], parentComponent, consumer, errorBean, additionalInfo); } /** - * Could be used to enable error reporting even in release versions from non-internal mode. - * @return false by default. + * Used to enable error reporting even in release versions. */ - @SuppressWarnings({"MethodMayBeStatic", "UnusedParameters"}) public boolean showErrorInRelease(IdeaLoggingEvent event) { return false; } - /** - * @noinspection ThrowablePrintStackTrace - */ - private static boolean sendError(IdeaLoggingEvent event, - String additionalInfo, - final Component parentComponent, - final Consumer<SubmittedReportInfo> callback) { - ErrorBean errorBean = new ErrorBean(event.getThrowable(), IdeaLogger.ourLastActionId); - - return doSubmit(event, parentComponent, callback, errorBean, additionalInfo); - } - private static boolean doSubmit(final IdeaLoggingEvent event, - final Component parentComponent, - final Consumer<SubmittedReportInfo> callback, - final ErrorBean errorBean, - final String description) { + final Component parentComponent, + final Consumer<SubmittedReportInfo> callback, + final ErrorBean errorBean, + final String description) { final DataContext dataContext = DataManager.getInstance().getDataContext(parentComponent); final Project project = CommonDataKeys.PROJECT.getData(dataContext); - final ErrorReportConfigurable errorReportConfigurable = ErrorReportConfigurable.getInstance(); - if (!errorReportConfigurable.KEEP_ITN_PASSWORD && - !StringUtil.isEmpty(errorReportConfigurable.ITN_LOGIN) && - StringUtil.isEmpty(errorReportConfigurable.getPlainItnPassword())) { - final JetBrainsAccountDialog dlg = new JetBrainsAccountDialog(parentComponent); + ErrorReportConfigurable settings = ErrorReportConfigurable.getInstance(); + if (!settings.KEEP_ITN_PASSWORD && !StringUtil.isEmpty(settings.ITN_LOGIN) && StringUtil.isEmpty(settings.getPlainItnPassword())) { + JetBrainsAccountDialog dlg = new JetBrainsAccountDialog(parentComponent); dlg.show(); if (!dlg.isOK()) { return false; @@ -129,55 +109,46 @@ public class ITNReporter extends ErrorReportSubmitter { } Object data = event.getData(); - if (data instanceof AbstractMessage) { errorBean.setAssigneeId(((AbstractMessage)data).getAssigneeId()); } - if (data instanceof LogMessageEx) { errorBean.setAttachments(((LogMessageEx)data).getAttachments()); } - @NonNls String login = errorReportConfigurable.ITN_LOGIN; - @NonNls String password = errorReportConfigurable.getPlainItnPassword(); - if (login.trim().length() == 0 && password.trim().length() == 0) { + String login = settings.ITN_LOGIN; + String password = settings.getPlainItnPassword(); + if (StringUtil.isEmptyOrSpaces(login) && StringUtil.isEmptyOrSpaces(password)) { login = "idea_anonymous"; password = "guest"; } - ErrorReportSender.sendError(project, login, password, errorBean, new Consumer<Integer>() { - @SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"}) + ITNProxy.sendError(project, login, password, errorBean, new Consumer<Integer>() { @Override public void consume(Integer threadId) { - previousExceptionThreadId = threadId; - wasException = true; - final SubmittedReportInfo reportInfo = new SubmittedReportInfo(URL_HEADER + threadId, String.valueOf(threadId), - SubmittedReportInfo.SubmissionStatus.NEW_ISSUE); + updatePreviousThreadId(threadId); + String url = ITNProxy.getBrowseUrl(threadId); + String linkText = String.valueOf(threadId); + final SubmittedReportInfo reportInfo = new SubmittedReportInfo(url, linkText, SubmittedReportInfo.SubmissionStatus.NEW_ISSUE); callback.consume(reportInfo); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { StringBuilder text = new StringBuilder(); - final String url = IdeErrorsDialog.getUrl(reportInfo, true); - IdeErrorsDialog.appendSubmissionInformation(reportInfo, text, url); - text.append("."); - if (reportInfo.getStatus() != SubmittedReportInfo.SubmissionStatus.FAILED) { - text.append("<br/>").append(DiagnosticBundle.message("error.report.gratitude")); - } - - NotificationType type = reportInfo.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED - ? NotificationType.ERROR - : NotificationType.INFORMATION; - NotificationListener listener = url != null ? new NotificationListener.UrlOpeningListener(true) : null; - ReportMessages.GROUP.createNotification(ReportMessages.ERROR_REPORT, - XmlStringUtil.wrapInHtml(text), - type, listener).setImportant(false).notify(project); + IdeErrorsDialog.appendSubmissionInformation(reportInfo, text); + text.append('.').append("<br/>").append(DiagnosticBundle.message("error.report.gratitude")); + String content = XmlStringUtil.wrapInHtml(text); + ReportMessages.GROUP + .createNotification(ReportMessages.ERROR_REPORT, content, NotificationType.INFORMATION, NotificationListener.URL_OPENING_LISTENER) + .setImportant(false) + .notify(project); } }); } }, new Consumer<Exception>() { @Override public void consume(final Exception e) { + Logger.getInstance(ITNReporter.class).info("reporting failed: " + e); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { @@ -192,13 +163,12 @@ public class ITNReporter extends ErrorReportSubmitter { msg = DiagnosticBundle.message("error.report.sending.failure"); } if (e instanceof UpdateAvailableException) { - String message = DiagnosticBundle.message( - "error.report.new.eap.build.message", e.getMessage()); - showMessageDialog(parentComponent, project, message, CommonBundle.getWarningTitle(), Messages.getWarningIcon()); - callback.consume(new SubmittedReportInfo(null, "0", SubmittedReportInfo.SubmissionStatus.FAILED)); + String message = DiagnosticBundle.message("error.report.new.eap.build.message", e.getMessage()); + showMessageDialog(parentComponent, project, message, CommonBundle.getWarningTitle(), Messages.getWarningIcon()); + callback.consume(new SubmittedReportInfo(SubmittedReportInfo.SubmissionStatus.FAILED)); } else if (showYesNoDialog(parentComponent, project, msg, ReportMessages.ERROR_REPORT, Messages.getErrorIcon()) != Messages.YES) { - callback.consume(new SubmittedReportInfo(null, "0", SubmittedReportInfo.SubmissionStatus.FAILED)); + callback.consume(new SubmittedReportInfo(SubmittedReportInfo.SubmissionStatus.FAILED)); } else { if (e instanceof NoSuchEAPUserException) { @@ -225,10 +195,15 @@ public class ITNReporter extends ErrorReportSubmitter { return true; } + private static void updatePreviousThreadId(Integer threadId) { + previousExceptionThreadId = threadId; + } + private static void showMessageDialog(Component parentComponent, Project project, String message, String title, Icon icon) { if (parentComponent.isShowing()) { Messages.showMessageDialog(parentComponent, message, title, icon); - } else { + } + else { Messages.showMessageDialog(project, message, title, icon); } } @@ -237,7 +212,8 @@ public class ITNReporter extends ErrorReportSubmitter { private static int showYesNoDialog(Component parentComponent, Project project, String message, String title, Icon icon) { if (parentComponent.isShowing()) { return Messages.showYesNoDialog(parentComponent, message, title, icon); - } else { + } + else { return Messages.showYesNoDialog(project, message, title, icon); } } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java b/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java index ac296d677232..a0e96c5d674e 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.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. @@ -563,9 +563,9 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene String url = null; if (message.isSubmitted()) { - final SubmittedReportInfo info = message.getSubmissionInfo(); - url = getUrl(info, getSubmitter(throwable) instanceof ITNReporter); - appendSubmissionInformation(info, text, url); + SubmittedReportInfo info = message.getSubmissionInfo(); + url = info.getURL(); + appendSubmissionInformation(info, text); text.append(". "); } else if (message.isSubmitting()) { @@ -578,33 +578,18 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene myInfoLabel.setHyperlinkTarget(url); } - public static void appendSubmissionInformation(SubmittedReportInfo info, StringBuilder out, @Nullable String url) { + public static void appendSubmissionInformation(SubmittedReportInfo info, StringBuilder out) { if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED) { out.append(" ").append(DiagnosticBundle.message("error.list.message.submission.failed")); } - else { - if (info.getLinkText() != null) { - out.append(" ").append(DiagnosticBundle.message("error.list.message.submitted.as.link", url, info.getLinkText())); - if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.DUPLICATE) { - out.append(" ").append(DiagnosticBundle.message("error.list.message.duplicate")); - } - } - else { - out.append(DiagnosticBundle.message("error.list.message.submitted")); + else if (info.getURL() != null && info.getLinkText() != null) { + out.append(" ").append(DiagnosticBundle.message("error.list.message.submitted.as.link", info.getURL(), info.getLinkText())); + if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.DUPLICATE) { + out.append(" ").append(DiagnosticBundle.message("error.list.message.duplicate")); } } - } - - @Nullable - public static String getUrl(SubmittedReportInfo info, boolean reportedToJetbrains) { - if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED || info.getLinkText() == null) { - return null; - } - if (reportedToJetbrains) { - return "http://ea.jetbrains.com/browser/ea_reports/" + info.getLinkText(); - } else { - return info.getURL(); + out.append(DiagnosticBundle.message("error.list.message.submitted")); } } @@ -919,38 +904,37 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene private boolean reportMessage(final AbstractMessage logMessage, final boolean dialogClosed) { final ErrorReportSubmitter submitter = getSubmitter(logMessage.getThrowable()); + if (submitter == null) return false; - if (submitter != null) { - logMessage.setSubmitting(true); - if (!dialogClosed) { - updateControls(); - } - Container parentComponent; - if (dialogClosed) { - IdeFrame ideFrame = UIUtil.getParentOfType(IdeFrame.class, getContentPane()); - parentComponent = ideFrame.getComponent(); - } - else { - parentComponent = getContentPane(); - } - return submitter.trySubmitAsync(getEvents(logMessage), logMessage.getAdditionalInfo(), parentComponent, - new Consumer<SubmittedReportInfo>() { - @Override - public void consume(final SubmittedReportInfo submittedReportInfo) { - logMessage.setSubmitting(false); - logMessage.setSubmitted(submittedReportInfo); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - if (!dialogClosed) { - updateOnSubmit(); - } - } - }); - } - }); + logMessage.setSubmitting(true); + if (!dialogClosed) { + updateControls(); } - return false; + Container parentComponent; + if (dialogClosed) { + IdeFrame ideFrame = UIUtil.getParentOfType(IdeFrame.class, getContentPane()); + parentComponent = ideFrame.getComponent(); + } + else { + parentComponent = getContentPane(); + } + + return submitter.submit( + getEvents(logMessage), logMessage.getAdditionalInfo(), parentComponent, new Consumer<SubmittedReportInfo>() { + @Override + public void consume(final SubmittedReportInfo submittedReportInfo) { + logMessage.setSubmitting(false); + logMessage.setSubmitted(submittedReportInfo); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (!dialogClosed) { + updateOnSubmit(); + } + } + }); + } + }); } private IdeaLoggingEvent[] getEvents(final AbstractMessage logMessage) { diff --git a/platform/platform-impl/src/com/intellij/diagnostic/SubmitPerformanceReportAction.java b/platform/platform-impl/src/com/intellij/diagnostic/SubmitPerformanceReportAction.java deleted file mode 100644 index 88798381f8a8..000000000000 --- a/platform/platform-impl/src/com/intellij/diagnostic/SubmitPerformanceReportAction.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.diagnostic; - -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.application.ApplicationInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.progress.Task; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.SystemProperties; -import com.intellij.util.io.ZipUtil; -import org.apache.commons.net.ftp.FTPClient; -import org.apache.commons.net.ftp.FTPReply; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.zip.ZipOutputStream; - -/** - * @author yole - */ -public class SubmitPerformanceReportAction extends AnAction implements DumbAware { - private final DateFormat myDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss"); - - private static final String MESSAGE_TITLE = "Submit Performance Report"; - - public void actionPerformed(final AnActionEvent e) { - String reportFileName = "perf_" + ApplicationInfo.getInstance().getBuild().asString() + "_" + - SystemProperties.getUserName() + "_" + myDateFormat.format(new Date()) + ".zip"; - final File reportPath = new File(SystemProperties.getUserHome(), reportFileName); - final File logDir = new File(PathManager.getLogPath()); - final Project project = e.getData(CommonDataKeys.PROJECT); - - final boolean[] archiveCreated = new boolean[1]; - final boolean completed = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { - public void run() { - try { - ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(reportPath)); - ZipUtil.addDirToZipRecursively(zip, reportPath, logDir, "", new FileFilter() { - public boolean accept(final File pathname) { - ProgressManager.checkCanceled(); - - if (logDir.equals(pathname.getParentFile())) { - return pathname.getPath().contains("threadDumps"); - } - return true; - } - }, null); - zip.close(); - archiveCreated[0] = true; - } - catch (final IOException ex) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - Messages.showErrorDialog(project, "Failed to create performance report archive: " + ex.getMessage(), MESSAGE_TITLE); - } - }); - } - } - }, "Collecting Performance Report data", true, project); - - if (!completed || - !archiveCreated[0]) { - return; - } - - int rc = Messages.showYesNoDialog(project, "The performance report has been saved to\n" + reportPath + - "\n\nWould you like to submit it to JetBrains?", MESSAGE_TITLE, - Messages.getQuestionIcon()); - if (rc == Messages.YES) { - ProgressManager.getInstance().run(new Task.Backgroundable(project, "Uploading Performance Report") { - public void run(@NotNull final ProgressIndicator indicator) { - final String error = uploadFileToFTP(reportPath, "ftp.intellij.net", ".uploads", indicator); - if (error != null) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - Messages.showErrorDialog(error, MESSAGE_TITLE); - } - }); - } - } - }); - } - } - - @Nullable - private static String uploadFileToFTP(final File reportPath, @NonNls final String ftpSite, @NonNls final String directory, - final ProgressIndicator indicator) { - FTPClient ftp = new FTPClient(); - ftp.setConnectTimeout(30 * 1000); - try { - indicator.setText("Connecting to server..."); - ftp.connect(ftpSite); - indicator.setText("Connected to server"); - - if (!ftp.login("anonymous", "anonymous@jetbrains.com")) { - return "Failed to login"; - } - indicator.setText("Logged in"); - - // After connection attempt, you should check the reply code to verify - // success. - int reply = ftp.getReplyCode(); - - if (!FTPReply.isPositiveCompletion(reply)) { - ftp.disconnect(); - return "FTP server refused connection: " + reply; - } - if (!ftp.changeWorkingDirectory(directory)) { - return "Failed to change directory"; - } - - // else won't work behind FW - ftp.enterLocalPassiveMode(); - - if (!ftp.setFileType(FTPClient.BINARY_FILE_TYPE)) { - return "Failed to switch to binary mode"; - } - - indicator.setText("Transferring (" + StringUtil.formatFileSize(reportPath.length()) + ")"); - FileInputStream readStream = new FileInputStream(reportPath); - try { - if (!ftp.storeFile(reportPath.getName(), readStream)) { - return "Failed to upload file"; - } - } catch (IOException e) { - return "Error during transfer: " + e.getMessage(); - } - finally { - readStream.close(); - } - ftp.logout(); - return null; - } - catch (IOException e) { - return "Failed to upload: " + e.getMessage(); - } - finally { - if (ftp.isConnected()) { - try { - ftp.disconnect(); - } - catch (IOException ioe) { - // do nothing - } - } - } - } -} diff --git a/platform/platform-impl/src/com/intellij/errorreport/ErrorReportSender.java b/platform/platform-impl/src/com/intellij/errorreport/ErrorReportSender.java deleted file mode 100644 index 7cce2895fd85..000000000000 --- a/platform/platform-impl/src/com/intellij/errorreport/ErrorReportSender.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.errorreport; - -import com.intellij.diagnostic.DiagnosticBundle; -import com.intellij.errorreport.bean.ErrorBean; -import com.intellij.errorreport.itn.ITNProxy; -import com.intellij.idea.IdeaLogger; -import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.progress.Task; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.Consumer; -import com.intellij.util.net.HttpConfigurable; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -/** - * Created by IntelliJ IDEA. - * User: stathik - * Date: May 22, 2003 - * Time: 8:57:19 PM - * To change this template use Options | File Templates. - */ -public class ErrorReportSender { - @NonNls public static final String PREPARE_URL = "http://www.intellij.net/"; - - private ErrorReportSender() { - } - - static class SendTask { - private final Project myProject; - private String myLogin; - private String myPassword; - private ErrorBean errorBean; - - public SendTask(final Project project, ErrorBean errorBean) { - myProject = project; - this.errorBean = errorBean; - } - - public void setCredentials(String login, String password) { - myLogin = login; - myPassword = password; - } - - public void sendReport(final Consumer<Integer> callback, final Consumer<Exception> errback) { - Task.Backgroundable task = new Task.Backgroundable(myProject, DiagnosticBundle.message("title.submitting.error.report")) { - @Override - public void run(@NotNull ProgressIndicator indicator) { - try { - HttpConfigurable.getInstance().prepareURL(PREPARE_URL); - - if (!StringUtil.isEmpty(myLogin)) { - int threadId = ITNProxy.postNewThread( - myLogin, - myPassword, - errorBean, - IdeaLogger.getOurCompilationTimestamp()); - callback.consume(threadId); - } - } - catch (Exception ex) { - errback.consume(ex); - } - } - }; - if (myProject == null) { - task.run(new EmptyProgressIndicator()); - } - else { - ProgressManager.getInstance().run(task); - } - } - } - - public static void sendError(Project project, String login, String password, ErrorBean error, - Consumer<Integer> callback, Consumer<Exception> errback) { - SendTask sendTask = new SendTask(project, error); - sendTask.setCredentials(login, password); - sendTask.sendReport(callback, errback); - } -} diff --git a/platform/platform-impl/src/com/intellij/errorreport/bean/ErrorBean.java b/platform/platform-impl/src/com/intellij/errorreport/bean/ErrorBean.java index 89832a8b7864..d4c17f7a89d7 100644 --- a/platform/platform-impl/src/com/intellij/errorreport/bean/ErrorBean.java +++ b/platform/platform-impl/src/com/intellij/errorreport/bean/ErrorBean.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. @@ -16,10 +16,9 @@ package com.intellij.errorreport.bean; import com.intellij.openapi.diagnostic.Attachment; +import com.intellij.util.ExceptionUtil; import org.jetbrains.annotations.NonNls; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; import java.util.Collections; import java.util.List; @@ -41,11 +40,7 @@ public class ErrorBean { public ErrorBean(Throwable throwable, String lastAction) { if (throwable != null) { message = throwable.getMessage(); - - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - //noinspection IOResourceOpenedButNotSafelyClosed - throwable.printStackTrace(new PrintStream(stream, true)); - stackTrace = stream.toString(); + stackTrace = ExceptionUtil.getThrowableText(throwable); } this.lastAction = lastAction; } diff --git a/platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java b/platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java index ec112437520b..4339b36cbf22 100644 --- a/platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java +++ b/platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java @@ -20,202 +20,292 @@ import com.intellij.errorreport.bean.ErrorBean; import com.intellij.errorreport.error.InternalEAPException; import com.intellij.errorreport.error.NoSuchEAPUserException; import com.intellij.errorreport.error.UpdateAvailableException; +import com.intellij.idea.IdeaLogger; import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diagnostic.Attachment; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; import com.intellij.openapi.updateSettings.impl.UpdateSettings; -import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Consumer; import com.intellij.util.SystemProperties; -import com.intellij.util.net.HttpConfigurable; -import org.jetbrains.annotations.NonNls; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.net.ssl.CertificateUtil; +import org.jetbrains.annotations.NotNull; +import javax.net.ssl.*; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; -import java.util.ArrayList; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.Calendar; -import java.util.Iterator; -import java.util.List; +import java.util.Map; +import java.util.Set; /** - * Created by IntelliJ IDEA. - * User: stathik - * Date: Aug 4, 2003 - * Time: 8:12:00 PM - * To change this template use Options | File Templates. + * @author stathik + * @since Aug 4, 2003 */ public class ITNProxy { - @NonNls public static final String ENCODING = "UTF8"; - public static final String POST_DELIMITER = "&"; + private static final String NEW_THREAD_VIEW_URL = "https://ea.jetbrains.com/browser/ea_reports/"; + private static final String NEW_THREAD_POST_URL = "https://ea-report.jetbrains.com/trackerRpc/idea/createScr"; + private static final String ENCODING = "UTF8"; + + public static void sendError(Project project, + final String login, + final String password, + final ErrorBean error, + final Consumer<Integer> callback, + final Consumer<Exception> errback) { + if (StringUtil.isEmpty(login)) { + return; + } - @NonNls public static final String NEW_THREAD_URL = "http://www.intellij.net/trackerRpc/idea/createScr"; + Task.Backgroundable task = new Task.Backgroundable(project, DiagnosticBundle.message("title.submitting.error.report")) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + int threadId = postNewThread(login, password, error); + callback.consume(threadId); + } + catch (Exception ex) { + errback.consume(ex); + } + } + }; + + if (project == null) { + task.run(new EmptyProgressIndicator()); + } + else { + ProgressManager.getInstance().run(task); + } + } - @NonNls private static final String HTTP_CONTENT_LENGTH = "Content-Length"; - @NonNls private static final String HTTP_CONTENT_TYPE = "Content-Type"; - @NonNls private static final String HTTP_WWW_FORM = "application/x-www-form-urlencoded"; - @NonNls private static final String HTTP_POST = "POST"; + public static String getBrowseUrl(int threadId) { + return NEW_THREAD_VIEW_URL + threadId; + } - public static int postNewThread (String login, String password, ErrorBean error, String compilationTimestamp) - throws IOException, NoSuchEAPUserException, InternalEAPException, UpdateAvailableException { + private static SSLContext ourSslContext; - @NonNls List<Couple<String>> params = createParametersFor(login, - password, - error, - compilationTimestamp, - ApplicationManager.getApplication(), - (ApplicationInfoEx) ApplicationInfo.getInstance(), - ApplicationNamesInfo.getInstance(), - UpdateSettings.getInstance()); + private static int postNewThread(String login, String password, ErrorBean error) throws Exception { + if (ourSslContext == null) { + ourSslContext = initContext(); + } - HttpURLConnection connection = post(new URL(NEW_THREAD_URL), join(params)); + Map<String, String> params = createParameters(login, password, error); + HttpURLConnection connection = post(new URL(NEW_THREAD_POST_URL), join(params)); int responseCode = connection.getResponseCode(); - if (responseCode != HttpURLConnection.HTTP_OK) { throw new InternalEAPException(DiagnosticBundle.message("error.http.result.code", responseCode)); } - String reply; - - InputStream is = new BufferedInputStream(connection.getInputStream()); + String response; + InputStream is = connection.getInputStream(); try { - reply = readFrom(is); - } finally { + byte[] bytes = FileUtil.loadBytes(is); + response = new String(bytes, ENCODING); + } + finally { is.close(); } - if ("unauthorized".equals(reply)) { + if ("unauthorized".equals(response)) { throw new NoSuchEAPUserException(login); } - - if (reply.startsWith("update ")) { - throw new UpdateAvailableException(reply.substring(7)); + if (response.startsWith("update ")) { + throw new UpdateAvailableException(response.substring(7)); } - - if (reply.startsWith("message ")) { - throw new InternalEAPException(reply.substring(8)); + if (response.startsWith("message ")) { + throw new InternalEAPException(response.substring(8)); } try { - return Integer.valueOf(reply.trim()).intValue(); - } catch (NumberFormatException ex) { - // Tibor!!!! :-E + return Integer.valueOf(response.trim()).intValue(); + } + catch (NumberFormatException ex) { throw new InternalEAPException(DiagnosticBundle.message("error.itn.returns.wrong.data")); } } - private static List<Couple<String>> createParametersFor(String login, - String password, - ErrorBean error, - String compilationTimestamp, Application application, ApplicationInfoEx appInfo, - ApplicationNamesInfo namesInfo, - UpdateSettings updateSettings) { - @NonNls List<Couple<String>> params = new ArrayList<Couple<String>>(); - - params.add(Couple.of("protocol.version", "1")); + private static Map<String, String> createParameters(String login, String password, ErrorBean error) { + Map<String, String> params = ContainerUtil.newLinkedHashMap(40); - params.add(Couple.of("user.login", login)); - params.add(Couple.of("user.password", password)); + params.put("protocol.version", "1"); - params.add(Couple.of("os.name", SystemProperties.getOsName())); + params.put("user.login", login); + params.put("user.password", password); - params.add(Couple.of("java.version", SystemProperties.getJavaVersion())); - params.add(Couple.of("java.vm.vendor", SystemProperties.getJavaVmVendor())); + params.put("os.name", SystemProperties.getOsName()); + params.put("java.version", SystemProperties.getJavaVersion()); + params.put("java.vm.vendor", SystemProperties.getJavaVmVendor()); - params.add(Couple.of("app.name", namesInfo.getProductName())); - params.add(Couple.of("app.name.full", namesInfo.getFullProductName())); - params.add(Couple.of("app.name.version", appInfo.getVersionName())); - params.add(Couple.of("app.eap", Boolean.toString(appInfo.isEAP()))); - params.add(Couple.of("app.internal", Boolean.toString(application.isInternal()))); - params.add(Couple.of("app.build", appInfo.getBuild().asString())); - params.add(Couple.of("app.version.major", appInfo.getMajorVersion())); - params.add(Couple.of("app.version.minor", appInfo.getMinorVersion())); - params.add(Couple.of("app.build.date", format(appInfo.getBuildDate()))); - params.add(Couple.of("app.build.date.release", format(appInfo.getMajorReleaseBuildDate()))); - params.add(Couple.of("app.compilation.timestamp", compilationTimestamp)); + ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx(); + ApplicationNamesInfo namesInfo = ApplicationNamesInfo.getInstance(); + Application application = ApplicationManager.getApplication(); + params.put("app.name", namesInfo.getProductName()); + params.put("app.name.full", namesInfo.getFullProductName()); + params.put("app.name.version", appInfo.getVersionName()); + params.put("app.eap", Boolean.toString(appInfo.isEAP())); + params.put("app.internal", Boolean.toString(application.isInternal())); + params.put("app.build", appInfo.getBuild().asString()); + params.put("app.version.major", appInfo.getMajorVersion()); + params.put("app.version.minor", appInfo.getMinorVersion()); + params.put("app.build.date", format(appInfo.getBuildDate())); + params.put("app.build.date.release", format(appInfo.getMajorReleaseBuildDate())); + params.put("app.compilation.timestamp", IdeaLogger.getOurCompilationTimestamp()); - params.add(Couple.of("update.channel.status", updateSettings.getSelectedChannelStatus().getCode())); - params.add(Couple.of("update.ignored.builds", StringUtil.join(updateSettings.getIgnoredBuildNumbers(), ","))); + UpdateSettings updateSettings = UpdateSettings.getInstance(); + params.put("update.channel.status", updateSettings.getSelectedChannelStatus().getCode()); + params.put("update.ignored.builds", StringUtil.join(updateSettings.getIgnoredBuildNumbers(), ",")); - params.add(Couple.of("plugin.name", error.getPluginName())); - params.add(Couple.of("plugin.version", error.getPluginVersion())); + params.put("plugin.name", error.getPluginName()); + params.put("plugin.version", error.getPluginVersion()); - params.add(Couple.of("last.action", error.getLastAction())); - params.add(Couple.of("previous.exception", - error.getPreviousException() == null ? null : Integer.toString(error.getPreviousException()))); + params.put("last.action", error.getLastAction()); + params.put("previous.exception", error.getPreviousException() == null ? null : Integer.toString(error.getPreviousException())); - params.add(Couple.of("error.message", error.getMessage())); - params.add(Couple.of("error.stacktrace", error.getStackTrace())); + params.put("error.message", error.getMessage()); + params.put("error.stacktrace", error.getStackTrace()); + params.put("error.description", error.getDescription()); - params.add(Couple.of("error.description", error.getDescription())); - - params.add(Couple.of("assignee.id", error.getAssigneeId() == null ? null : Integer.toString(error.getAssigneeId()))); + params.put("assignee.id", error.getAssigneeId() == null ? null : Integer.toString(error.getAssigneeId())); for (Attachment attachment : error.getAttachments()) { - params.add(Couple.of("attachment.name", attachment.getName())); - params.add(Couple.of("attachment.value", attachment.getEncodedBytes())); + params.put("attachment.name", attachment.getName()); + params.put("attachment.value", attachment.getEncodedBytes()); } return params; } - private static String readFrom(InputStream is) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int c; - while ((c = is.read()) != -1) { - out.write(c); - } - String s = out.toString(); - out.close(); - return s; - } - private static String format(Calendar calendar) { return calendar == null ? null : Long.toString(calendar.getTime().getTime()); } + private static byte[] join(Map<String, String> params) throws UnsupportedEncodingException { + StringBuilder builder = new StringBuilder(); + for (Map.Entry<String, String> param : params.entrySet()) { + if (StringUtil.isEmpty(param.getKey())) { + throw new IllegalArgumentException(param.toString()); + } + if (builder.length() > 0) { + builder.append('&'); + } + if (StringUtil.isNotEmpty(param.getValue())) { + builder.append(param.getKey()).append('=').append(URLEncoder.encode(param.getValue(), ENCODING)); + } + } + return builder.toString().getBytes(ENCODING); + } + private static HttpURLConnection post(URL url, byte[] bytes) throws IOException { - HttpURLConnection connection = (HttpURLConnection)HttpConfigurable.getInstance().openConnection(url.toString()); + HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); + + connection.setSSLSocketFactory(ourSslContext.getSocketFactory()); + if (!SystemInfo.isJavaVersionAtLeast("1.7") || !SystemProperties.getBooleanProperty("jsse.enableSNIExtension", true)) { + connection.setHostnameVerifier(new EaHostnameVerifier(url.getHost(), "ftp.intellij.net")); + } - connection.setRequestMethod(HTTP_POST); + connection.setRequestMethod("POST"); connection.setDoInput(true); connection.setDoOutput(true); - connection.setRequestProperty(HTTP_CONTENT_TYPE, String.format("%s; charset=%s", HTTP_WWW_FORM, ENCODING)); - connection.setRequestProperty(HTTP_CONTENT_LENGTH, Integer.toString(bytes.length)); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=" + ENCODING); + connection.setRequestProperty("Content-Length", Integer.toString(bytes.length)); - OutputStream out = new BufferedOutputStream(connection.getOutputStream()); + OutputStream out = connection.getOutputStream(); try { out.write(bytes); - out.flush(); - } finally { + } + finally { out.close(); } return connection; } - private static byte[] join(List<Couple<String>> params) throws UnsupportedEncodingException { - StringBuilder builder = new StringBuilder(); - - Iterator<Couple<String>> it = params.iterator(); - - while (it.hasNext()) { - Couple<String> param = it.next(); - - if (StringUtil.isEmpty(param.first)) - throw new IllegalArgumentException(param.toString()); + private synchronized static SSLContext initContext() throws GeneralSecurityException, IOException { + CertificateFactory cf = CertificateFactory.getInstance(CertificateUtil.X509); + Certificate ca = cf.generateCertificate(new ByteArrayInputStream(JB_CA_CERT.getBytes(ENCODING))); + KeyStore ks = KeyStore.getInstance(CertificateUtil.JKS); + ks.load(null, null); + ks.setCertificateEntry("JetBrains CA", ca); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(CertificateUtil.X509); + tmf.init(ks); + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(null, tmf.getTrustManagers(), null); + return ctx; + } - if (StringUtil.isNotEmpty(param.second)) - builder.append(param.first).append("=").append(URLEncoder.encode(param.second, ENCODING)); + private static class EaHostnameVerifier implements HostnameVerifier { + private final Set<String> myAllowedHosts; - if (it.hasNext()) - builder.append(POST_DELIMITER); + public EaHostnameVerifier(@NotNull String... allowedHosts) { + myAllowedHosts = ContainerUtil.newHashSet(allowedHosts); } - return builder.toString().getBytes(); + @Override + public boolean verify(String hostname, SSLSession session) { + try { + Certificate[] certificates = session.getPeerCertificates(); + if (certificates.length > 0) { + Certificate certificate = certificates[0]; + if (certificate instanceof X509Certificate) { + String cn = CertificateUtil.getCommonName((X509Certificate)certificate); + return myAllowedHosts.contains(cn); + } + } + } + catch (SSLPeerUnverifiedException ignored) { } + return false; + } } + + @SuppressWarnings("SpellCheckingInspection") private static final String JB_CA_CERT = + "-----BEGIN CERTIFICATE-----\n" + + "MIIFvjCCA6agAwIBAgIQMYHnK1dpIZVCoitWqBwhXjANBgkqhkiG9w0BAQsFADBn\n" + + "MRMwEQYKCZImiZPyLGQBGRYDTmV0MRgwFgYKCZImiZPyLGQBGRYISW50ZWxsaUox\n" + + "FDASBgoJkiaJk/IsZAEZFgRMYWJzMSAwHgYDVQQDExdKZXRCcmFpbnMgRW50ZXJw\n" + + "cmlzZSBDQTAeFw0xMjEyMjkxMDEyMzJaFw0zMjEyMjkxMDIyMzBaMGcxEzARBgoJ\n" + + "kiaJk/IsZAEZFgNOZXQxGDAWBgoJkiaJk/IsZAEZFghJbnRlbGxpSjEUMBIGCgmS\n" + + "JomT8ixkARkWBExhYnMxIDAeBgNVBAMTF0pldEJyYWlucyBFbnRlcnByaXNlIENB\n" + + "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzPCE2gPgKECo5CB3BTAw\n" + + "4XrrNpg+YwTMzeNNDYs4VdPzBq0snWsbm5qP6z1GBGUTr4agERQUxc4//gZMR0UJ\n" + + "89GWVNYPbZ/MrkfyaOiem8xosuZ+7WoFu4nYnKbBBMBA7S2idrPSmPv2wYiHJCY7\n" + + "eN2AdViiFSAUeGw/7pIgou92/4Bbm6SSzRBKBYfRIfwq0ZgETSIjhNR5o3XJB5i2\n" + + "CkSjMk7kNiMWBaq+Alv+Um/xMFnl5jiq9H7YAALgH/mZHr8ANniSyBwkj4r/7GQ3\n" + + "UIYwoLrGxSOSEY9UhEpdqQkRbSSjQiFYMlhYEAtLERK4KZObTuUgdiE6Wk38EOKZ\n" + + "wy1eE/EIh8vWBHFSH5opPSK4dyamxj9o5c2g1hJ07ZBUCV/nsrKb+ruMkwBfI286\n" + + "+HPTMUmoKuUfSfHZ5TiuF5EvcSD7Df2ZCFpRugPs26FRGvtsiBMEmu4u6fu5RNkh\n" + + "s7Ueq6ISblt6dj/youywiAZnyrtNKJVyK0m051g9b2IokHjrk9XTswTqBHDjZKYr\n" + + "YG/5jDSSzvR/ptR9YIrHF0a9A6LQLZ6ews4FUO6O/RhiYXV8FggD7ZUg019OBUx3\n" + + "rF1L3GBYA8YhYP/N18r8DqOaFgUiRDyeRMbka9OXZ2KJT6iL+mOfg/svSW8lc4Ly\n" + + "EgcyJ9sk7MRwrhlp3Kc0W7UCAwEAAaNmMGQwEwYJKwYBBAGCNxQCBAYeBABDAEEw\n" + + "CwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFB/HK/yYoWW9\n" + + "vr2XAyhcMmV3gSfGMBAGCSsGAQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBCwUAA4IC\n" + + "AQBnYu49dZRBK9W3voy6bgzz64sZfX51/RIA6aaoHAH3U1bC8EepChqWeRgijGCD\n" + + "CBvLTk7bk/7fgXPPvL+8RwYaxEewCi7t1RQKqPmNvUnEnw28OLvYLBEO7a4yeN5Y\n" + + "YaZwdfVH+0qMvTqMQku5p5Xx3dY+DAm4EqXEFD0svfeMJmOA+R1CIqRz1CXnN2FY\n" + + "A+86m7WLmGZ8oWlRUJDa1etqrE3ZxXHH/IunVJOGOfaQVkid3u3ageyUOnMw/iME\n" + + "7vi0UNVYVsCjXYZxrzCDLCxtguZaV4rMYvLRt1oUxZ+VnmdVa3aW0W//GQ70sqh2\n" + + "KQDtIF6Iumf8ya4vA0+K+AAowOSR/k4jQzlWQdZvJNMHP/Jc0OyJyHEegjtWssrS\n" + + "NoRtI6V4j277ugWF1Xpt1x0YxYyGSZTI4rqGLqVT8x6Llr24YaHCdp56rKWC/5ob\n" + + "IFZ7tJys7oQqof11ANDExrnHv/FEE39VDlfEIUVGyCpsyKbzO7MPfdOce2bIaQOS\n" + + "dQ76TpYClrnezikJgp9MSQmd3+ozs9w1upGynHNGNmVhzZ5sex9voWcGoyjmOFhs\n" + + "wg13S9Hjy3VYq8y0krRYLEGLctd4vnxWGzJzUNSnqezwHZRl4v4Ejp3dQUZP+5sY\n" + + "1F81Vj1G264YnZAcWp5x3GTI4K6+k9Xx3pwUPcKOYdlpZQ==\n" + + "-----END CERTIFICATE-----\n"; } diff --git a/platform/platform-impl/src/com/intellij/featureStatistics/FeatureUsageTrackerImpl.java b/platform/platform-impl/src/com/intellij/featureStatistics/FeatureUsageTrackerImpl.java index 21a44f1c6718..78b13aa63940 100644 --- a/platform/platform-impl/src/com/intellij/featureStatistics/FeatureUsageTrackerImpl.java +++ b/platform/platform-impl/src/com/intellij/featureStatistics/FeatureUsageTrackerImpl.java @@ -15,10 +15,7 @@ */ package com.intellij.featureStatistics; -import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; -import com.intellij.openapi.components.StoragePathMacros; +import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Element; @@ -30,9 +27,9 @@ import java.util.Set; @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"}) @State( - name = "FeatureUsageStatistics", - storages = {@Storage( - file = StoragePathMacros.APP_CONFIG + "/feature.usage.statistics.xml")}) + name = "FeatureUsageStatistics", + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/feature.usage.statistics.xml", roamingType = RoamingType.DISABLED)} +) public class FeatureUsageTrackerImpl extends FeatureUsageTracker implements PersistentStateComponent<Element> { private static final int HOUR = 1000 * 60 * 60; private static final long DAY = HOUR * 24; @@ -94,7 +91,7 @@ public class FeatureUsageTrackerImpl extends FeatureUsageTracker implements Pers if (descriptor != null && System.currentTimeMillis() - descriptor.getLastTimeUsed() > 10 * DAY) { return true; } - + return isToBeShown(featureId, project, HOUR); } diff --git a/platform/platform-impl/src/com/intellij/ide/ApplicationLoadListener.java b/platform/platform-impl/src/com/intellij/ide/ApplicationLoadListener.java index f1d11ff938d9..b1432f87e10d 100644 --- a/platform/platform-impl/src/com/intellij/ide/ApplicationLoadListener.java +++ b/platform/platform-impl/src/com/intellij/ide/ApplicationLoadListener.java @@ -17,9 +17,10 @@ package com.intellij.ide; import com.intellij.openapi.application.Application; import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.NotNull; public interface ApplicationLoadListener { ExtensionPointName<ApplicationLoadListener> EP_NAME = ExtensionPointName.create("com.intellij.ApplicationLoadListener"); - void beforeApplicationLoaded(Application application); + void beforeApplicationLoaded(@NotNull Application application); } diff --git a/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java b/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java index c288150bd7ab..293c4a55eeae 100644 --- a/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java +++ b/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java @@ -141,7 +141,7 @@ public class CommandLineProcessor { starter instanceof ApplicationStarterEx && ((ApplicationStarterEx)starter).canProcessExternalCommandLine()) { LOG.info("Processing command with " + starter); - ((ApplicationStarterEx) starter).processExternalCommandLine(ArrayUtil.toStringArray(args)); + ((ApplicationStarterEx) starter).processExternalCommandLine(ArrayUtil.toStringArray(args), currentDirectory); return null; } } diff --git a/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java b/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java index 4189b6adf98b..59b115a43105 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java +++ b/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java @@ -34,17 +34,14 @@ import org.jetbrains.annotations.Nullable; */ @State( name = "RecentDirectoryProjectsManager", - roamingType = RoamingType.DISABLED, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/other.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", roamingType = RoamingType.DISABLED)} ) public class RecentDirectoryProjectsManager extends RecentProjectsManagerBase { public RecentDirectoryProjectsManager(MessageBus messageBus) { super(messageBus); } + @Override @Nullable protected String getProjectPath(@NotNull Project project) { final ProjectBaseDirectory baseDir = ProjectBaseDirectory.getInstance(project); @@ -52,6 +49,7 @@ public class RecentDirectoryProjectsManager extends RecentProjectsManagerBase { return baseDirVFile != null ? FileUtil.toSystemDependentName(baseDirVFile.getPath()) : null; } + @Override protected void doOpenProject(@NotNull String projectPath, Project projectToClose, boolean forceOpenInNewFrame) { final VirtualFile projectDir = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(projectPath)); if (projectDir != null) { diff --git a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java index 1a6bffbcd297..7991c3af0104 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java +++ b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java @@ -86,6 +86,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene messageBus.connect().subscribe(AppLifecycleListener.TOPIC, new MyAppLifecycleListener()); } + @Override public State getState() { synchronized (myStateLock) { myState.validateRecentProjects(); @@ -93,6 +94,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene } } + @Override public void loadState(final State state) { synchronized (myStateLock) { myState = state; @@ -212,7 +214,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene if (addClearListItem) { AnAction clearListAction = new DumbAwareAction(IdeBundle.message("action.clear.list")) { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { String message = IdeBundle.message("action.clear.list.message"); String title = IdeBundle.message("action.clear.list.title"); if (Messages.showOkCancelDialog(e.getProject(), message, title, Messages.getQuestionIcon()) == Messages.OK) { @@ -249,6 +251,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene return file.exists() && (!file.isDirectory() || new File(file, Project.DIRECTORY_STORE_FOLDER).exists()); } + @Override public void projectOpened(final Project project) { String path = getProjectPath(project); if (path != null) { @@ -269,6 +272,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene } } + @Override public void projectClosed(final Project project) { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); if (openProjects.length > 0) { @@ -347,6 +351,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene } private class MyAppLifecycleListener extends AppLifecycleListener.Adapter { + @Override public void appFrameCreated(final String[] commandLineArgs, @NotNull final Ref<Boolean> willOpenProject) { if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { ProjectManager.getInstance().addProjectManagerListener(RecentProjectsManagerBase.this); @@ -356,19 +361,23 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene } } + @Override public void appStarting(Project projectFromCommandLine) { if (projectFromCommandLine != null) return; doReopenLastProject(); } + @Override public void projectFrameClosed() { updateLastProjectPath(); } + @Override public void projectOpenFailed() { updateLastProjectPath(); } + @Override public void appClosing() { updateLastProjectPath(); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/AboutAction.java b/platform/platform-impl/src/com/intellij/ide/actions/AboutAction.java index e0c9373bbf2b..43cc4d0be9d8 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/AboutAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/AboutAction.java @@ -16,6 +16,7 @@ package com.intellij.ide.actions; import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -31,7 +32,7 @@ import java.awt.*; public class AboutAction extends AnAction implements DumbAware { @Override public void update(AnActionEvent e) { - e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu); + e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu || !ActionPlaces.MAIN_MENU.equals(e.getPlace())); e.getPresentation().setDescription("Show information about " + ApplicationNamesInfo.getInstance().getFullProductName()); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java b/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java index 77544ae1d14c..8b1375d9f933 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java @@ -41,7 +41,7 @@ public class AssociateFileType extends AnAction { // the action should also be available for files which have been auto-detected as text or as a particular language (IDEA-79574) haveSmthToDo = FileTypeManager.getInstance().getFileTypeByFileName(file.getName()) == FileTypes.UNKNOWN; } - presentation.setVisible(haveSmthToDo || ActionPlaces.MAIN_MENU.equals(e.getPlace())); + presentation.setVisible(haveSmthToDo || ActionPlaces.isMainMenuOrActionSearch(e.getPlace())); presentation.setEnabled(haveSmthToDo); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/BaseShowRecentFilesAction.java b/platform/platform-impl/src/com/intellij/ide/actions/BaseShowRecentFilesAction.java index 442d09b8b3bd..505d0f5eef3d 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/BaseShowRecentFilesAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/BaseShowRecentFilesAction.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. @@ -39,6 +39,7 @@ import com.intellij.ui.components.JBList; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.IconUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -53,11 +54,11 @@ import java.io.File; public abstract class BaseShowRecentFilesAction extends AnAction implements DumbAware { private static final Color BORDER_COLOR = Gray._135; - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { show(CommonDataKeys.PROJECT.getData(e.getDataContext())); } - public void update(AnActionEvent event){ + public void update(@NotNull AnActionEvent event){ Presentation presentation = event.getPresentation(); Project project = CommonDataKeys.PROJECT.getData(event.getDataContext()); presentation.setEnabled(project != null); @@ -93,7 +94,7 @@ public abstract class BaseShowRecentFilesAction extends AnAction implements Dumb final JList list = new JBList(model); list.addKeyListener( new KeyAdapter() { - public void keyPressed(KeyEvent e) { + public void keyPressed(@NotNull KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_DELETE) { int index = list.getSelectedIndex(); if (index == -1 || index >= list.getModel().getSize()){ @@ -150,7 +151,7 @@ public abstract class BaseShowRecentFilesAction extends AnAction implements Dumb JPanel footerPanel = new JPanel(new BorderLayout()) { @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { super.paintComponent(g); g.setColor(BORDER_COLOR); g.drawLine(0, 0, getWidth(), 0); @@ -237,7 +238,7 @@ public abstract class BaseShowRecentFilesAction extends AnAction implements Dumb } @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(@NotNull ActionEvent e) { if (myPopup != null) { myPopup.cancel(); } @@ -272,7 +273,7 @@ public abstract class BaseShowRecentFilesAction extends AnAction implements Dumb return fullText; } - public void valueChanged(final ListSelectionEvent e) { + public void valueChanged(@NotNull final ListSelectionEvent e) { //noinspection SSBasedInspection SwingUtilities.invokeLater(new Runnable() { public void run() { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CreateLauncherScriptAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CreateLauncherScriptAction.java index 411f2d9d93bb..0b696c8f4496 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CreateLauncherScriptAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CreateLauncherScriptAction.java @@ -134,12 +134,12 @@ public class CreateLauncherScriptAction extends DumbAwareAction { private static File createLauncherScriptFile() throws IOException, ExecutionException { String runPath = PathManager.getHomePath(); + final String productName = ApplicationNamesInfo.getInstance().getProductName().toLowerCase(); if (!SystemInfo.isMac) { // for Macs just use "*.app" - final String productName = ApplicationNamesInfo.getInstance().getProductName().toLowerCase(); runPath += "/bin/" + productName + ".sh"; } else if (runPath.endsWith(CONTENTS)) { - runPath = runPath.substring(0, runPath.length() - CONTENTS.length()); + runPath += "/MacOS/" + productName; } String launcherContents = ExecUtil.loadTemplate(CreateLauncherScriptAction.class.getClassLoader(), "launcher.py", newHashMap(asList("$CONFIG_PATH$", "$RUN_PATH$"), diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ExitAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ExitAction.java index 52487a57f9f2..3017b8624828 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ExitAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ExitAction.java @@ -16,6 +16,7 @@ */ package com.intellij.ide.actions; +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ex.ApplicationManagerEx; @@ -24,7 +25,7 @@ import com.intellij.openapi.util.SystemInfo; public class ExitAction extends AnAction implements DumbAware { public void update(AnActionEvent e) { - e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu); + e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu || !ActionPlaces.MAIN_MENU.equals(e.getPlace())); } public void actionPerformed(AnActionEvent e) { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/OccurenceNavigatorActionBase.java b/platform/platform-impl/src/com/intellij/ide/actions/OccurenceNavigatorActionBase.java index f2314a728913..c7ae78f0cd59 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/OccurenceNavigatorActionBase.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/OccurenceNavigatorActionBase.java @@ -68,14 +68,14 @@ abstract class OccurenceNavigatorActionBase extends AnAction implements DumbAwar if (project == null) { presentation.setEnabled(false); // make it invisible only in main menu to avoid initial invisibility in toolbars - presentation.setVisible(!ActionPlaces.MAIN_MENU.equals(event.getPlace())); + presentation.setVisible(!ActionPlaces.isMainMenuOrActionSearch(event.getPlace())); return; } OccurenceNavigator navigator = getNavigator(event.getDataContext()); if (navigator == null) { presentation.setEnabled(false); // make it invisible only in main menu to avoid initial invisibility in toolbars - presentation.setVisible(!ActionPlaces.MAIN_MENU.equals(event.getPlace())); + presentation.setVisible(!ActionPlaces.isMainMenuOrActionSearch(event.getPlace())); return; } presentation.setVisible(true); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/PinActiveTabAction.java b/platform/platform-impl/src/com/intellij/ide/actions/PinActiveTabAction.java index ddfc1a3989aa..560082bf9e64 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/PinActiveTabAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/PinActiveTabAction.java @@ -116,12 +116,17 @@ public class PinActiveTabAction extends ToggleAction implements DumbAware { super.update(e); Presentation presentation = e.getPresentation(); DataContext context = e.getDataContext(); - if (getFile(context) != null) { - presentation.setEnabledAndVisible(true); - } - else { - Content content = getContent(context); - presentation.setEnabledAndVisible(content != null && content.isPinnable()); + EditorWindow window = getEditorWindow(context); + if (window == null || window.getOwner().isPreview()) { + presentation.setEnabledAndVisible(false); + } else { + if (getFile(context) != null) { + presentation.setEnabledAndVisible(true); + } + else { + Content content = getContent(context); + presentation.setEnabledAndVisible(content != null && content.isPinnable()); + } } if (ActionPlaces.EDITOR_TAB_POPUP.equals(e.getPlace()) || ViewContext.CELL_POPUP_PLACE.equals(e.getPlace())) { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java index 59ac567af38a..abd875af728f 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.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. @@ -25,13 +25,14 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; /** * @author Konstantin Bulenkov */ public class ShowRecentFilesAction extends DumbAwareAction { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { final Project project = e.getData(CommonDataKeys.PROJECT); if (project != null) { FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.recent.files"); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsAction.java index eda6ec9f2d7c..5ced3f0db498 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsAction.java @@ -34,7 +34,7 @@ public class ShowSettingsAction extends AnAction implements DumbAware { @Override public void update(AnActionEvent e) { - if (SystemInfo.isMac && e.getPlace().equals(ActionPlaces.MAIN_MENU)) { + if (SystemInfo.isMac && ActionPlaces.isMainMenuOrActionSearch(e.getPlace())) { // It's called from Preferences in App menu. e.getPresentation().setVisible(false); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java index 2c5cc5b02282..8d6f806802c2 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java @@ -75,7 +75,7 @@ public class ShowSettingsUtilImpl extends ShowSettingsUtil { new IdeConfigurablesGroup()}; return Registry.is("ide.new.settings.dialog") - ? MixedConfigurableGroup.getGroups(getConfigurables(groups, true)) + ? new ConfigurableGroup[]{new SortedConfigurableGroup(getConfigurables(groups, true))} : groups; } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/SplitAction.java b/platform/platform-impl/src/com/intellij/ide/actions/SplitAction.java index fb55f3649484..9e9a349bb3fd 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/SplitAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/SplitAction.java @@ -60,7 +60,8 @@ public abstract class SplitAction extends AnAction implements DumbAware { final int minimum = myCloseSource ? 2 : 1; final boolean enabled = project != null && window != null - && window.getTabCount() >= minimum; + && window.getTabCount() >= minimum + && !window.getOwner().isPreview(); event.getPresentation().setEnabled(enabled); } } 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 cb35b5a504f9..cecf0f60a803 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java @@ -94,7 +94,7 @@ public class Switcher extends AnAction implements DumbAware { public void run() { synchronized (Switcher.class) { if (SWITCHER != null) { - SWITCHER.navigate(); + SWITCHER.navigate(false); } } } @@ -135,7 +135,7 @@ public class Switcher extends AnAction implements DumbAware { @NonNls private static final String SWITCHER_TITLE = "Switcher"; - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); if (project == null) return; @@ -200,7 +200,7 @@ public class Switcher extends AnAction implements DumbAware { jList.setSelectedIndex(jList.getAnchorSelectionIndex()); } if (jList.getSelectedIndex() != -1) { - navigate(); + navigate(false); } } return true; @@ -226,7 +226,7 @@ public class Switcher extends AnAction implements DumbAware { descriptions = new JPanel(new BorderLayout()) { @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { super.paintComponent(g); g.setColor(UIUtil.isUnderDarcula() ? SEPARATOR_COLOR : BORDER_COLOR); g.drawLine(0, 0, getWidth(), 0); @@ -249,7 +249,7 @@ public class Switcher extends AnAction implements DumbAware { final Map<ToolWindow, String> map = ContainerUtil.reverseMap(twShortcuts); Collections.sort(windows, new Comparator<ToolWindow>() { @Override - public int compare(ToolWindow o1, ToolWindow o2) { + public int compare(@NotNull ToolWindow o1, @NotNull ToolWindow o2) { return StringUtil.compare(map.get(o1), map.get(o2), false); } }); @@ -297,7 +297,7 @@ public class Switcher extends AnAction implements DumbAware { toolWindows.addMouseMotionListener(this); myClickListener.installOn(toolWindows); toolWindows.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - public void valueChanged(ListSelectionEvent e) { + public void valueChanged(@NotNull ListSelectionEvent e) { if (!toolWindows.isSelectionEmpty() && !files.isSelectionEmpty()) { files.clearSelection(); } @@ -306,7 +306,7 @@ public class Switcher extends AnAction implements DumbAware { separator = new JPanel() { @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { super.paintComponent(g); g.setColor(SEPARATOR_COLOR); g.drawLine(0, 0, 0, getHeight()); @@ -374,7 +374,7 @@ public class Switcher extends AnAction implements DumbAware { JPanel myPanel = new JPanel(new BorderLayout()); JLabel myLabel = new JLabel() { @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { GraphicsConfig config = new GraphicsConfig(g); ((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f)); super.paintComponent(g); @@ -425,7 +425,7 @@ public class Switcher extends AnAction implements DumbAware { return fullText; } - public void valueChanged(final ListSelectionEvent e) { + public void valueChanged(@NotNull final ListSelectionEvent e) { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { updatePathLabel(); @@ -466,7 +466,7 @@ public class Switcher extends AnAction implements DumbAware { files.setSelectionMode(pinned ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION); files.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - public void valueChanged(ListSelectionEvent e) { + public void valueChanged(@NotNull ListSelectionEvent e) { if (!files.isSelectionEmpty() && !toolWindows.isSelectionEmpty()) { toolWindows.getSelectionModel().clearSelection(); } @@ -526,21 +526,21 @@ public class Switcher extends AnAction implements DumbAware { if (isPinnedMode()) { new AnAction(null ,null ,null) { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { changeSelection(); } }.registerCustomShortcutSet(CustomShortcutSet.fromString("TAB"), this, myPopup); new AnAction(null, null, null) { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { //suppress all actions to activate a toolwindow : IDEA-71277 } }.registerCustomShortcutSet(TW_SHORTCUT, this, myPopup); new AnAction(null, null, null) { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { if (mySpeedSearch != null && mySpeedSearch.isPopupActive()) { mySpeedSearch.hidePopup(); } else { @@ -590,24 +590,24 @@ public class Switcher extends AnAction implements DumbAware { return ((KeyboardShortcut)shortcutSet.getShortcuts()[0]).getFirstKeyStroke().getModifiers(); } - public void keyTyped(KeyEvent e) { + public void keyTyped(@NotNull KeyEvent e) { } public void keyReleased(KeyEvent e) { - if ((e.getKeyCode() == CTRL_KEY && isAutoHide()) - || e.getKeyCode() == VK_ENTER) { - navigate(); - } else - if (e.getKeyCode() == VK_LEFT) { + if (e.getKeyCode() == CTRL_KEY && isAutoHide() || e.getKeyCode() == VK_ENTER) { + navigate(e.isShiftDown()); + } + else if (e.getKeyCode() == VK_LEFT) { goLeft(); - } else if (e.getKeyCode() == VK_RIGHT) { + } + else if (e.getKeyCode() == VK_RIGHT) { goRight(); } } KeyEvent lastEvent; - public void keyPressed(KeyEvent e) { - if ((mySpeedSearch != null && mySpeedSearch.isPopupActive()) || lastEvent == e) return; + public void keyPressed(@NotNull KeyEvent e) { + if (mySpeedSearch != null && mySpeedSearch.isPopupActive() || lastEvent == e) return; lastEvent = e; switch (e.getKeyCode()) { case VK_UP: @@ -668,7 +668,7 @@ public class Switcher extends AnAction implements DumbAware { if (value instanceof FileInfo) { final FileInfo info = (FileInfo)value; final VirtualFile virtualFile = info.first; - final FileEditorManagerImpl editorManager = ((FileEditorManagerImpl)FileEditorManager.getInstance(project)); + final FileEditorManagerImpl editorManager = (FileEditorManagerImpl)FileEditorManager.getInstance(project); final JList jList = getSelectedList(); final EditorWindow wnd = findAppropriateWindow(info); if (wnd == null) { @@ -836,7 +836,7 @@ public class Switcher extends AnAction implements DumbAware { } } - void navigate() { + void navigate(final boolean openInNewWindow) { final Object[] values = getSelectedList().getSelectedValues(); myPopup.closeOk(null); if (values.length > 0 && values[0] instanceof ToolWindow) { @@ -850,14 +850,19 @@ public class Switcher extends AnAction implements DumbAware { if (value instanceof FileInfo) { final FileInfo info = (FileInfo)value; - if (info.second != null) { + VirtualFile file = info.first; + if (openInNewWindow) { + manager.openFileInNewWindow(file); + } + else if (info.second != null) { EditorWindow wnd = findAppropriateWindow(info); if (wnd != null) { - manager.openFileImpl2(wnd, info.first, true); - manager.addSelectionRecord(info.first, wnd); + manager.openFileImpl2(wnd, file, true); + manager.addSelectionRecord(file, wnd); } - } else { - manager.openFile(info.first, true, true); + } + else { + manager.openFile(file, true, true); } } } @@ -873,13 +878,13 @@ public class Switcher extends AnAction implements DumbAware { return ArrayUtil.contains(info.second, windows) ? info.second : windows.length > 0 ? windows[0] : null; } - public void mouseClicked(MouseEvent e) { + public void mouseClicked(@NotNull MouseEvent e) { } private boolean mouseMovedFirstTime = true; private JList mouseMoveSrc = null; private int mouseMoveListIndex = -1; - public void mouseMoved(MouseEvent e) { + public void mouseMoved(@NotNull MouseEvent e) { if (mouseMovedFirstTime) { mouseMovedFirstTime = false; return; @@ -907,15 +912,15 @@ public class Switcher extends AnAction implements DumbAware { files.repaint(); } - public void mousePressed(MouseEvent e) {} - public void mouseReleased(MouseEvent e) {} - public void mouseEntered(MouseEvent e) {} - public void mouseExited(MouseEvent e) { + public void mousePressed(@NotNull MouseEvent e) {} + public void mouseReleased(@NotNull MouseEvent e) {} + public void mouseEntered(@NotNull MouseEvent e) {} + public void mouseExited(@NotNull MouseEvent e) { mouseMoveSrc = null; mouseMoveListIndex = -1; repaintLists(); } - public void mouseDragged(MouseEvent e) {} + public void mouseDragged(@NotNull MouseEvent e) {} private class SwitcherSpeedSearch extends SpeedSearchBase<SwitcherPanel> implements PropertyChangeListener { private Object[] myElements; @@ -1031,7 +1036,7 @@ public class Switcher extends AnAction implements DumbAware { } @Override - public void propertyChange(PropertyChangeEvent evt) { + public void propertyChange(@NotNull PropertyChangeEvent evt) { final MyList list = getSelectedList(); final Object value = list.getSelectedValue(); if (project.isDisposed()) { @@ -1068,7 +1073,7 @@ public class Switcher extends AnAction implements DumbAware { private Rectangle dBounds; @Override - public void layoutContainer(Container target) { + public void layoutContainer(@NotNull Container target) { final JScrollPane scrollPane = UIUtil.getParentOfType(JScrollPane.class, files); JComponent filesPane = scrollPane != null ? scrollPane : files; if (sBounds == null || !target.isShowing()) { @@ -1142,7 +1147,7 @@ public class Switcher extends AnAction implements DumbAware { } @Override - public void processKeyEvent(KeyEvent e) { + public void processKeyEvent(@NotNull KeyEvent e) { super.processKeyEvent(e); } } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java index af3c193ca9d9..04beaa0f370e 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java @@ -182,9 +182,21 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis } @Override + public void doCancelAction() { + doOKAction(); + } + + @Override protected void doOKAction() { for (AbstractCustomizeWizardStep step : mySteps) { - if (!step.beforeOkAction()) return; + if (!step.beforeOkAction()) { + int index = mySteps.indexOf(step); + if (myIndex != index) { + myIndex = index; + initCurrentStep(true); + } + return; + } } super.doOKAction(); } diff --git a/platform/platform-impl/src/com/intellij/ide/dnd/aware/DnDAwareTree.java b/platform/platform-impl/src/com/intellij/ide/dnd/aware/DnDAwareTree.java index e871ff1872c2..7411c70e5984 100644 --- a/platform/platform-impl/src/com/intellij/ide/dnd/aware/DnDAwareTree.java +++ b/platform/platform-impl/src/com/intellij/ide/dnd/aware/DnDAwareTree.java @@ -19,6 +19,7 @@ import com.intellij.ide.dnd.DnDAware; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.treeStructure.Tree; +import com.intellij.util.ui.JBSwingUtilities; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import com.intellij.util.ui.tree.WideSelectionTreeUI; @@ -54,7 +55,7 @@ public class DnDAwareTree extends Tree implements DnDAware { @Override protected void processMouseMotionEvent(MouseEvent e) { - if (SystemInfo.isMac && SwingUtilities.isRightMouseButton(e) && e.getID() == MouseEvent.MOUSE_DRAGGED) return; + if (SystemInfo.isMac && JBSwingUtilities.isRightMouseButton(e) && e.getID() == MouseEvent.MOUSE_DRAGGED) return; super.processMouseMotionEvent(e); } 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 de5d67b5e2d8..4d3a502c253e 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 @@ -193,7 +193,7 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider { } return myKey.get().get(); } - }, project == null ? Condition.FALSE : project.getDisposed()); + }, project == null ? Conditions.alwaysFalse() : project.getDisposed()); if (key instanceof byte[]) return (byte[])key; if (key instanceof PasswordSafeException) throw (PasswordSafeException)key; diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java index 57329fb8fe9d..e88a17416fc1 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java @@ -183,6 +183,7 @@ public class PluginManager extends PluginManagerCore { } else if (myPlugins2Enable != null && ENABLE.equals(description)) { disabledPlugins.removeAll(myPlugins2Enable); + PluginManagerMain.notifyPluginsWereUpdated("Changes were applied", null); } try { diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java index cc6e16aba6c2..9accbc5bc6c8 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java @@ -174,6 +174,9 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab update |= settings.SHOW_EDITOR_TOOLTIP != myComponent.myEditorTooltipCheckBox.isSelected(); settings.SHOW_EDITOR_TOOLTIP = myComponent.myEditorTooltipCheckBox.isSelected(); + update |= settings.NAVIGATE_TO_PREVIEW != myComponent.myNavigateToPreviewCheckBox.isSelected(); + settings.NAVIGATE_TO_PREVIEW = myComponent.myNavigateToPreviewCheckBox.isSelected(); + update |= settings.DISABLE_MNEMONICS_IN_CONTROLS != myComponent.myDisableMnemonicInControlsCheckBox.isSelected(); settings.DISABLE_MNEMONICS_IN_CONTROLS = myComponent.myDisableMnemonicInControlsCheckBox.isSelected(); @@ -280,6 +283,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab myComponent.myLeftLayoutCheckBox.setSelected(settings.LEFT_HORIZONTAL_SPLIT); myComponent.myRightLayoutCheckBox.setSelected(settings.RIGHT_HORIZONTAL_SPLIT); myComponent.myEditorTooltipCheckBox.setSelected(settings.SHOW_EDITOR_TOOLTIP); + myComponent.myNavigateToPreviewCheckBox.setSelected(settings.NAVIGATE_TO_PREVIEW); myComponent.myDisableMnemonicInControlsCheckBox.setSelected(settings.DISABLE_MNEMONICS_IN_CONTROLS); boolean alphaModeEnabled = WindowManagerEx.getInstanceEx().isAlphaModeSupported(); @@ -325,6 +329,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab isModified |= myComponent.myLeftLayoutCheckBox.isSelected() != settings.LEFT_HORIZONTAL_SPLIT; isModified |= myComponent.myRightLayoutCheckBox.isSelected() != settings.RIGHT_HORIZONTAL_SPLIT; isModified |= myComponent.myEditorTooltipCheckBox.isSelected() != settings.SHOW_EDITOR_TOOLTIP; + isModified |= myComponent.myNavigateToPreviewCheckBox.isSelected() != settings.NAVIGATE_TO_PREVIEW; isModified |= myComponent.myHideIconsInQuickNavigation.isSelected() != settings.SHOW_ICONS_IN_QUICK_NAVIGATION; @@ -397,6 +402,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab private JSlider myInitialTooltipDelaySlider; private ComboBox myPresentationModeFontSize; private JCheckBox myEditorTooltipCheckBox; + private JCheckBox myNavigateToPreviewCheckBox; private JCheckBox myAllowStatusBar; private JCheckBox myAllowLineNumbers; private JCheckBox myAllowAnnotations; diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form index 550e8e2482e1..7590feba2ad8 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form @@ -401,6 +401,14 @@ <text resource-bundle="messages/IdeBundle" key="checkbox.show.editor.preview.popup"/> </properties> </component> + <component id="3e39" class="javax.swing.JCheckBox" binding="myNavigateToPreviewCheckBox" default-binding="true"> + <constraints> + <grid row="6" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text resource-bundle="messages/IdeBundle" key="checkbox.use.preview.window"/> + </properties> + </component> </children> </grid> <grid id="e3cc0" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> 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 1036307e97d7..7e00dc3a9841 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 @@ -87,9 +87,8 @@ import java.util.List; */ @State( name = "LafManager", - roamingType = RoamingType.PER_PLATFORM, - storages = {@Storage( - file = StoragePathMacros.APP_CONFIG + "/options.xml")}) + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/options.xml", roamingType = RoamingType.PER_PLATFORM)} +) public final class LafManagerImpl extends LafManager implements ApplicationComponent, PersistentStateComponent<Element> { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.ui.LafManager"); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf.properties b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf.properties index f721abfdb92b..ef0a4214db11 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf.properties +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf.properties @@ -118,6 +118,7 @@ CheckBox.darcula.focused.backgroundColor2.selected=3B98FB ComboBoxUI=com.intellij.ide.ui.laf.darcula.ui.DarculaComboBoxUI ComboBox.disabledBackground=e8e8e8 +ComboBox.disabledForeground=777777 ComboBox.squareButton=false ComboBox.darcula.arrowFillColor=6e9bd5 ComboBox.darcula.arrowFocusedFillColor=2d82ed diff --git a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java index 8febc9ad392a..a0df30abae1c 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java @@ -41,8 +41,6 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.*; @@ -314,18 +312,6 @@ public class SearchUtil { } } - public static Runnable lightOptions(final SearchableConfigurable configurable, - final JComponent component, - final String option, - final GlassPanel glassPanel, - final boolean forceSelect) { - return new Runnable() { - public void run() { - traverseComponentsTree(configurable, glassPanel, component, option, forceSelect); - } - }; - } - public static String markup(@NonNls @NotNull String textToMarkup, @Nullable String filter) { if (filter == null || filter.length() == 0) { return textToMarkup; @@ -575,80 +561,6 @@ public class SearchUtil { return null; } - public static void showHintPopup(final ConfigurableSearchTextField searchField, - final JBPopup[] activePopup, - final Alarm showHintAlarm, - final Consumer<String> selectConfigurable, - final Project project) { - for (JBPopup aPopup : activePopup) { - if (aPopup != null) { - aPopup.cancel(); - } - } - - final JBPopup popup = createPopup(searchField, activePopup, showHintAlarm, selectConfigurable, project, 0); //no selection - if (popup != null) { - popup.showUnderneathOf(searchField); - searchField.requestFocusInWindow(); - } - - activePopup[0] = popup; - activePopup[1] = null; - } - - - public static void registerKeyboardNavigation(final ConfigurableSearchTextField searchField, - final JBPopup[] activePopup, - final Alarm showHintAlarm, - final Consumer<String> selectConfigurable, - final Project project) { - final Consumer<Integer> shower = new Consumer<Integer>() { - public void consume(final Integer direction) { - if (activePopup[0] != null) { - activePopup[0].cancel(); - } - - if (activePopup[1] != null && activePopup[1].isVisible()) { - return; - } - - final JBPopup popup = createPopup(searchField, activePopup, showHintAlarm, selectConfigurable, project, direction.intValue()); - if (popup != null) { - popup.showUnderneathOf(searchField); - } - activePopup[0] = null; - activePopup[1] = popup; - } - }; - searchField.registerKeyboardAction(new ActionListener() { - public void actionPerformed(ActionEvent e) { - shower.consume(1); - } - }, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); - searchField.registerKeyboardAction(new ActionListener() { - public void actionPerformed(ActionEvent e) { - shower.consume(-1); - } - }, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); - - searchField.addKeyboardListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ESCAPE && searchField.getText().length() > 0) { - e.consume(); - if (cancelPopups(activePopup)) return; - searchField.setText(""); - } - else if (e.getKeyCode() == KeyEvent.VK_ENTER) { - searchField.addCurrentTextToHistory(); - cancelPopups(activePopup); - if (e.getModifiers() == 0) { - e.consume(); - } - } - } - }); - } - private static boolean cancelPopups(final JBPopup[] activePopup) { for (JBPopup popup : activePopup) { if (popup != null && popup.isVisible()) { diff --git a/platform/platform-impl/src/com/intellij/ide/util/ProjectPropertiesComponentImpl.java b/platform/platform-impl/src/com/intellij/ide/util/ProjectPropertiesComponentImpl.java index e44921e6d5df..4b11b44b2bc9 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/ProjectPropertiesComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/util/ProjectPropertiesComponentImpl.java @@ -15,15 +15,13 @@ */ package com.intellij.ide.util; -import com.intellij.openapi.components.RoamingType; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.components.StoragePathMacros; @State( - name = "PropertiesComponent", - roamingType = RoamingType.DISABLED, - storages = {@Storage( - file = StoragePathMacros.WORKSPACE_FILE)}) + name = "PropertiesComponent", + storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)} +) public class ProjectPropertiesComponentImpl extends PropertiesComponentImpl { } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java b/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java index 40282a89eded..99e62b9da292 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java @@ -34,7 +34,10 @@ import java.util.Set; /** * User: ksafonov */ -@State(name = "UsageTrigger", roamingType = RoamingType.DISABLED, storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/statistics.application.usages.xml")}) +@State( + name = "UsageTrigger", + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/statistics.application.usages.xml", roamingType = RoamingType.DISABLED)} +) public class UsageTrigger implements PersistentStateComponent<UsageTrigger.State> { public static class State { @@ -72,7 +75,7 @@ public class UsageTrigger implements PersistentStateComponent<UsageTrigger.State myState = state; } - + public static class MyCollector extends UsagesCollector { private static final GroupDescriptor GROUP = GroupDescriptor.create("features counts", GroupDescriptor.HIGHER_PRIORITY); @@ -92,5 +95,5 @@ public class UsageTrigger implements PersistentStateComponent<UsageTrigger.State return GROUP; } } - + } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java index becc1d3699a0..3acca5fffe96 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java @@ -43,11 +43,7 @@ import java.util.Set; @State( name = "StatisticsApplicationUsages", - roamingType = RoamingType.DISABLED, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/statistics.application.usages.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/statistics.application.usages.xml", roamingType = RoamingType.DISABLED)} ) public class ApplicationStatisticsPersistenceComponent extends ApplicationStatisticsPersistence implements ApplicationComponent, PersistentStateComponent<Element> { diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/UsageStatisticsPersistenceComponent.java b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/UsageStatisticsPersistenceComponent.java index 65298387a902..d60be232c9ea 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/UsageStatisticsPersistenceComponent.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/UsageStatisticsPersistenceComponent.java @@ -35,11 +35,7 @@ import java.util.Set; @State( name = "UsagesStatistic", - roamingType = RoamingType.DISABLED, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/usage.statistics.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/usage.statistics.xml", roamingType = RoamingType.DISABLED)} ) public class UsageStatisticsPersistenceComponent extends BasicSentUsagesPersistenceComponent implements NamedComponent, PersistentStateComponent<Element> { 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 2d6a768a92c2..50ca0941f621 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 @@ -125,6 +125,7 @@ public class NotificationsConfigurablePanel extends JPanel implements Disposable public NotificationsTable() { super(new NotificationsTableModel()); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); final TableColumn idColumn = getColumnModel().getColumn(ID_COLUMN); idColumn.setPreferredWidth(200); @@ -151,7 +152,7 @@ public class NotificationsConfigurablePanel extends JPanel implements Disposable displayTypeColumn.setCellRenderer(new ComboBoxTableRenderer<NotificationDisplayType>(NotificationDisplayType.values()) { @Override protected void customizeComponent(NotificationDisplayType value, JTable table, boolean isSelected) { - super.customizeComponent(value, table, isSelected); + super.customizeComponent(myDisplayBalloons.isSelected() ? value : NotificationDisplayType.NONE, table, isSelected); if (!myDisplayBalloons.isSelected() && !isSelected) { setBackground(UIUtil.getComboBoxDisabledBackground()); setForeground(UIUtil.getComboBoxDisabledForeground()); diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ComputableActionGroup.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ComputableActionGroup.java index b5027fd9df81..3b0e247180b2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ComputableActionGroup.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ComputableActionGroup.java @@ -55,6 +55,13 @@ public abstract class ComputableActionGroup extends ActionGroup implements DumbA protected abstract CachedValueProvider<AnAction[]> createChildrenProvider(@NotNull ActionManager actionManager); public abstract static class Simple extends ComputableActionGroup { + protected Simple() { + } + + protected Simple(boolean popup) { + super(popup); + } + @NotNull @Override protected final CachedValueProvider<AnAction[]> createChildrenProvider(@NotNull final ActionManager actionManager) { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java index bf7c79695c94..eb9436481767 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java @@ -70,10 +70,10 @@ public class QuickListsManager implements ExportableApplicationComponent, NamedJ return loadListFromDocument(schemeContent); } - public Document writeScheme(@NotNull final QuickList scheme) throws WriteExternalException { + public Element writeScheme(@NotNull final QuickList scheme) throws WriteExternalException { Element element = new Element(LIST_TAG); scheme.writeExternal(element); - return new Document(element); + return element; } public boolean shouldBeSaved(@NotNull final QuickList scheme) { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java index 0d116c7a927f..fd96e54fc98e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java @@ -31,11 +31,7 @@ import java.util.*; */ @State( name = "AbbreviationManager", - roamingType = RoamingType.PER_PLATFORM, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/abbreviations.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/abbreviations.xml", roamingType = RoamingType.PER_PLATFORM)} ) public class AbbreviationManagerImpl extends AbbreviationManager implements ExportableComponent, PersistentStateComponent<Element> { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java index c5a9b01c4d21..32e823bd9758 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java @@ -70,6 +70,7 @@ import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; +import java.util.concurrent.Future; public final class ActionManagerImpl extends ActionManagerEx implements ApplicationComponent { @NonNls public static final String ACTION_ELEMENT_NAME = "action"; @@ -360,7 +361,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } private static void assertActionIsGroupOrStub(final AnAction action) { - if (!(action instanceof ActionGroup || action instanceof ActionStub)) { + if (!(action instanceof ActionGroup || action instanceof ActionStub || action instanceof ChameleonAction)) { LOG.error("Action : " + action + "; class: " + action.getClass()); } } @@ -520,11 +521,9 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat LOG.assertTrue(action.equals(stub)); AnAction anAction = convertStub(stub); - - addToMap(stub.getId(), anAction, stub.getPluginId(), stub.getProjectType() == null ? null : new ProjectType(stub.getProjectType())); myAction2Id.put(anAction, stub.getId()); - return anAction; + return addToMap(stub.getId(), anAction, stub.getPluginId(), stub.getProjectType()); } @Override @@ -653,7 +652,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } else { - registerAction(id, action, pluginId); + registerAction(id, action, pluginId, element.getAttributeValue(PROJECT_TYPE)); } } } @@ -991,8 +990,12 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @Override public void registerAction(@NotNull String actionId, @NotNull AnAction action, @Nullable PluginId pluginId) { + registerAction(actionId, action, pluginId, null); + } + + public void registerAction(@NotNull String actionId, @NotNull AnAction action, @Nullable PluginId pluginId, @Nullable String projectType) { synchronized (myLock) { - if (!addToMap(actionId, action, pluginId, null)) return; + if (addToMap(actionId, action, pluginId, projectType) == null) return; if (myAction2Id.containsKey(action)) { reportActionError(pluginId, "action was already registered for another ID. ID is " + myAction2Id.get(action) + getPluginInfo(pluginId)); @@ -1012,31 +1015,42 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - public boolean addToMap(String actionId, AnAction action, PluginId pluginId, ProjectType projectType) { - if (myId2Action.containsKey(actionId)) { - // make sure id+projectType is unique - AnAction o = myId2Action.get(actionId); - ChameleonAction chameleonAction; - if (o instanceof ChameleonAction) { - chameleonAction = (ChameleonAction)o; - } - else { - chameleonAction = new ChameleonAction(o, projectType); - myId2Action.put(actionId, chameleonAction); - } - AnAction old = chameleonAction.addAction(action, projectType); - if (old != null) { - reportActionError(pluginId, - "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action + - "; Registered action is " + - myId2Action.get(actionId) + getPluginInfo(pluginId)); - return false; - } + private AnAction addToMap(String actionId, AnAction action, PluginId pluginId, String projectType) { + if (projectType != null || myId2Action.containsKey(actionId)) { + return registerChameleon(actionId, action, pluginId, projectType); } else { myId2Action.put(actionId, action); + return action; } - return true; + } + + private AnAction registerChameleon(String actionId, AnAction action, PluginId pluginId, String projectType) { + ProjectType type = projectType == null ? null : new ProjectType(projectType); + // make sure id+projectType is unique + AnAction o = myId2Action.get(actionId); + ChameleonAction chameleonAction; + if (o == null) { + chameleonAction = new ChameleonAction(action, type); + myId2Action.put(actionId, chameleonAction); + return chameleonAction; + } + if (o instanceof ChameleonAction) { + chameleonAction = (ChameleonAction)o; + } + else { + chameleonAction = new ChameleonAction(o, type); + myId2Action.put(actionId, chameleonAction); + } + AnAction old = chameleonAction.addAction(action, type); + if (old != null) { + reportActionError(pluginId, + "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action + + "; Registered action is " + + myId2Action.get(actionId) + getPluginInfo(pluginId)); + return null; + } + return chameleonAction; } @Override @@ -1053,7 +1067,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return; } } - AnAction oldValue = (AnAction)myId2Action.remove(actionId); + AnAction oldValue = myId2Action.remove(actionId); myAction2Id.remove(oldValue); myId2Index.remove(actionId); for (PluginId pluginName : myPlugin2Id.keySet()) { @@ -1251,7 +1265,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - public void preloadActions() { + public Future<?> preloadActions() { if (myPreloadActionsRunnable == null) { myPreloadActionsRunnable = new Runnable() { @Override @@ -1262,8 +1276,9 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } }; - ApplicationManager.getApplication().executeOnPooledThread(myPreloadActionsRunnable); + return ApplicationManager.getApplication().executeOnPooledThread(myPreloadActionsRunnable); } + return null; } private void doPreloadActions() { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java index 4b46dc191c0c..f4e92f7eae02 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java @@ -15,14 +15,13 @@ */ package com.intellij.openapi.actionSystem.impl; -import com.intellij.openapi.actionSystem.ActionStub; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectType; import com.intellij.openapi.project.ProjectTypeService; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import java.util.HashMap; import java.util.Map; @@ -36,6 +35,7 @@ public class ChameleonAction extends AnAction { public ChameleonAction(@NotNull AnAction first, ProjectType projectType) { addAction(first, projectType); + copyFrom(myActions.values().iterator().next()); } public AnAction addAction(AnAction action, ProjectType projectType) { @@ -48,21 +48,35 @@ public class ChameleonAction extends AnAction { } @Override - public void actionPerformed(AnActionEvent e) { - getAction(e).actionPerformed(e); + public void actionPerformed(@NotNull AnActionEvent e) { + AnAction action = getAction(e); + assert action != null; + action.actionPerformed(e); } @Override - public void update(AnActionEvent e) { - super.update(e); + public void update(@NotNull AnActionEvent e) { + AnAction action = getAction(e); + if (action != null) { + e.getPresentation().setVisible(true); + action.update(e); + } + else { + e.getPresentation().setVisible(false); + } } + @Nullable private AnAction getAction(AnActionEvent e) { Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); ProjectType projectType = ProjectTypeService.getProjectType(project); AnAction action = myActions.get(projectType); if (action == null) action = myActions.get(null); - if (action == null) action = myActions.values().iterator().next(); return action; } + + @TestOnly + public Map<ProjectType, AnAction> getActions() { + return myActions; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/Utils.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/Utils.java index 85c0555f9bc6..9af1d30c28bf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/Utils.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/Utils.java @@ -26,6 +26,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; 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.IdeFocusManager; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; @@ -164,7 +165,7 @@ public class Utils{ } } else if (child instanceof Separator) { - if (!list.isEmpty() && !(list.get(list.size() - 1) instanceof Separator)) { + if (!StringUtil.isEmpty(((Separator)child).getText()) || (!list.isEmpty() && !(list.get(list.size() - 1) instanceof Separator))) { list.add(child); } } @@ -284,8 +285,10 @@ public class Utils{ for (int i = 0, size = list.size(); i < size; i++) { final AnAction action = list.get(i); if (action instanceof Separator) { - if (i > 0 && i < size - 1) { + final String text = ((Separator)action).getText(); + if (!StringUtil.isEmpty(text) || (i > 0 && i < size - 1)) { component.add(new JPopupMenu.Separator() { + private final JMenuItem myMenu = !StringUtil.isEmpty(text) ? new JMenuItem(text) : null; @Override public Insets getInsets() { final Insets insets = super.getInsets(); @@ -296,12 +299,29 @@ public class Utils{ } @Override + public void doLayout() { + super.doLayout(); + if (myMenu != null) { + myMenu.setBounds(getBounds()); + } + } + + @Override protected void paintComponent(Graphics g) { if (UIUtil.isUnderWindowsClassicLookAndFeel() || UIUtil.isUnderDarcula() || UIUtil.isUnderWindowsLookAndFeel()) { g.setColor(component.getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); } - super.paintComponent(g); + if (myMenu != null) { + myMenu.paint(g); + } else { + super.paintComponent(g); + } + } + + @Override + public Dimension getPreferredSize() { + return myMenu != null ? myMenu.getPreferredSize() : super.getPreferredSize(); } }); } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/PluginPathManager.java b/platform/platform-impl/src/com/intellij/openapi/application/PluginPathManager.java index 1ab050053e28..9bad9d2d3774 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/PluginPathManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/PluginPathManager.java @@ -16,8 +16,10 @@ package com.intellij.openapi.application; import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.NotNull; import java.io.File; +import java.io.FileFilter; import java.util.*; /** @@ -32,26 +34,38 @@ public class PluginPathManager { private static List<File> findSubrepos() { List<File> result = new ArrayList<File>(); - File[] subdirs = new File(PathManager.getHomePath()).listFiles(); - if (subdirs == null) return result; - Arrays.sort(subdirs, new Comparator<File>() { + File[] gitRoots = getSortedGitRoots(new File(PathManager.getHomePath())); + for (File subdir : gitRoots) { + File pluginsDir = new File(subdir, "plugins"); + if (pluginsDir.exists()) { + result.add(pluginsDir); + } + else { + result.add(subdir); + } + result.addAll(Arrays.asList(getSortedGitRoots(subdir))); + } + return result; + } + + @NotNull + private static File[] getSortedGitRoots(@NotNull File dir) { + File[] gitRoots = dir.listFiles(new FileFilter() { + @Override + public boolean accept(File child) { + return child.isDirectory() && new File(child, ".git").exists(); + } + }); + if (gitRoots == null) { + return new File[0]; + } + Arrays.sort(gitRoots, new Comparator<File>() { @Override public int compare(File file, File file2) { return FileUtil.compareFiles(file, file2); } }); - for (File subdir : subdirs) { - if (new File(subdir, ".git").exists()) { - File pluginsDir = new File(subdir, "plugins"); - if (pluginsDir.exists()) { - result.add(pluginsDir); - } - else { - result.add(subdir); - } - } - } - return result; + return gitRoots; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index e3a36a2e59ab..a8eca00b505e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -531,10 +531,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App @Override public void load(@Nullable String optionsPath) throws IOException { - load(PathManager.getConfigPath(), optionsPath); + load(PathManager.getConfigPath(), optionsPath == null ? PathManager.getOptionsPath() : optionsPath); } - public void load(@NotNull String configPath, @Nullable String optionsPath) throws IOException { + public void load(@NotNull String configPath, @NotNull String optionsPath) throws IOException { getStateStore().setOptionsPath(optionsPath); getStateStore().setConfigPath(configPath); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java index a00533d3244b..bf03cb11f1d6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java @@ -36,7 +36,6 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto private static final String XML_EXTENSION = ".xml"; private static final String DEFAULT_STORAGE_SPEC = StoragePathMacros.APP_CONFIG + "/" + PathManager.DEFAULT_OPTIONS_FILE_NAME + XML_EXTENSION; - private static final String OPTIONS_MACRO = "OPTIONS"; private static final String ROOT_ELEMENT_NAME = "application"; private final ApplicationImpl myApplication; @@ -93,14 +92,13 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto } @Override - public void setOptionsPath(final String path) { - myStateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.APP_CONFIG), path); - myStateStorageManager.addMacro(OPTIONS_MACRO, path); + public void setOptionsPath(@NotNull String path) { + myStateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, path); } @Override public void setConfigPath(@NotNull final String configPath) { - myStateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.ROOT_CONFIG), configPath); + myStateStorageManager.addMacro(StoragePathMacros.ROOT_CONFIG, configPath); myConfigPath = configPath; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java index 25dd8a05cab3..dc07fee151e2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java @@ -59,8 +59,8 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { } @Override - public void load(@NotNull final Element rootElement) throws IOException { - super.load(rootElement); + public void load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { + super.load(rootElement, pathMacroSubstitutor, intern); final String v = rootElement.getAttributeValue(VERSION_OPTION); if (v != null) { @@ -74,7 +74,11 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { @Override @NotNull protected Element save() { - final Element root = super.save(); + Element root = super.save(); + if (root == null) { + root = new Element(myRootElementName); + } + root.setAttribute(VERSION_OPTION, Integer.toString(myVersion)); return root; } @@ -113,7 +117,7 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { } public BaseStorageData getMainStorageData() throws StateStorageException { - return (BaseStorageData) getMainStorage().getStorageData(false); + return (BaseStorageData)getMainStorage().getStorageData(); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java index fc2017b57a4a..ed63bf12d4c4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java @@ -29,12 +29,12 @@ import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.ReflectionUtil; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; import java.lang.reflect.Type; import java.util.*; @@ -46,12 +46,6 @@ public abstract class ComponentStoreImpl implements IComponentStore { private final List<SettingsSavingComponent> mySettingsSavingComponents = Collections.synchronizedList(new ArrayList<SettingsSavingComponent>()); @Nullable private SaveSessionImpl mySession; - @Deprecated - @Nullable - private StateStorage getStateStorage(@NotNull final Storage storageSpec) throws StateStorageException { - return getStateStorageManager().getStateStorage(storageSpec); - } - @Nullable protected abstract StateStorage getDefaultsStorage(); @@ -255,9 +249,10 @@ public abstract class ComponentStoreImpl implements IComponentStore { Storage[] storageSpecs = getComponentStorageSpecs(component, StateStorageOperation.READ); for (Storage storageSpec : storageSpecs) { - StateStorage stateStorage = getStateStorage(storageSpec); - if (stateStorage == null || !stateStorage.hasState(component, name, stateClass, reloadData)) continue; - state = stateStorage.getState(component, name, stateClass, state); + StateStorage stateStorage = getStateStorageManager().getStateStorage(storageSpec); + if (stateStorage != null && stateStorage.hasState(component, name, stateClass, reloadData)) { + state = stateStorage.getState(component, name, stateClass, state); + } } if (state != null) { @@ -320,17 +315,15 @@ public abstract class ComponentStoreImpl implements IComponentStore { } assert storages.length > 0; - final Class<StorageAnnotationsDefaultValues.NullStateStorageChooser> defaultClass = - StorageAnnotationsDefaultValues.NullStateStorageChooser.class; - final Class<? extends StateStorageChooser> storageChooserClass = stateSpec.storageChooser(); - final StateStorageChooser<PersistentStateComponent<?>> defaultStateStorageChooser = getDefaultStateStorageChooser(); - assert storageChooserClass != defaultClass || defaultStateStorageChooser != null : "State chooser not specified for: " + - persistentStateComponent.getClass(); - - if (storageChooserClass == defaultClass) { + if (storageChooserClass == StateStorageChooser.class) { + StateStorageChooser<PersistentStateComponent<?>> defaultStateStorageChooser = getDefaultStateStorageChooser(); + assert defaultStateStorageChooser != null : "State chooser not specified for: " + persistentStateComponent.getClass(); return defaultStateStorageChooser.selectStorages(storages, persistentStateComponent, operation); } + else if (storageChooserClass == LastStorageChooserForWrite.class) { + return LastStorageChooserForWrite.INSTANCE.selectStorages(storages, persistentStateComponent, operation); + } else { try { @SuppressWarnings("unchecked") @@ -361,7 +354,7 @@ public abstract class ComponentStoreImpl implements IComponentStore { @NotNull @Override - public List<IFile> getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException { + public List<File> getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException { try { return myStorageManagerSaveSession.getAllStorageFilesToSave(); } @@ -426,12 +419,10 @@ public abstract class ComponentStoreImpl implements IComponentStore { protected void commit() throws StateStorageException { final StateStorageManager storageManager = getStateStorageManager(); - final StateStorageManager.ExternalizationSession session = storageManager.startExternalization(); String[] names = ArrayUtil.toStringArray(myComponents.keySet()); Arrays.sort(names); - for (String name : names) { Object component = myComponents.get(name); if (component instanceof PersistentStateComponent) { @@ -452,10 +443,9 @@ public abstract class ComponentStoreImpl implements IComponentStore { @NotNull @Override - public List<IFile> getAllStorageFiles(final boolean includingSubStructures) { + public List<File> getAllStorageFiles(final boolean includingSubStructures) { return myStorageManagerSaveSession.getAllStorageFiles(); } - } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/CompoundSaveSession.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/CompoundSaveSession.java index 51cc717726f3..aacdb58aeeec 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/CompoundSaveSession.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/CompoundSaveSession.java @@ -18,9 +18,9 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.util.SmartList; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; +import java.io.File; import java.util.List; import java.util.Map; @@ -36,8 +36,8 @@ public class CompoundSaveSession { } } - public List<IFile> getAllStorageFilesToSave() throws StateStorageException { - List<IFile> result = new SmartList<IFile>(); + public List<File> getAllStorageFilesToSave() throws StateStorageException { + List<File> result = new SmartList<File>(); for (StateStorage.SaveSession saveSession : mySaveSessions.values()) { result.addAll(saveSession.getStorageFilesToSave()); } @@ -70,8 +70,8 @@ public class CompoundSaveSession { return mySaveSessions.get(storage); } - public List<IFile> getAllStorageFiles() { - List<IFile> result = new SmartList<IFile>(); + public List<File> getAllStorageFiles() { + List<File> result = new SmartList<File>(); for (StateStorage.SaveSession saveSession : mySaveSessions.values()) { result.addAll(saveSession.getAllStorageFiles()); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java index 6f0c3b8122cf..fad17c8d2889 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java @@ -21,13 +21,12 @@ import com.intellij.openapi.project.impl.ProjectImpl; import com.intellij.openapi.project.impl.ProjectManagerImpl; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; -import org.jdom.Document; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Collections; @@ -40,10 +39,10 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { private final ProjectManagerImpl myProjectManager; @NonNls private static final String ROOT_TAG_NAME = "defaultProject"; - public DefaultProjectStoreImpl(final ProjectImpl project, final ProjectManagerImpl projectManager) { + public DefaultProjectStoreImpl(@NotNull ProjectImpl project, final ProjectManagerImpl projectManager) { super(project); - myProjectManager = projectManager; + myProjectManager = projectManager; myElement = projectManager.getDefaultProjectRootElement(); } @@ -53,29 +52,28 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { return element != null ? element.clone() : null; } - @NotNull @Override protected StateStorageManager createStateStorageManager() { - Document _d = null; + Element _d = null; if (myElement != null) { myElement.detach(); - _d = new Document(myElement); + _d = myElement; } final ComponentManager componentManager = getComponentManager(); final PathMacroManager pathMacroManager = PathMacroManager.getInstance(componentManager); - final Document document = _d; + final Element element = _d; - final XmlElementStorage storage = new XmlElementStorage(pathMacroManager.createTrackingSubstitutor(), componentManager, - ROOT_TAG_NAME, null, "", ComponentRoamingManager.getInstance(), + final XmlElementStorage storage = new XmlElementStorage("", RoamingType.DISABLED, pathMacroManager.createTrackingSubstitutor(), componentManager, + ROOT_TAG_NAME, null, ComponentVersionProvider.EMPTY) { @Override @Nullable - protected Document loadDocument() throws StateStorageException { - return document; + protected Element loadLocalData() { + return element; } @Override @@ -96,26 +94,28 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { @Override protected void doSave() throws StateStorageException { - myProjectManager.setDefaultProjectRootElement(getDocumentToSave().getRootElement()); + Element element = getElementToSave(); + myProjectManager.setDefaultProjectRootElement(element == null ? null : element); } @NotNull @Override - public Collection<IFile> getStorageFilesToSave() throws StateStorageException { + public Collection<File> getStorageFilesToSave() throws StateStorageException { return Collections.emptyList(); } @NotNull @Override - public List<IFile> getAllStorageFiles() { + public List<File> getAllStorageFiles() { return Collections.emptyList(); } } }; + //noinspection deprecation return new StateStorageManager() { @Override - public void addMacro(String macro, String expansion) { + public void addMacro(@NotNull String macro, @NotNull String expansion) { throw new UnsupportedOperationException("Method addMacro not implemented in " + getClass()); } @@ -131,6 +131,12 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { return storage; } + @Nullable + @Override + public StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType) { + return storage; + } + @Override @Nullable public StateStorage getFileStateStorage(@NotNull String fileSpec) { @@ -158,6 +164,7 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { storage.finishSave(((MySaveSession)saveSession).saveSession); } + @NotNull @Override public String expandMacros(@NotNull String file) { throw new UnsupportedOperationException("Method expandMacros not implemented in " + getClass()); @@ -186,6 +193,7 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { throw new UnsupportedOperationException("Method getStreamProviders not implemented in " + getClass()); } + @NotNull @Override public Collection<String> getStorageFileNames() { throw new UnsupportedOperationException("Method getStorageFileNames not implemented in " + getClass()); @@ -238,13 +246,13 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { @NotNull @Override - public List<IFile> getAllStorageFilesToSave() throws StateStorageException { + public List<File> getAllStorageFilesToSave() throws StateStorageException { return Collections.emptyList(); } @NotNull @Override - public List<IFile> getAllStorageFiles() { + public List<File> getAllStorageFiles() { return Collections.emptyList(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java index b62db63b73c2..9415aee49b09 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java @@ -65,12 +65,12 @@ class DefaultsStateStorage implements StateStorage { } @Nullable - public <T> T getState(final Object component, final String componentName, final Class<T> stateClass, @Nullable final T mergeInto) throws + public <T> T getState(final Object component, @NotNull final String componentName, final Class<T> stateClass, @Nullable final T mergeInto) throws StateStorageException { return DefaultStateSerializer.deserializeState(getState(component, componentName), stateClass, mergeInto); } - public boolean hasState(final Object component, final String componentName, final Class<?> aClass, final boolean reloadData) throws StateStorageException { + public boolean hasState(final Object component, @NotNull final String componentName, final Class<?> aClass, final boolean reloadData) throws StateStorageException { final URL url = DecodeDefaultsUtil.getDefaults(component, componentName); return url != null; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java index cda453be5cd2..912c239cffcb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java @@ -26,7 +26,8 @@ import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; -import com.intellij.util.io.fs.IFile; +import com.intellij.util.SmartList; +import com.intellij.util.containers.SmartHashSet; import com.intellij.util.messages.MessageBus; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -37,16 +38,13 @@ import java.io.File; import java.io.IOException; import java.util.*; -import static com.intellij.util.io.fs.FileSystem.FILE_SYSTEM; - //todo: support missing plugins //todo: support storage data public class DirectoryBasedStorage implements StateStorage, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.DirectoryBasedStorage"); - private static final IFile[] EMPTY_FILES = new IFile[0]; private final TrackingPathMacroSubstitutor myPathMacroSubstitutor; - private final IFile myDir; + private final File myDir; private final StateSplitter mySplitter; private final FileTypeManager myFileTypeManager; @@ -59,7 +57,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @NotNull Disposable parentDisposable, @NotNull PicoContainer picoContainer) { myPathMacroSubstitutor = pathMacroSubstitutor; - myDir = FILE_SYSTEM.createFile(dir); + myDir = new File(dir); mySplitter = splitter; Disposer.register(parentDisposable, this); @@ -96,7 +94,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override @Nullable - public <T> T getState(final Object component, final String componentName, Class<T> stateClass, @Nullable T mergeInto) + public <T> T getState(final Object component, @NotNull final String componentName, Class<T> stateClass, @Nullable T mergeInto) throws StateStorageException { if (myStorageData == null) myStorageData = loadState(); @@ -116,7 +114,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override - public boolean hasState(final Object component, final String componentName, final Class<?> aClass, final boolean reloadData) throws StateStorageException { + public boolean hasState(final Object component, @NotNull String componentName, final Class<?> aClass, final boolean reloadData) throws StateStorageException { if (!myDir.exists()) return false; if (reloadData) myStorageData = null; return true; @@ -180,32 +178,28 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override public void save() throws StateStorageException { assert mySession == this; - final Set<String> currentNames = new HashSet<String>(); - - IFile[] children = myDir.exists() ? myDir.listFiles() : EMPTY_FILES; - for (IFile child : children) { - final String fileName = child.getName(); - if (!myFileTypeManager.isFileIgnored(fileName) && StringUtil.endsWithIgnoreCase(fileName, ".xml")) { - currentNames.add(fileName); + final Set<String> currentNames = new SmartHashSet<String>(); + File[] children = myDir.listFiles(); + if (children != null) { + for (File child : children) { + final String fileName = child.getName(); + if (!myFileTypeManager.isFileIgnored(fileName) && StringUtil.endsWithIgnoreCase(fileName, ".xml")) { + currentNames.add(fileName); + } } } myStorageData.process(new DirectoryStorageData.StorageDataProcessor() { @Override - public void process(final String componentName, final IFile file, final Element element) { + public void process(final String componentName, final File file, final Element element) { currentNames.remove(file.getName()); if (myPathMacroSubstitutor != null) { myPathMacroSubstitutor.collapsePaths(element); } - if (file.getTimeStamp() <= myStorageData.getLastTimeStamp()) { - if (!myDir.exists()) { - myDir.createParentDirs(); - myDir.mkDir(); - } - - StorageUtil.save(file, element, MySaveSession.this); + if (file.lastModified() <= myStorageData.getLastTimeStamp()) { + StorageUtil.save(file, element, MySaveSession.this, false, null); myStorageData.updateLastTimestamp(file); } } @@ -217,15 +211,14 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { public void run() { if (myDir.exists()) { for (String name : currentNames) { - IFile child = myDir.getChild(name); - - if (child.getTimeStamp() > myStorageData.getLastTimeStamp()) { + File child = new File(myDir, name); + if (child.lastModified() > myStorageData.getLastTimeStamp()) { // do not touch new files during VC update (which aren't read yet) // now got an opposite problem: file is recreated if was removed by VC during update. return; } - final VirtualFile virtualFile = StorageUtil.getVirtualFile(child); + final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(child); if (virtualFile != null) { try { LOG.debug("Removing configuration file: " + virtualFile.getPresentableUrl()); @@ -272,23 +265,26 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override @NotNull - public Collection<IFile> getStorageFilesToSave() throws StateStorageException { + public Collection<File> getStorageFilesToSave() throws StateStorageException { assert mySession == this; if (!myDir.exists()) return getAllStorageFiles(); assert myDir.isDirectory() : myDir.getPath(); - final List<IFile> filesToSave = new ArrayList<IFile>(); - - IFile[] children = myDir.listFiles(); - final Set<String> currentChildNames = new HashSet<String>(); - for (IFile child : children) { - if (!myFileTypeManager.isFileIgnored(child.getName())) currentChildNames.add(child.getName()); + final List<File> filesToSave = new ArrayList<File>(); + final Set<String> currentChildNames = new SmartHashSet<String>(); + File[] children = myDir.listFiles(); + if (children != null) { + for (File child : children) { + if (!myFileTypeManager.isFileIgnored(child.getName())) { + currentChildNames.add(child.getName()); + } + } } myStorageData.process(new DirectoryStorageData.StorageDataProcessor() { @Override - public void process(final String componentName, final IFile file, final Element element) { + public void process(final String componentName, final File file, final Element element) { if (currentChildNames.contains(file.getName())) { currentChildNames.remove(file.getName()); @@ -301,13 +297,11 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { filesToSave.add(file); } } - } }); for (String childName : currentChildNames) { - final IFile child = myDir.getChild(childName); - filesToSave.add(child); + filesToSave.add(new File(myDir, childName)); } return filesToSave; @@ -315,8 +309,8 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override @NotNull - public List<IFile> getAllStorageFiles() { - return new ArrayList<IFile>(myStorageData.getAllStorageFiles().keySet()); + public List<File> getAllStorageFiles() { + return new SmartList<File>(myStorageData.getAllStorageFiles().keySet()); } } @@ -328,24 +322,25 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { } @Override - public void setState(@NotNull final Object component, final String componentName, @NotNull final Object state, final Storage storageSpec) - throws StateStorageException { + public void setState(@NotNull final Object component, final String componentName, @NotNull final Object state, final Storage storageSpec) { assert mySession == this; setState(componentName, state, storageSpec); } - private void setState(final String componentName, @NotNull Object state, final Storage storageSpec) throws StateStorageException { + private void setState(final String componentName, @NotNull Object state, final Storage storageSpec) { try { final Element element = DefaultStateSerializer.serializeState(state, storageSpec); - for (Pair<Element, String> pair : mySplitter.splitState(element)) { - Element e = pair.first; - String name = pair.second; + if (element != null) { + for (Pair<Element, String> pair : mySplitter.splitState(element)) { + Element e = pair.first; + String name = pair.second; - Element statePart = new Element(StorageData.COMPONENT); - statePart.setAttribute(StorageData.NAME, componentName); - statePart.addContent(e.detach()); + Element statePart = new Element(StorageData.COMPONENT); + statePart.setAttribute(StorageData.NAME, componentName); + statePart.addContent(e.detach()); - myStorageData.put(componentName, myDir.getChild(name), statePart, false); + myStorageData.put(componentName, new File(myDir, name), statePart, false); + } } } catch (WriteExternalException e) { diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java index 923072d4fb1d..54fd9d65cb92 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java @@ -22,19 +22,14 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.components.StateStorageException; -import com.intellij.openapi.components.StoragePathMacros; -import com.intellij.openapi.components.TrackingPathMacroSubstitutor; +import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; -import com.intellij.util.io.fs.FileSystem; -import com.intellij.util.io.fs.IFile; import com.intellij.util.messages.MessageBus; -import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; @@ -44,46 +39,40 @@ import org.picocontainer.PicoContainer; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.util.Collection; import java.util.Collections; import java.util.List; public class FileBasedStorage extends XmlElementStorage { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.FileBasedStorage"); + private static final Logger LOG = Logger.getInstance(FileBasedStorage.class); private static boolean ourConfigDirectoryRefreshed = false; private final String myFilePath; - private final IFile myFile; - private final String myRootElementName; + private final File myFile; private volatile VirtualFile myCachedVirtualFile; - public FileBasedStorage(@Nullable TrackingPathMacroSubstitutor pathMacroManager, - StreamProvider streamProvider, - String filePath, - String fileSpec, - String rootElementName, + public FileBasedStorage(@NotNull String filePath, + @NotNull String fileSpec, + @Nullable RoamingType roamingType, + @Nullable TrackingPathMacroSubstitutor pathMacroManager, + @NotNull String rootElementName, @NotNull Disposable parentDisposable, PicoContainer picoContainer, - ComponentRoamingManager componentRoamingManager, + @Nullable StreamProvider streamProvider, ComponentVersionProvider componentVersionProvider) { - super(pathMacroManager, parentDisposable, rootElementName, streamProvider, fileSpec, componentRoamingManager, componentVersionProvider); + super(fileSpec, roamingType, pathMacroManager, parentDisposable, rootElementName, streamProvider, componentVersionProvider); refreshConfigDirectoryOnce(); - myRootElementName = rootElementName; myFilePath = filePath; - myFile = FileSystem.FILE_SYSTEM.createFile(myFilePath); + myFile = new File(filePath); VirtualFileTracker virtualFileTracker = ServiceManager.getService(VirtualFileTracker.class); MessageBus messageBus = (MessageBus)picoContainer.getComponentInstanceOfType(MessageBus.class); if (virtualFileTracker != null && messageBus != null) { - final String path = myFile.getAbsolutePath(); - final String fileUrl = LocalFileSystem.PROTOCOL_PREFIX + path.replace(File.separatorChar, '/'); - final Listener listener = messageBus.syncPublisher(STORAGE_TOPIC); - virtualFileTracker.addTracker(fileUrl, new VirtualFileAdapter() { + virtualFileTracker.addTracker(LocalFileSystem.PROTOCOL_PREFIX + myFile.getAbsolutePath().replace(File.separatorChar, '/'), new VirtualFileAdapter() { @Override public void fileMoved(@NotNull VirtualFileMoveEvent event) { myCachedVirtualFile = null; @@ -95,6 +84,11 @@ public class FileBasedStorage extends XmlElementStorage { } @Override + public void fileCreated(@NotNull VirtualFileEvent event) { + myCachedVirtualFile = event.getFile(); + } + + @Override public void contentsChanged(@NotNull final VirtualFileEvent event) { if (!isDisposed()) { listener.storageFileChanged(event, FileBasedStorage.this); @@ -130,13 +124,6 @@ public class FileBasedStorage extends XmlElementStorage { return new FileSaveSession(externalizationSession); } - public void resetProviderCache() { - myProviderUpToDateHash = -1; - if (myRemoteVersionProvider != null) { - myRemoteVersionProvider.myProviderVersions = null; - } - } - private class FileSaveSession extends MySaveSession { protected FileSaveSession(MyExternalizationSession externalizationSession) { super(externalizationSession); @@ -145,9 +132,11 @@ public class FileBasedStorage extends XmlElementStorage { @Override protected boolean physicalContentNeedsSave() { VirtualFile file = getVirtualFile(); - if (file == null || !file.exists()) + if (file == null || !file.exists()) { return !myStorageData.isEmpty(); - return !StorageUtil.contentEquals(getDocumentToSave(), file); + } + Element element = getElementToSave(); + return element == null || !StorageUtil.contentEquals(element, file); } @Override @@ -169,14 +158,13 @@ public class FileBasedStorage extends XmlElementStorage { } LOG.assertTrue(myFile != null); - myCachedVirtualFile = StorageUtil.save(myFile, getDocumentToSave(), this); + myCachedVirtualFile = StorageUtil.save(myFile, getElementToSave(), this, true, myCachedVirtualFile); } @NotNull @Override - public Collection<IFile> getStorageFilesToSave() throws StateStorageException { - boolean needsSave = needsSave(); - if (needsSave) { + public Collection<File> getStorageFilesToSave() { + if (needsSave()) { if (LOG.isDebugEnabled()) { LOG.info("File " + myFileSpec + " needs save; hash=" + myUpToDateHash + "; currentHash=" + calcHash() + "; " + "content needs save=" + physicalContentNeedsSave()); @@ -190,21 +178,17 @@ public class FileBasedStorage extends XmlElementStorage { @NotNull @Override - public List<IFile> getAllStorageFiles() { + public List<File> getAllStorageFiles() { return Collections.singletonList(myFile); } } @Override - protected void loadState(final StorageData result, final Element element) throws StateStorageException { - ((FileStorageData)result).myFilePath = myFile.getAbsolutePath(); - super.loadState(result, element); - } - - @Override @NotNull protected StorageData createStorageData() { - return new FileStorageData(myRootElementName); + FileStorageData data = new FileStorageData(myRootElementName); + data.myFilePath = myFilePath; + return data; } public static class FileStorageData extends StorageData { @@ -234,18 +218,24 @@ public class FileBasedStorage extends XmlElementStorage { public VirtualFile getVirtualFile() { VirtualFile virtualFile = myCachedVirtualFile; if (virtualFile == null) { - myCachedVirtualFile = virtualFile = StorageUtil.getVirtualFile(myFile); + myCachedVirtualFile = virtualFile = LocalFileSystem.getInstance().findFileByIoFile(myFile); } return virtualFile; } + @NotNull public File getFile() { - return new File(myFile.getPath()); + return myFile; + } + + @NotNull + public String getFilePath() { + return myFilePath; } @Override @Nullable - protected Document loadDocument() throws StateStorageException { + protected Element loadLocalData() { myBlockSavingTheContent = false; try { VirtualFile file = getVirtualFile(); @@ -256,7 +246,7 @@ public class FileBasedStorage extends XmlElementStorage { if (file.getLength() == 0) { return processReadException(null); } - return loadDocumentImpl(file); + return StorageData.load(file); } catch (final JDOMException e) { return processReadException(e); @@ -267,7 +257,7 @@ public class FileBasedStorage extends XmlElementStorage { } @Nullable - private Document processReadException(@Nullable final Exception e) { + private Element processReadException(@Nullable final Exception e) { boolean contentTruncated = e == null; myBlockSavingTheContent = isProjectOrModuleOrWorkspaceFile() && !contentTruncated; if (!ApplicationManager.getApplication().isUnitTestMode() && !ApplicationManager.getApplication().isHeadlessEnvironment()) { @@ -291,45 +281,27 @@ public class FileBasedStorage extends XmlElementStorage { return isProjectOrModuleOrWorkspaceFile() && !contentTruncated ? "Please correct the file content" : "File content will be recreated"; } - private static Document loadDocumentImpl(final VirtualFile file) throws IOException, JDOMException { - InputStream stream = file.getInputStream(); - try { - return JDOMUtil.loadDocument(stream); - } - finally { - stream.close(); - } - } - - public String getFileName() { - return myFile.getName(); - } - - public String getFilePath() { - return myFilePath; - } - @Override public void setDefaultState(final Element element) { element.setName(myRootElementName); super.setDefaultState(element); } - protected boolean physicalContentNeedsSave(final Document doc) { - VirtualFile file = getVirtualFile(); - return file == null || !file.exists() || !StorageUtil.contentEquals(doc, file); - } - @Nullable public File updateFileExternallyFromStreamProviders() throws IOException { - StorageData loadedData = loadData(true); - Document document = getDocument(loadedData); - if (physicalContentNeedsSave(document)) { - File file = new File(myFile.getAbsolutePath()); - JDOMUtil.writeDocument(document, file, "\n"); - return file; + Element element = getElement(loadData(true)); + if (element == null) { + FileUtil.delete(myFile); + return null; } - return null; + BufferExposingByteArrayOutputStream out = StorageUtil.newContentIfDiffers(element, getVirtualFile()); + if (out == null) { + return null; + } + + File file = new File(myFile.getAbsolutePath()); + FileUtil.writeToFile(file, out.getInternalBuffer(), 0, out.size()); + return file; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java index c669bd1f37be..0ebfae887771 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java @@ -15,18 +15,18 @@ */ package com.intellij.openapi.components.impl.stores; -import com.intellij.openapi.components.StateStorageException; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.components.StateStorage; +import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import java.util.Set; -import java.util.Collection; import java.io.IOException; +import java.util.Collection; +import java.util.Set; public interface IApplicationStore extends IComponentStore { - void setOptionsPath(String path); + void setOptionsPath(@NotNull String path); void setConfigPath(@NotNull String configPath); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java index 0427a8be15ab..93a65a2eb50b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java @@ -19,10 +19,10 @@ import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; import java.util.List; import java.util.Set; @@ -54,17 +54,19 @@ public interface IComponentStore { interface SaveSession { @NotNull - List<IFile> getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException; + List<File> getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException; + @NotNull SaveSession save() throws IOException; + void finishSave(); + void reset(); @Nullable - Set<String> analyzeExternalChanges(@NotNull Set<Pair<VirtualFile,StateStorage>> changedFiles); + Set<String> analyzeExternalChanges(@NotNull Set<Pair<VirtualFile, StateStorage>> changedFiles); @NotNull - List<IFile> getAllStorageFiles(final boolean includingSubStructures); + List<File> getAllStorageFiles(final boolean includingSubStructures); } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java index 2120030db2c1..ac7bf84f6b3e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +@SuppressWarnings("deprecation") final class OldStreamProviderAdapter extends StreamProvider implements CurrentUserHolder { final com.intellij.openapi.options.StreamProvider myProvider; private final RoamingType myRoamingType; @@ -32,9 +33,8 @@ final class OldStreamProviderAdapter extends StreamProvider implements CurrentUs } @Override - public boolean saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException { + public void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException { myProvider.saveContent(fileSpec, new BufferExposingByteArrayInputStream(content, size), size, roamingType, async); - return false; } @Nullable @@ -65,4 +65,4 @@ final class OldStreamProviderAdapter extends StreamProvider implements CurrentUs public String getCurrentUserName() { return myProvider instanceof CurrentUserHolder ? ((CurrentUserHolder)myProvider).getCurrentUserName() : null; } -}
\ No newline at end of file +} diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java index 0eed50a65131..c7b93afbe8d8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java @@ -56,17 +56,12 @@ class ProjectStateStorageManager extends StateStorageManagerImpl { assert config != null : "Couldn't find old storage for " + component.getClass().getName(); final boolean workspace = isWorkspace(config.options); - String macro = StoragePathMacros.getMacroName(workspace ? StoragePathMacros.WORKSPACE_FILE : StoragePathMacros.PROJECT_FILE); - - String name = "$" + macro + "$"; - - StateStorage storage = getFileStateStorage(name); - + String fileSpec = workspace ? StoragePathMacros.WORKSPACE_FILE : StoragePathMacros.PROJECT_FILE; + StateStorage storage = getStateStorage(fileSpec, workspace ? RoamingType.DISABLED : RoamingType.PER_USER); if (operation == StateStorageOperation.READ && storage != null && workspace && !storage.hasState(component, componentName, Element.class, false)) { - name = StoragePathMacros.PROJECT_FILE; + fileSpec = StoragePathMacros.PROJECT_FILE; } - - return name; + return fileSpec; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java index 6288c62ab9e5..01e2f9e036d0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java @@ -39,13 +39,14 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; +import com.intellij.util.PathUtilRt; +import com.intellij.util.SmartList; import com.intellij.util.containers.OrderedSet; -import com.intellij.util.io.fs.FileSystem; -import com.intellij.util.io.fs.IFile; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.*; import java.lang.annotation.Annotation; @@ -55,23 +56,21 @@ import java.util.List; import java.util.Set; class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProjectStore { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.ProjectStoreImpl"); + private static final Logger LOG = Logger.getInstance(ProjectStoreImpl.class); @NonNls private static final String OLD_PROJECT_SUFFIX = "_old."; @NonNls static final String OPTION_WORKSPACE = "workspace"; - @NonNls static final String DEFAULT_STATE_STORAGE = StoragePathMacros.PROJECT_FILE; - static final Storage DEFAULT_STORAGE_ANNOTATION = new MyStorage(); private static int originalVersion = -1; protected ProjectImpl myProject; private StorageScheme myScheme = StorageScheme.DEFAULT; - private String myCachedLocation; private String myPresentableUrl; - ProjectStoreImpl(final ProjectImpl project) { + ProjectStoreImpl(@NotNull ProjectImpl project) { super(project); + myProject = project; } @@ -169,15 +168,15 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject final File dirStore = file.isDirectory() ? new File(file, Project.DIRECTORY_STORE_FOLDER) : new File(file.getParentFile(), Project.DIRECTORY_STORE_FOLDER); - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.PROJECT_FILE), new File(dirStore, "misc.xml").getPath()); + stateStorageManager.addMacro(StoragePathMacros.PROJECT_FILE, new File(dirStore, "misc.xml").getPath()); final File ws = new File(dirStore, "workspace.xml"); - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.WORKSPACE_FILE), ws.getPath()); + stateStorageManager.addMacro(StoragePathMacros.WORKSPACE_FILE, ws.getPath()); if (!ws.exists() && !file.isDirectory()) { useOldWsContent(filePath, ws); } - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.PROJECT_CONFIG_DIR), dirStore.getPath()); + stateStorageManager.addMacro(StoragePathMacros.PROJECT_CONFIG_DIR, dirStore.getPath()); ApplicationManager.getApplication().invokeAndWait(new Runnable() { @Override @@ -189,10 +188,10 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject else { myScheme = StorageScheme.DEFAULT; - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.PROJECT_FILE), filePath); + stateStorageManager.addMacro(StoragePathMacros.PROJECT_FILE, filePath); final String workspacePath = composeWsPath(filePath); - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.WORKSPACE_FILE), workspacePath); + stateStorageManager.addMacro(StoragePathMacros.WORKSPACE_FILE, workspacePath); ApplicationManager.getApplication().invokeAndWait(new Runnable() { @Override @@ -202,7 +201,6 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject }, ModalityState.defaultModalityState()); } - myCachedLocation = null; myPresentableUrl = null; } @@ -258,18 +256,26 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject final String path = getProjectFilePath(); if (!StringUtil.isEmptyOrSpaces(path)) { - return myScheme == StorageScheme.DEFAULT ? new File(path).getParent() : new File(path).getParentFile().getParent(); + return getBasePath(new File(path)); } //we are not yet initialized completely ("open directory", etc) - final StateStorage s = getStateStorageManager().getFileStateStorage(StoragePathMacros.PROJECT_FILE); - if (!(s instanceof FileBasedStorage)) return null; - final FileBasedStorage storage = (FileBasedStorage)s; + StateStorage storage = getStateStorageManager().getStateStorage(StoragePathMacros.PROJECT_FILE, RoamingType.PER_USER); + if (!(storage instanceof FileBasedStorage)) { + return null; + } - final File file = storage.getFile(); - if (file == null) return null; + return getBasePath(((FileBasedStorage)storage).getFile()); + } - return myScheme == StorageScheme.DEFAULT ? file.getParent() : file.getParentFile().getParent(); + private String getBasePath(@NotNull File file) { + if (myScheme == StorageScheme.DEFAULT) { + return file.getParent(); + } + else { + File parentFile = file.getParentFile(); + return parentFile == null ? null : parentFile.getParent(); + } } @NotNull @@ -302,7 +308,7 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject return baseDir.getName().replace(":", ""); } else { - String temp = getProjectFileName(); + String temp = PathUtilRt.getFileName(((FileBasedStorage)getProjectFileStorage()).getFilePath()); FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(temp); if (fileType instanceof ProjectFileType) { temp = temp.substring(0, temp.length() - fileType.getDefaultExtension().length() - 1); @@ -316,13 +322,6 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject } @NotNull - private String getProjectFileName() { - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(StoragePathMacros.PROJECT_FILE); - assert storage != null; - return storage.getFileName(); - } - - @NotNull @Override public StorageScheme getStorageScheme() { return myScheme; @@ -349,26 +348,28 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject @Override public VirtualFile getProjectFile() { - if (myProject.isDefault()) return null; - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(StoragePathMacros.PROJECT_FILE); + return myProject.isDefault() ? null : ((FileBasedStorage)getProjectFileStorage()).getVirtualFile(); + } + + @NotNull + private XmlElementStorage getProjectFileStorage() { + // XmlElementStorage if default project, otherwise FileBasedStorage + XmlElementStorage storage = (XmlElementStorage)getStateStorageManager().getStateStorage(StoragePathMacros.PROJECT_FILE, RoamingType.PER_USER); assert storage != null; - return storage.getVirtualFile(); + return storage; } @Override public VirtualFile getWorkspaceFile() { if (myProject.isDefault()) return null; - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(StoragePathMacros.WORKSPACE_FILE); + final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getStateStorage(StoragePathMacros.WORKSPACE_FILE, RoamingType.DISABLED); assert storage != null; return storage.getVirtualFile(); } @Override public void loadProjectFromTemplate(@NotNull final ProjectImpl defaultProject) { - final StateStorage stateStorage = getStateStorageManager().getFileStateStorage(DEFAULT_STATE_STORAGE); - - assert stateStorage instanceof XmlElementStorage; - XmlElementStorage xmlElementStorage = (XmlElementStorage)stateStorage; + XmlElementStorage stateStorage = getProjectFileStorage(); defaultProject.save(); final IProjectStore projectStore = defaultProject.getStateStore(); @@ -376,24 +377,19 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject DefaultProjectStoreImpl defaultProjectStore = (DefaultProjectStoreImpl)projectStore; final Element element = defaultProjectStore.getStateCopy(); if (element != null) { - xmlElementStorage.setDefaultState(element); + stateStorage.setDefaultState(element); } } @NotNull @Override public String getProjectFilePath() { - if (myProject.isDefault()) return ""; - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(StoragePathMacros.PROJECT_FILE); - assert storage != null; - return storage.getFilePath(); + return myProject.isDefault() ? "" : ((FileBasedStorage)getProjectFileStorage()).getFilePath(); } @Override protected XmlElementStorage getMainStorage() { - final XmlElementStorage storage = (XmlElementStorage)getStateStorageManager().getFileStateStorage(DEFAULT_STATE_STORAGE); - assert storage != null; - return storage; + return getProjectFileStorage(); } @NotNull @@ -402,7 +398,6 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject return new ProjectStateStorageManager(PathMacroManager.getInstance(getComponentManager()).createTrackingSubstitutor(), myProject); } - static class ProjectStorageData extends BaseStorageData { protected final Project myProject; @@ -447,16 +442,16 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject } @Override - public void load(@NotNull final Element root) throws IOException { - final String v = root.getAttributeValue(VERSION_OPTION); + public void load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { + final String v = rootElement.getAttributeValue(VERSION_OPTION); //noinspection AssignmentToStaticFieldFromInstanceMethod originalVersion = v != null ? Integer.parseInt(v) : 0; if (originalVersion != ProjectManagerImpl.CURRENT_FORMAT_VERSION) { - convert(root, originalVersion); + convert(rootElement, originalVersion); } - super.load(root); + super.load(rootElement, pathMacroSubstitutor, intern); } protected void convert(final Element root, final int originalVersion) { @@ -479,19 +474,16 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject @NotNull @Override - public List<IFile> getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException { - List<IFile> result = new ArrayList<IFile>(); - + public List<File> getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException { + List<File> result = new SmartList<File>(); if (includingSubStructures) { - collectSubfilesToSave(result); + collectSubFilesToSave(result); } - result.addAll(super.getAllStorageFilesToSave(false)); - return result; } - protected void collectSubfilesToSave(final List<IFile> result) throws IOException { } + protected void collectSubFilesToSave(final List<File> result) throws IOException { } @NotNull @Override @@ -523,10 +515,10 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject return ApplicationManager.getApplication().runReadAction(new Computable<ReadonlyStatusHandler.OperationStatus>() { @Override public ReadonlyStatusHandler.OperationStatus compute() { - final List<IFile> filesToSave; + final List<File> filesToSave; try { filesToSave = getAllStorageFilesToSave(true); - final Iterator<IFile> iterator = filesToSave.iterator(); + final Iterator<File> iterator = filesToSave.iterator(); while (iterator.hasNext()) { if (!iterator.next().exists()) { iterator.remove(); @@ -543,14 +535,12 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject if (myProject.isToSaveProjectName()) { final VirtualFile baseDir = getProjectBaseDir(); if (baseDir != null && baseDir.isValid()) { - filesToSave.add(FileSystem.FILE_SYSTEM - .createFile(new File(new File(baseDir.getPath(), Project.DIRECTORY_STORE_FOLDER), ProjectImpl.NAME_FILE).getPath())); + filesToSave.add(new File(new File(baseDir.getPath(), Project.DIRECTORY_STORE_FOLDER), ProjectImpl.NAME_FILE)); } } - for (IFile file : filesToSave) { + for (File file : filesToSave) { final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file); - if (virtualFile != null) { virtualFile.refresh(false, false); if (virtualFile.isValid() && !virtualFile.isWritable()) { @@ -670,7 +660,7 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject @Override public String file() { - return DEFAULT_STATE_STORAGE; + return StoragePathMacros.PROJECT_FILE; } @Override @@ -679,15 +669,21 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject } @Override + public RoamingType roamingType() { + return RoamingType.PER_USER; + } + + @Override public Class<? extends StateStorage> storageClass() { - return StorageAnnotationsDefaultValues.NullStateStorage.class; + return StateStorage.class; } @Override public Class<? extends StateSplitter> stateSplitter() { - return StorageAnnotationsDefaultValues.NullStateSplitter.class; + return StateSplitter.class; } + @NotNull @Override public Class<? extends Annotation> annotationType() { throw new UnsupportedOperationException("Method annotationType not implemented in " + getClass()); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java index 68d882d2b204..d3e9d1c03d2e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java @@ -19,10 +19,10 @@ import com.intellij.openapi.components.*; import com.intellij.openapi.options.StreamProvider; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.util.Collection; import java.util.List; import java.util.Set; @@ -31,7 +31,7 @@ import java.util.Set; * @author mike */ public interface StateStorageManager { - void addMacro(String macro, String expansion); + void addMacro(@NotNull String macro, @NotNull String expansion); @Nullable TrackingPathMacroSubstitutor getMacroSubstitutor(); @@ -40,8 +40,16 @@ public interface StateStorageManager { StateStorage getStateStorage(@NotNull Storage storageSpec) throws StateStorageException; @Nullable + StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType); + + @Deprecated + @Nullable + /** + * @deprecated Use {@link #getStateStorage(String, com.intellij.openapi.components.RoamingType)} + */ StateStorage getFileStateStorage(@NotNull String fileSpec); + @NotNull Collection<String> getStorageFileNames(); void clearStateStorage(@NotNull String file); @@ -57,7 +65,7 @@ public interface StateStorageManager { @Nullable StateStorage getOldStorage(Object component, String componentName, StateStorageOperation operation) throws StateStorageException; - @Nullable + @NotNull String expandMacros(@NotNull String file); @Deprecated @@ -81,11 +89,11 @@ public interface StateStorageManager { Set<String> analyzeExternalChanges(@NotNull Set<Pair<VirtualFile, StateStorage>> files); @NotNull - List<IFile> getAllStorageFilesToSave() throws StateStorageException; + List<File> getAllStorageFilesToSave() throws StateStorageException; @NotNull - List<IFile> getAllStorageFiles(); + List<File> getAllStorageFiles(); void save() throws StateStorageException; } -}
\ No newline at end of file +} diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java index 431c9a97820c..651acce271c0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java @@ -24,12 +24,14 @@ import com.intellij.openapi.options.CurrentUserHolder; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.RoamingTypeDisabled; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ObjectUtils; +import com.intellij.util.PathUtilRt; +import com.intellij.util.ReflectionUtil; import com.intellij.util.SmartList; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TObjectLongHashMap; @@ -99,9 +101,14 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } @Override - public synchronized void addMacro(String macro, String expansion) { - // avoid hundreds of $MODULE_FILE$ instances - myMacros.put(("$" + macro + "$").intern(), expansion); + public synchronized void addMacro(@NotNull String macro, @NotNull String expansion) { + assert !macro.isEmpty(); + // backward compatibility + if (macro.charAt(0) != '$') { + LOG.warn("Add macros instead of macro name: " + macro); + expansion = '$' + macro + '$'; + } + myMacros.put(macro, expansion); } @Override @@ -123,15 +130,15 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } } - @Override @Nullable - public StateStorage getFileStateStorage(@NotNull String fileName) { + @Override + public StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType) { myStorageLock.lock(); try { - StateStorage stateStorage = myStorages.get(fileName); + StateStorage stateStorage = myStorages.get(fileSpec); if (stateStorage == null) { - stateStorage = createFileStateStorage(fileName); - putStorageToMap(fileName, stateStorage); + stateStorage = createFileStateStorage(fileSpec, roamingType); + putStorageToMap(fileSpec, stateStorage); } return stateStorage; } @@ -141,6 +148,13 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } @Override + @Nullable + public StateStorage getFileStateStorage(@NotNull String fileSpec) { + return getStateStorage(fileSpec, RoamingType.PER_USER); + } + + @NotNull + @Override public Collection<String> getStorageFileNames() { myStorageLock.lock(); try { @@ -176,19 +190,19 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Nullable private StateStorage createStateStorage(Storage storageSpec) throws StateStorageException { - if (!storageSpec.storageClass().equals(StorageAnnotationsDefaultValues.NullStateStorage.class)) { + if (!storageSpec.storageClass().equals(StateStorage.class)) { String key = UUID.randomUUID().toString(); ((MutablePicoContainer)myPicoContainer).registerComponentImplementation(key, storageSpec.storageClass()); return (StateStorage)myPicoContainer.getComponentInstance(key); } - if (!storageSpec.stateSplitter().equals(StorageAnnotationsDefaultValues.NullStateSplitter.class)) { + if (!storageSpec.stateSplitter().equals(StateSplitter.class)) { return createDirectoryStateStorage(storageSpec.file(), storageSpec.stateSplitter()); } - return createFileStateStorage(storageSpec.file()); + return createFileStateStorage(storageSpec.file(), storageSpec.roamingType()); } private static String getStorageSpecId(Storage storageSpec) { - if (!storageSpec.storageClass().equals(StorageAnnotationsDefaultValues.NullStateStorage.class)) { + if (!storageSpec.storageClass().equals(StateStorage.class)) { return storageSpec.storageClass().getName(); } else { @@ -209,41 +223,30 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Nullable private StateStorage createDirectoryStateStorage(String file, Class<? extends StateSplitter> splitterClass) throws StateStorageException { - String expandedFile = expandMacros(file); - if (expandedFile == null) { - myStorages.put(file, null); - return null; - } - final StateSplitter splitter; try { - splitter = splitterClass.newInstance(); + splitter = ReflectionUtil.newInstance(splitterClass); } - catch (InstantiationException e) { + catch (RuntimeException e) { throw new StateStorageException(e); } - catch (IllegalAccessException e) { - throw new StateStorageException(e); - } - - return new DirectoryBasedStorage(myPathMacroSubstitutor, expandedFile, splitter, this, myPicoContainer); + return new DirectoryBasedStorage(myPathMacroSubstitutor, expandMacros(file), splitter, this, myPicoContainer); } @Nullable - private StateStorage createFileStateStorage(@NotNull final String fileSpec) { + private StateStorage createFileStateStorage(@NotNull final String fileSpec, @Nullable RoamingType roamingType) { String expandedFile = expandMacros(fileSpec); - if (expandedFile == null) { - myStorages.put(fileSpec, null); - return null; - } - String extension = FileUtilRt.getExtension(new File(expandedFile).getName()); - if (!ourHeadlessEnvironment && extension.isEmpty()) { + if (!ourHeadlessEnvironment && PathUtilRt.getFileName(expandedFile).lastIndexOf('.') < 0) { throw new IllegalArgumentException("Extension is missing for storage file: " + expandedFile); } - return new FileBasedStorage(getMacroSubstitutor(fileSpec), getStreamProvider(), expandedFile, fileSpec, myRootTagName, this, - myPicoContainer, ComponentRoamingManager.getInstance(), this) { + if (roamingType != RoamingType.PER_USER && fileSpec.equals(StoragePathMacros.WORKSPACE_FILE)) { + roamingType = RoamingType.DISABLED; + } + + return new FileBasedStorage(expandedFile, fileSpec, roamingType, getMacroSubstitutor(fileSpec), myRootTagName, this, + myPicoContainer, getStreamProvider(), this) { @Override @NotNull protected StorageData createStorageData() { @@ -271,8 +274,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di String filePath = getNotNullVersionsFilePath(); if (filePath != null) { try { - Document document = JDOMUtil.loadDocument(new File(filePath)); - loadComponentVersions(result, document); + loadComponentVersions(result, JDOMUtil.loadDocument(new File(filePath))); } catch (JDOMException e) { LOG.debug(e); @@ -294,6 +296,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di public static void loadComponentVersions(TObjectLongHashMap<String> result, Document document) { List<Element> componentObjects = document.getRootElement().getChildren("component"); + result.ensureCapacity(componentObjects.size()); for (Element component : componentObjects) { String name = component.getAttributeValue("name"); String version = component.getAttributeValue("version"); @@ -330,26 +333,21 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di private static final Pattern MACRO_PATTERN = Pattern.compile("(\\$[^\\$]*\\$)"); @Override - @Nullable + @NotNull public synchronized String expandMacros(@NotNull String file) { - final Matcher matcher = MACRO_PATTERN.matcher(file); + Matcher matcher = MACRO_PATTERN.matcher(file); while (matcher.find()) { String m = matcher.group(1); - if (!myMacros.containsKey(m) || !ApplicationManager.getApplication().isUnitTestMode() && myMacros.get(m) == null) { - throw new IllegalArgumentException("Unknown macro: " + m + " in storage spec: " + file); + if (!myMacros.containsKey(m)) { + throw new IllegalArgumentException("Unknown macro: " + m + " in storage file spec: " + file); } } - String actualFile = file; - + String expanded = file; for (String macro : myMacros.keySet()) { - final String replacement = myMacros.get(macro); - if (replacement != null) { - actualFile = StringUtil.replace(actualFile, macro, replacement); - } + expanded = StringUtil.replace(expanded, macro, myMacros.get(macro)); } - - return actualFile; + return expanded; } @NotNull @@ -424,7 +422,9 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Override @Nullable public StateStorage getOldStorage(Object component, String componentName, StateStorageOperation operation) throws StateStorageException { - return getFileStateStorage(getOldStorageSpec(component, componentName, operation)); + String oldStorageSpec = getOldStorageSpec(component, componentName, operation); + //noinspection deprecation + return oldStorageSpec == null ? null : getStateStorage(oldStorageSpec, component instanceof RoamingTypeDisabled ? RoamingType.DISABLED : RoamingType.PER_USER); } @Nullable @@ -440,14 +440,14 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Override @NotNull - public List<IFile> getAllStorageFilesToSave() throws StateStorageException { + public List<File> getAllStorageFilesToSave() throws StateStorageException { assert mySession == this; return myCompoundSaveSession.getAllStorageFilesToSave(); } @Override @NotNull - public List<IFile> getAllStorageFiles() { + public List<File> getAllStorageFiles() { return myCompoundSaveSession.getAllStorageFiles(); } @@ -503,15 +503,16 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } public void save() { - if (!isDirty) return; + if (!isDirty) { + return; + } + String filePath = getNotNullVersionsFilePath(); if (filePath != null) { - File dir = new File(filePath).getParentFile(); - if (!dir.isDirectory() && !dir.mkdirs()) { - LOG.warn("Unable to create: " + dir); - } + File file = new File(filePath); + FileUtilRt.createParentDirs(file); try { - JDOMUtil.writeDocument(new Document(createComponentVersionsXml(getComponentVersions())), filePath, "\n"); + JDOMUtil.writeParent(createComponentVersionsXml(getComponentVersions()), file, "\n"); isDirty = false; } catch (IOException e) { @@ -546,6 +547,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di return root; } + @SuppressWarnings("deprecation") private static class OldStreamProviderManager extends StreamProvider implements CurrentUserHolder { private final List<OldStreamProviderAdapter> myStreamProviders = new SmartList<OldStreamProviderAdapter>(); @@ -575,22 +577,17 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } @Override - public boolean saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException { - boolean result = false; + public void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException { for (StreamProvider streamProvider : myStreamProviders) { try { - if (streamProvider.isEnabled() && streamProvider.isApplicable(fileSpec, roamingType) && streamProvider.saveContent(fileSpec, content, size, roamingType, async)) { - result = true; + if (streamProvider.isEnabled() && streamProvider.isApplicable(fileSpec, roamingType)) { + streamProvider.saveContent(fileSpec, content, size, roamingType, async); } } - catch (ConnectException e) { - LOG.debug("Cannot send user profile to server: " + e.getLocalizedMessage()); - } catch (Exception e) { LOG.debug(e); } } - return result; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java index bd732705446a..2344c8526b0a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java @@ -26,7 +26,6 @@ import com.intellij.openapi.editor.DocumentRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.project.ex.ProjectEx; -import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; @@ -38,7 +37,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.SystemProperties; import com.intellij.util.UniqueFileNamesProvider; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.io.fs.IFile; import com.intellij.util.ui.UIUtil; import org.jdom.Document; import org.jdom.Element; @@ -59,11 +57,12 @@ import java.util.Set; * @author mike */ public class StorageUtil { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.StorageUtil"); + private static final Logger LOG = Logger.getInstance(StorageUtil.class); private static final boolean DUMP_COMPONENT_STATES = SystemProperties.getBooleanProperty("idea.log.externally.changed.component.states", false); @SuppressWarnings("SpellCheckingInspection") private static final SimpleDateFormat LOG_DIR_FORMAT = new SimpleDateFormat("yyyyMMdd-HHmmss"); + private static final Pair<byte[], String> NON_EXISTENT_FILE_DATA = Pair.create(null, SystemProperties.getLineSeparator()); private StorageUtil() { } @@ -110,34 +109,86 @@ public class StorageUtil { return notified; } + + public static boolean isEmpty(@Nullable Parent element) { + if (element == null) { + return true; + } + else if (element instanceof Element) { + return JDOMUtil.isEmpty((Element)element); + } + else { + Document document = (Document)element; + return !document.hasRootElement() || JDOMUtil.isEmpty(document.getRootElement()); + } + } + + /** + * Due to historical reasons files in ROOT_CONFIG don’t wrapped into document (xml prolog) opposite to files in APP_CONFIG + */ @Nullable - static VirtualFile save(@NotNull IFile file, Parent element, Object requestor) throws StateStorageException { - try { - String lineSeparator; - String oldText; - if (file.exists()) { - VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); - Couple<String> pair = loadFile(vFile); - lineSeparator = pair.second; - oldText = pair.first; + static VirtualFile save(@NotNull File file, @Nullable Parent element, Object requestor, boolean wrapAsDocument, @Nullable VirtualFile cachedVirtualFile) throws StateStorageException { + if (isEmpty(element)) { + if (!file.exists()) { + return null; + } + + VirtualFile virtualFile = cachedVirtualFile; + if (virtualFile == null || !virtualFile.isValid()) { + virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file); + } + if (virtualFile == null) { + LOG.info("Cannot find virtual file " + file.getAbsolutePath()); + FileUtil.delete(file); } else { - oldText = null; - lineSeparator = SystemProperties.getLineSeparator(); - file.createParentDirs(); + AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); + try { + virtualFile.delete(requestor); + } + catch (IOException e) { + throw new StateStorageException(e); + } + finally { + token.finish(); + } } + return null; + } - String text = JDOMUtil.writeParent(element, lineSeparator); - if (text.equals(oldText)) { - return null; + VirtualFile virtualFile = cachedVirtualFile == null || !cachedVirtualFile.isValid() ? null : cachedVirtualFile; + Parent document = !wrapAsDocument || element instanceof Document ? element : new Document((Element)element); + try { + BufferExposingByteArrayOutputStream byteOut; + if (file.exists()) { + if (virtualFile == null) { + virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + } + + Pair<byte[], String> pair = loadFile(virtualFile); + byteOut = writeToBytes(document, pair.second); + if (equal(pair.first, byteOut)) { + return null; + } + } + else { + FileUtil.createParentDirs(file); + byteOut = writeToBytes(document, SystemProperties.getLineSeparator()); } // mark this action as modifying the file which daemon analyzer should ignore AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); try { - VirtualFile virtualFile = getOrCreateVirtualFile(requestor, file); - byte[] bytes = text.getBytes(CharsetToolkit.UTF8); - virtualFile.setBinaryContent(bytes, -1, -1, requestor); + if (virtualFile == null) { + virtualFile = getOrCreateVirtualFile(requestor, file); + } + OutputStream virtualFileOut = virtualFile.getOutputStream(requestor); + try { + byteOut.writeTo(virtualFileOut); + } + finally { + virtualFileOut.close(); + } return virtualFile; } finally { @@ -150,70 +201,78 @@ public class StorageUtil { } @NotNull - static VirtualFile getOrCreateVirtualFile(final Object requestor, final IFile ioFile) throws IOException { - VirtualFile vFile = getVirtualFile(ioFile); - - if (vFile == null) { - vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile); - } + private static BufferExposingByteArrayOutputStream writeToBytes(@NotNull Parent element, @NotNull String lineSeparator) throws IOException { + BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(512); + JDOMUtil.writeParent(element, out, lineSeparator); + return out; + } - if (vFile == null) { - final IFile parentFile = ioFile.getParentFile(); - final VirtualFile parentVFile = - LocalFileSystem.getInstance().refreshAndFindFileByIoFile(parentFile); // need refresh if the directory has just been created - if (parentVFile == null) { - throw new IOException(ProjectBundle.message("project.configuration.save.file.not.found", parentFile.getPath())); + @NotNull + static VirtualFile getOrCreateVirtualFile(@Nullable Object requestor, @NotNull File ioFile) throws IOException { + VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile); + if (virtualFile == null) { + File parentFile = ioFile.getParentFile(); + // need refresh if the directory has just been created + VirtualFile parentVirtualFile = parentFile == null ? null : LocalFileSystem.getInstance().refreshAndFindFileByIoFile(parentFile); + if (parentVirtualFile == null) { + throw new IOException(ProjectBundle.message("project.configuration.save.file.not.found", parentFile == null ? "" : parentFile.getPath())); } - vFile = parentVFile.createChildData(requestor, ioFile.getName()); + virtualFile = parentVirtualFile.createChildData(requestor, ioFile.getName()); } - - return vFile; - } - - @Nullable - static VirtualFile getVirtualFile(final IFile ioFile) { - return LocalFileSystem.getInstance().findFileByIoFile(ioFile); + return virtualFile; } /** * @return pair.first - file contents (null if file does not exist), pair.second - file line separators */ - private static Couple<String> loadFile(@Nullable final VirtualFile file) throws IOException { + @NotNull + private static Pair<byte[], String> loadFile(@Nullable final VirtualFile file) throws IOException { if (file == null || !file.exists()) { - return Couple.of(null, SystemProperties.getLineSeparator()); + return NON_EXISTENT_FILE_DATA; } - String fileText = new String(file.contentsToByteArray(), CharsetToolkit.UTF8); - final int index = fileText.indexOf('\n'); - return Couple.of(fileText, index == -1 - ? SystemProperties.getLineSeparator() - : index - 1 >= 0 ? fileText.charAt(index - 1) == '\r' ? "\r\n" : "\n" : "\n"); + byte[] bytes = file.contentsToByteArray(); + String lineSeparator = file.getDetectedLineSeparator(); + if (lineSeparator == null) { + String fileText = new String(bytes, CharsetToolkit.UTF8); + final int index = fileText.indexOf('\n'); + lineSeparator = index == -1 + ? SystemProperties.getLineSeparator() + : index - 1 >= 0 ? fileText.charAt(index - 1) == '\r' ? "\r\n" : "\n" : "\n"; + } + return Pair.create(bytes, lineSeparator); } - public static boolean contentEquals(@NotNull final Document document, @NotNull final VirtualFile file) { + public static boolean contentEquals(@NotNull Parent element, @NotNull VirtualFile file) { + return newContentIfDiffers(element, file) == null; + } + + @Nullable + public static BufferExposingByteArrayOutputStream newContentIfDiffers(@NotNull Parent element, @Nullable VirtualFile file) { try { - final Couple<String> pair = loadFile(file); - return pair.first != null && pair.first.equals(JDOMUtil.writeDocument(document, pair.second)); + Pair<byte[], String> pair = loadFile(file); + BufferExposingByteArrayOutputStream out = writeToBytes(element, pair.second); + return pair.first != null && equal(pair.first, out) ? null : out; } catch (IOException e) { LOG.debug(e); - return false; + return null; } } - public static boolean contentEquals(@NotNull final Element element, @NotNull final VirtualFile file) { - try { - final Couple<String> pair = loadFile(file); - return pair.first != null && pair.first.equals(printElement(element, pair.second)); - } - catch (IOException e) { - LOG.debug(e); + public static boolean equal(byte[] a1, @NotNull BufferExposingByteArrayOutputStream out) { + int length = out.size(); + if (a1.length != length) { return false; } - } - static String printElement(final Element element, final String lineSeparator) throws StateStorageException { - return JDOMUtil.writeElement(element, lineSeparator); + byte[] internalBuffer = out.getInternalBuffer(); + for (int i = 0; i < length; i++) { + if (a1[i] != internalBuffer[i]) { + return false; + } + } + return true; } @Nullable @@ -229,6 +288,7 @@ public class StorageUtil { } } + @SuppressWarnings("Contract") @Nullable public static Document loadDocument(@Nullable InputStream stream) { if (stream == null) { @@ -252,29 +312,20 @@ public class StorageUtil { } @NotNull - public static BufferExposingByteArrayOutputStream documentToBytes(@NotNull Document document, boolean useSystemLineSeparator) throws IOException { - BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(512); - OutputStreamWriter writer = new OutputStreamWriter(out, CharsetToolkit.UTF8_CHARSET); - try { - JDOMUtil.writeDocument(document, writer, useSystemLineSeparator ? SystemProperties.getLineSeparator() : "\n"); - return out; - } - finally { - writer.close(); - } + public static BufferExposingByteArrayOutputStream elementToBytes(@NotNull Parent element, boolean useSystemLineSeparator) throws IOException { + return writeToBytes(element, useSystemLineSeparator ? SystemProperties.getLineSeparator() : "\n"); } - public static boolean sendContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull Document copy, @NotNull RoamingType type, boolean async) { + public static void sendContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull Parent element, @NotNull RoamingType type, boolean async) { if (!provider.isApplicable(fileSpec, type)) { - return false; + return; } try { - return doSendContent(provider, fileSpec, copy, type, async); + doSendContent(provider, fileSpec, element, type, async); } catch (IOException e) { LOG.warn(e); - return false; } } @@ -287,10 +338,10 @@ public class StorageUtil { /** * You must call {@link StreamProvider#isApplicable(String, com.intellij.openapi.components.RoamingType)} before */ - public static boolean doSendContent(StreamProvider provider, String fileSpec, Document copy, RoamingType type, boolean async) throws IOException { + public static void doSendContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull Parent element, @NotNull RoamingType type, boolean async) throws IOException { // we should use standard line-separator (\n) - stream provider can share file content on any OS - BufferExposingByteArrayOutputStream content = documentToBytes(copy, false); - return provider.saveContent(fileSpec, content.getInternalBuffer(), content.size(), type, async); + BufferExposingByteArrayOutputStream content = elementToBytes(element, false); + provider.saveContent(fileSpec, content.getInternalBuffer(), content.size(), type, async); } public static void logStateDiffInfo(Set<Pair<VirtualFile, StateStorage>> changedFiles, Set<String> componentNames) { @@ -309,10 +360,9 @@ public class StorageUtil { StateStorage storage = pair.second; if (storage instanceof XmlElementStorage) { - Document state = ((XmlElementStorage)storage).logComponents(); + Element state = ((XmlElementStorage)storage).logComponents(); if (state != null) { - File logFile = new File(logDirectory, "prev_" + file.getName()); - JDOMUtil.writeDocument(state, logFile, "\n"); + JDOMUtil.writeParent(state, new File(logDirectory, "prev_" + file.getName()), "\n"); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java index ec57d8c63601..cf49b927ad0e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java @@ -12,7 +12,9 @@ import java.util.Collections; public abstract class StreamProvider { public static final StreamProvider[] EMPTY_ARRAY = new StreamProvider[0]; - public abstract boolean isEnabled(); + public boolean isEnabled() { + return true; + } /** * If true, special version file per storage file will keep version of component. @@ -37,7 +39,7 @@ public abstract class StreamProvider { * @param roamingType * @param async */ - public abstract boolean saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException; + public abstract void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException; @Nullable public abstract InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException; diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java index b028676168d5..76f47db77533 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java @@ -15,44 +15,48 @@ */ package com.intellij.openapi.components.impl.stores; -import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.options.CurrentUserHolder; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.vfs.SafeWriteRequestor; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; +import gnu.trove.THashSet; import gnu.trove.TObjectLongHashMap; import org.jdom.Document; import org.jdom.Element; +import org.jdom.JDOMException; import org.jdom.filter.ElementFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.*; public abstract class XmlElementStorage implements StateStorage, Disposable { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.XmlElementStorage"); + private static final Logger LOG = Logger.getInstance(XmlElementStorage.class); + + private final static RoamingElementFilter DISABLED_ROAMING_ELEMENT_FILTER = new RoamingElementFilter(RoamingType.DISABLED); private static final String ATTR_NAME = "name"; private static final String VERSION_FILE_SUFFIX = ".ver"; protected TrackingPathMacroSubstitutor myPathMacroSubstitutor; - @NotNull private final String myRootElementName; + @NotNull protected final String myRootElementName; private Object mySession; private StorageData myLoadedData; protected final StreamProvider myStreamProvider; protected final String myFileSpec; - private final ComponentRoamingManager myComponentRoamingManager; protected boolean myBlockSavingTheContent = false; protected int myUpToDateHash = -1; - protected int myProviderUpToDateHash = -1; + private int myProviderUpToDateHash = -1; private boolean mySavingDisabled = false; private final Map<String, Object> myStorageComponentStates = new THashMap<String, Object>(); // at load we store Element, on setState Integer of hash @@ -60,6 +64,8 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { private final ComponentVersionProvider myLocalVersionProvider; protected final RemoteComponentVersionProvider myRemoteVersionProvider; + private final RoamingType myRoamingType; + protected ComponentVersionListener myListener = new ComponentVersionListener(){ @Override public void componentStateChanged(String componentName) { @@ -69,18 +75,18 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { private boolean myDisposed; - protected XmlElementStorage(@Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, + protected XmlElementStorage(@NotNull String fileSpec, + @Nullable RoamingType roamingType, + @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, @NotNull Disposable parentDisposable, @NotNull String rootElementName, @Nullable StreamProvider streamProvider, - String fileSpec, - ComponentRoamingManager componentRoamingManager, ComponentVersionProvider componentVersionProvider) { + myFileSpec = fileSpec; + myRoamingType = roamingType == null ? RoamingType.PER_USER : roamingType; myPathMacroSubstitutor = pathMacroSubstitutor; myRootElementName = rootElementName; - myStreamProvider = streamProvider; - myFileSpec = fileSpec; - myComponentRoamingManager = componentRoamingManager; + myStreamProvider = myRoamingType == RoamingType.DISABLED ? null : streamProvider; Disposer.register(parentDisposable, this); myLocalVersionProvider = componentVersionProvider; @@ -92,36 +98,39 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Nullable - protected abstract Document loadDocument() throws StateStorageException; + protected abstract Element loadLocalData(); @Nullable - public synchronized Element getState(final String componentName) throws StateStorageException { + public synchronized Element getState(@NotNull String componentName) { final StorageData storageData = getStorageData(false); final Element state = storageData.getState(componentName); - if (state != null) { if (!myStorageComponentStates.containsKey(componentName)) { myStorageComponentStates.put(componentName, state); } storageData.removeState(componentName); } - return state; } @Override - public boolean hasState(final Object component, final String componentName, final Class<?> aClass, final boolean reloadData) throws StateStorageException { + public boolean hasState(final Object component, @NotNull String componentName, final Class<?> aClass, final boolean reloadData) throws StateStorageException { return getStorageData(reloadData).hasState(componentName); } @Override @Nullable - public <T> T getState(final Object component, final String componentName, Class<T> stateClass, @Nullable T mergeInto) throws StateStorageException { + public <T> T getState(final Object component, @NotNull String componentName, Class<T> stateClass, @Nullable T mergeInto) throws StateStorageException { return DefaultStateSerializer.deserializeState(getState(componentName), stateClass, mergeInto); } @NotNull - protected StorageData getStorageData(final boolean reloadData) throws StateStorageException { + protected StorageData getStorageData() { + return getStorageData(false); + } + + @NotNull + private StorageData getStorageData(boolean reloadData) { if (myLoadedData != null && !reloadData) { return myLoadedData; } @@ -131,48 +140,48 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @NotNull - protected StorageData loadData(boolean useProvidersData) throws StateStorageException { - Document document = loadDocument(); + protected StorageData loadData(boolean useProvidersData) { StorageData result = createStorageData(); - if (document != null) { - loadState(result, document.getRootElement()); - } - if (useProvidersData && myStreamProvider != null && myStreamProvider.isEnabled()) { - for (RoamingType roamingType : RoamingType.values()) { - if (roamingType != RoamingType.DISABLED && roamingType != RoamingType.GLOBAL) { - try { - Document sharedDocument = StorageUtil.loadDocument(myStreamProvider.loadContent(myFileSpec, roamingType)); - if (sharedDocument != null) { - filterOutOfDate(sharedDocument.getRootElement()); - loadState(result, sharedDocument.getRootElement()); - } - } - catch (Exception e) { - LOG.warn(e); - } - } + boolean wasLoaded = false; + try { + wasLoaded = loadDataFromStreamProvider(result); + } + catch (Exception e) { + LOG.warn(e); + } + + //noinspection deprecation + if (wasLoaded && !myStreamProvider.isVersioningRequired() && !(myStreamProvider instanceof OldStreamProviderAdapter || myStreamProvider instanceof CurrentUserHolder)) { + // we don't use local data if stream provider has one (to preserve backward compatibility, we don't use this logic for old stream providers) + return result; } } + Element element = loadLocalData(); + if (element != null) { + loadState(result, element); + } + return result; } - protected void loadState(final StorageData result, final Element element) throws StateStorageException { - if (myPathMacroSubstitutor != null) { - myPathMacroSubstitutor.expandPaths(element); + private boolean loadDataFromStreamProvider(@NotNull StorageData result) throws IOException, JDOMException { + assert myStreamProvider != null; + InputStream inputStream = myStreamProvider.loadContent(myFileSpec, myRoamingType); + if (inputStream == null) { + return false; } - IdeaPluginDescriptorImpl.internJDOMElement(element); + Element element = JDOMUtil.loadDocument(inputStream).getRootElement(); + filterOutOfDate(element); + loadState(result, element); + return true; + } - try { - result.load(element); - result.checkUnknownMacros(myPathMacroSubstitutor); - } - catch (IOException e) { - throw new StateStorageException(e); - } + private void loadState(@NotNull StorageData result, @NotNull Element element) { + result.load(element, myPathMacroSubstitutor, true); } @NotNull @@ -182,25 +191,15 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { public void setDefaultState(final Element element) { myLoadedData = createStorageData(); - try { - loadState(myLoadedData, element); - } - catch (StateStorageException e) { - LOG.error(e); - } + loadState(myLoadedData, element); } @Override @NotNull public ExternalizationSession startExternalization() { - try { - final ExternalizationSession session = new MyExternalizationSession(getStorageData(false).clone(), myListener); - mySession = session; - return session; - } - catch (StateStorageException e) { - throw new RuntimeException(e); - } + ExternalizationSession session = new MyExternalizationSession(getStorageData().clone(), myListener); + mySession = session; + return session; } @Override @@ -226,19 +225,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @NotNull @Override - public Collection<IFile> getStorageFilesToSave() throws StateStorageException { + public Collection<File> getStorageFilesToSave() throws StateStorageException { return Collections.emptySet(); } @NotNull @Override - public List<IFile> getAllStorageFiles() { + public List<File> getAllStorageFiles() { return Collections.emptyList(); } }; } - protected abstract MySaveSession createSaveSession(final MyExternalizationSession externalizationSession); + protected abstract MySaveSession createSaveSession(MyExternalizationSession externalizationSession); @Override public void finishSave(@NotNull final SaveSession saveSession) { @@ -265,22 +264,26 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Override - public void setState(@NotNull final Object component, final String componentName, @NotNull final Object state, final Storage storageSpec) throws StateStorageException { + public void setState(@NotNull Object component, @NotNull String componentName, @NotNull Object state, @Nullable Storage storageSpec) { assert mySession == this; + Element element; try { - setState(componentName, DefaultStateSerializer.serializeState(state, storageSpec)); + element = DefaultStateSerializer.serializeState(state, storageSpec); } catch (WriteExternalException e) { LOG.debug(e); + return; } - } - private synchronized void setState(final String componentName, final Element element) { - if (element.getAttributes().isEmpty() && element.getChildren().isEmpty()) { + if (element == null || JDOMUtil.isEmpty(element)) { return; } + setState(componentName, element); + } + + private synchronized void setState(@NotNull String componentName, @NotNull Element element) { myStorageData.setState(componentName, element); int hash = JDOMUtil.getTreeHash(element); try { @@ -296,8 +299,12 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - protected Document getDocument(StorageData data) { - final Element element = data.save(); + @Nullable + protected Element getElement(@NotNull StorageData data) { + Element element = data.save(); + if (element == null || JDOMUtil.isEmpty(element)) { + return null; + } if (myPathMacroSubstitutor != null) { try { @@ -308,18 +315,18 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - return new Document(element); + return element; } protected abstract class MySaveSession implements SaveSession, SafeWriteRequestor { final StorageData myStorageData; - private Document myDocumentToSave; + private Element myElementToSave; public MySaveSession(MyExternalizationSession externalizationSession) { myStorageData = externalizationSession.myStorageData; } - public final boolean needsSave() throws StateStorageException { + public final boolean needsSave() { assert mySession == this; return _needsSave(calcHash()); } @@ -382,11 +389,10 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { try { if (myStreamProvider != null && myStreamProvider.isEnabled() && (myProviderUpToDateHash == -1 || myProviderUpToDateHash != hash)) { try { - //noinspection IfStatementWithIdenticalBranches - if (saveForProvider(myStreamProvider)) { - //noinspection UnnecessaryReturnStatement - return; - } + saveForProvider(); + } + catch (IOException e) { + LOG.warn(e); } finally { myProviderUpToDateHash = hash; @@ -398,9 +404,9 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - private void saveLocally(final Integer hash) { + private void saveLocally(int hash) { try { - if (!isHashUpToDate(hash) && _needsSave(hash)) { + if (!(myUpToDateHash != -1 && myUpToDateHash == hash) && _needsSave(hash)) { doSave(); } } @@ -409,15 +415,15 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - private boolean saveForProvider(@NotNull StreamProvider streamProvider) { - if (!streamProvider.isApplicable(myFileSpec, RoamingType.PER_USER)) { - return false; + private void saveForProvider() throws IOException { + if (!myStreamProvider.isApplicable(myFileSpec, myRoamingType)) { + return; } - Document document = getDocumentToSave(); - Element rootElement = document.getRootElement(); - if (rootElement.getChildren().isEmpty()) { - return false; + Element element = getElementToSave(); + if (element == null || element.getChildren().isEmpty()) { + myStreamProvider.delete(myFileSpec, myRoamingType); + return; } // skip the whole document if some component has disabled roaming type @@ -425,59 +431,34 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { // one exclusion: workspace file (you don't have choice in this case) // for example, it is important for ICS ProjectId - we cannot keep project in another place, // but this project id must not be shared - if (!myFileSpec.equals(StoragePathMacros.WORKSPACE_FILE) && - rootElement.getContent(new RoamingElementFilter(RoamingType.DISABLED)).iterator().hasNext()) { - return false; - } - - RoamingElementFilter perPlatformFilter = new RoamingElementFilter(RoamingType.PER_PLATFORM); - if (rootElement.getContent(perPlatformFilter).iterator().hasNext()) { - return doSaveForProvider(rootElement, new RoamingElementFilter(RoamingType.PER_USER)) || - doSaveForProvider(rootElement, perPlatformFilter); + if (myFileSpec.equals(StoragePathMacros.WORKSPACE_FILE)) { + Element copiedElement = JDOMUtil.cloneElement(element, DISABLED_ROAMING_ELEMENT_FILTER); + if (copiedElement != null) { + doSaveForProvider(copiedElement, DISABLED_ROAMING_ELEMENT_FILTER.myRoamingType, myStreamProvider); + } } else { - return doSaveForProvider(document, RoamingType.PER_USER, streamProvider); + doSaveForProvider(element, myRoamingType, myStreamProvider); } } - private boolean doSaveForProvider(Element element, RoamingElementFilter filter) { - Element copiedElement = JDOMUtil.cloneElement(element, filter); - return copiedElement != null && doSaveForProvider(new Document(copiedElement), filter.myRoamingType, myStreamProvider); - } - - private boolean doSaveForProvider(Document actualDocument, RoamingType roamingType, StreamProvider streamProvider) { - try { - boolean result = StorageUtil.doSendContent(streamProvider, myFileSpec, actualDocument, roamingType, true); - if (streamProvider.isVersioningRequired()) { - TObjectLongHashMap<String> versions = loadVersions(actualDocument.getRootElement().getChildren(StorageData.COMPONENT)); - if (!versions.isEmpty()) { - Document versionDoc = new Document(StateStorageManagerImpl.createComponentVersionsXml(versions)); - StorageUtil.doSendContent(streamProvider, myFileSpec + VERSION_FILE_SUFFIX, versionDoc, roamingType, true); - } + private void doSaveForProvider(@NotNull Element element, @NotNull RoamingType roamingType, @NotNull StreamProvider streamProvider) throws IOException { + StorageUtil.doSendContent(streamProvider, myFileSpec, element, roamingType, true); + if (streamProvider.isVersioningRequired()) { + TObjectLongHashMap<String> versions = loadVersions(element.getChildren(StorageData.COMPONENT)); + if (!versions.isEmpty()) { + Element versionDoc = StateStorageManagerImpl.createComponentVersionsXml(versions); + StorageUtil.doSendContent(streamProvider, myFileSpec + VERSION_FILE_SUFFIX, versionDoc, roamingType, true); } - return result; - } - catch (IOException e) { - LOG.warn(e); - return false; } } - private boolean isHashUpToDate(final Integer hash) { - return myUpToDateHash != -1 && myUpToDateHash == hash; - } - - protected Document getDocumentToSave() { - if (myDocumentToSave != null) return myDocumentToSave; - - final Element element = myStorageData.save(); - myDocumentToSave = new Document(element); - - if (myPathMacroSubstitutor != null) { - myPathMacroSubstitutor.collapsePaths(element); + @Nullable + protected Element getElementToSave() { + if (myElementToSave == null) { + myElementToSave = getElement(myStorageData); } - - return myDocumentToSave; + return myElementToSave; } public StorageData getData() { @@ -488,12 +469,12 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Nullable public Set<String> analyzeExternalChanges(@NotNull final Set<Pair<VirtualFile,StateStorage>> changedFiles) { try { - Document document = loadDocument(); + Element element = loadLocalData(); StorageData storageData = createStorageData(); - if (document == null) { + if (element == null) { return Collections.emptySet(); } - loadState(storageData, document.getRootElement()); + loadState(storageData, element); return storageData.getDifference(myStorageData, myPathMacroSubstitutor); } catch (StateStorageException e) { @@ -502,21 +483,6 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { return null; } - - private class RoamingElementFilter extends ElementFilter { - final RoamingType myRoamingType; - - public RoamingElementFilter(RoamingType roamingType) { - super(StorageData.COMPONENT); - - myRoamingType = roamingType; - } - - @Override - public boolean matches(Object obj) { - return super.matches(obj) && myComponentRoamingManager.getRoamingType(((Element)obj).getAttributeValue(StorageData.NAME)) == myRoamingType; - } - } } private TObjectLongHashMap<String> loadVersions(List<Element> elements) { @@ -545,11 +511,9 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Override public void reload(@NotNull final Set<String> changedComponents) throws StateStorageException { final StorageData storageData = loadData(false); - final StorageData oldLoadedData = myLoadedData; - if (oldLoadedData != null) { - Set<String> componentsToRetain = new HashSet<String>(oldLoadedData.myComponentStates.keySet()); + Set<String> componentsToRetain = new THashSet<String>(oldLoadedData.myComponentStates.keySet()); componentsToRetain.addAll(changedComponents); // add empty configuration tags for removed components @@ -568,7 +532,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { myLoadedData = storageData; } - private void filterOutOfDate(Element element) { + private void filterOutOfDate(@NotNull Element element) { if (myRemoteVersionProvider == null) { return; } @@ -587,12 +551,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Nullable - Document logComponents() throws StateStorageException { - return mySession instanceof MySaveSession ? getDocument(((MySaveSession)mySession).myStorageData) : null; + Element logComponents() { + return mySession instanceof MySaveSession ? getElement(((MySaveSession)mySession).myStorageData) : null; } - protected class RemoteComponentVersionProvider implements ComponentVersionProvider { - protected TObjectLongHashMap<String> myProviderVersions; + public void resetProviderCache() { + myProviderUpToDateHash = -1; + if (myRemoteVersionProvider != null) { + myRemoteVersionProvider.myProviderVersions = null; + } + } + + private final class RemoteComponentVersionProvider implements ComponentVersionProvider { + private TObjectLongHashMap<String> myProviderVersions; @Override public long getVersion(String name) { @@ -632,4 +603,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } } + + private static class RoamingElementFilter extends ElementFilter { + final RoamingType myRoamingType; + + public RoamingElementFilter(RoamingType roamingType) { + super(StorageData.COMPONENT); + + myRoamingType = roamingType; + } + + @Override + public boolean matches(Object obj) { + return super.matches(obj) && ComponentRoamingManager.getInstance().getRoamingType(((Element)obj).getAttributeValue(StorageData.NAME)) == myRoamingType; + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java b/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java index b508686ade00..369acf473cbc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java @@ -26,6 +26,7 @@ import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileInputStream; @@ -57,13 +58,13 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { } @Override - public void processExternalCommandLine(String[] args) { + public void processExternalCommandLine(String[] args, @Nullable String currentDirectory) { if (!checkArguments(args)) { Messages.showMessageDialog(getUsageMessage(), StringUtil.toTitleCase(getCommandName()), Messages.getInformationIcon()); return; } try { - processCommand(args); + processCommand(args, currentDirectory); } catch (Exception e) { Messages.showMessageDialog(String.format("Error showing %s: %s", getCommandName(), e.getMessage()), @@ -86,7 +87,7 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { public abstract String getUsageMessage(); - protected abstract void processCommand(String[] args) throws Exception; + protected abstract void processCommand(String[] args, @Nullable String currentDirectory) throws Exception; @Override public void premain(String[] args) { @@ -99,7 +100,7 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { @Override public void main(String[] args) { try { - processCommand(args); + processCommand(args, null); } catch (Exception e) { e.printStackTrace(); @@ -116,12 +117,12 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { System.exit(0); } - public static VirtualFile findOrCreateFile(String path) throws IOException { + public static VirtualFile findOrCreateFile(String path, @Nullable String currentDirectory) throws IOException { final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(path)); if (file == null) { boolean result = new File(path).createNewFile(); if (result) { - return findFile(path); + return findFile(path, currentDirectory); } else { throw new FileNotFoundException("Can't create file " + path); @@ -167,11 +168,10 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { } @NotNull - public static VirtualFile findFile(final String path) throws OperationFailedException { + public static VirtualFile findFile(final String path, @Nullable String currentDirectory) throws OperationFailedException { File ioFile = new File(path); - if (!ioFile.exists()) { - final String dir = PathManager.getOriginalWorkingDir(); - ioFile = new File(dir + File.separator + path); + if (!ioFile.isAbsolute() && currentDirectory != null) { + ioFile = new File(currentDirectory, path); } final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile); if (file == null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java b/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java index 742a405f350e..143df9617310 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java @@ -21,6 +21,7 @@ import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.fileTypes.UnknownFileType; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; /** * @author max @@ -37,11 +38,11 @@ public class DiffApplication extends ApplicationStarterBase { return DiffBundle.message("diff.application.usage.parameters.and.description", scriptName); } - public void processCommand(String[] args) throws OperationFailedException { + public void processCommand(String[] args, @Nullable String currentDirectory) throws OperationFailedException { final String path1 = args[1]; final String path2 = args[2]; - final VirtualFile file1 = findFile(path1); - final VirtualFile file2 = findFile(path2); + final VirtualFile file1 = findFile(path1, currentDirectory); + final VirtualFile file2 = findFile(path2, currentDirectory); final boolean areDirs = areDirs(file1, file2); final boolean areJars = areJars(file1, file2); if (areDirs || areJars) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java b/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java index 0c088929ea49..d1d6cf6cb9f5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java @@ -18,6 +18,7 @@ package com.intellij.openapi.diff; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; /** * @author Konstantin Bulenkov @@ -34,11 +35,11 @@ public class MergeApplication extends ApplicationStarterBase { } @Override - protected void processCommand(String[] args) throws Exception { - final VirtualFile left = findFile(args[1]); - final VirtualFile right = findFile(args[2]); - final VirtualFile middle = findFile(args[3]); - final VirtualFile result = findOrCreateFile(args.length == 4 ? args[3] : args[4]); + protected void processCommand(String[] args, @Nullable String currentDirectory) throws Exception { + final VirtualFile left = findFile(args[1], currentDirectory); + final VirtualFile right = findFile(args[2], currentDirectory); + final VirtualFile middle = findFile(args[3], currentDirectory); + final VirtualFile result = findOrCreateFile(args.length == 4 ? args[3] : args[4], currentDirectory); MergeRequest request = DiffRequestFactory.getInstance() .createMergeRequest(getText(left), getText(right), getText(middle), result, diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/ScrollToTheEndToolbarAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/ScrollToTheEndToolbarAction.java index cc1be90ef046..dab14bdadcc2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/ScrollToTheEndToolbarAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/ScrollToTheEndToolbarAction.java @@ -48,7 +48,7 @@ public class ScrollToTheEndToolbarAction extends DumbAwareAction { Rectangle visibleArea = myEditor.getScrollingModel().getVisibleArea(); Dimension size = myEditor.getContentComponent().getSize(); boolean isEndVisible = visibleArea.y + visibleArea.height >= size.height; - boolean isOnLastLine = document.getLineNumber(caretOffset) == document.getLineCount() - 1; + boolean isOnLastLine = document.getLineCount() == 0 || document.getLineNumber(caretOffset) == document.getLineCount() - 1; e.getPresentation().setEnabled(!isEndVisible || !isOnLastLine); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java index cf3c196a9d78..0d0377bdf7fe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java @@ -382,7 +382,7 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Name } @Override - public Document writeScheme(@NotNull final EditorColorsSchemeImpl scheme) { + public Element writeScheme(@NotNull final EditorColorsSchemeImpl scheme) { Element root = new Element(SCHEME_NODE_NAME); try { scheme.writeExternal(root); @@ -392,7 +392,7 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Name return null; } - return new Document(root); + return root; } @Override 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 51f6ec4a8cdb..165429ce56df 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 @@ -81,10 +81,7 @@ import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.text.CharArrayCharSequence; import com.intellij.util.text.CharArrayUtil; -import com.intellij.util.ui.ButtonlessScrollBarUI; -import com.intellij.util.ui.GraphicsUtil; -import com.intellij.util.ui.MacUIUtil; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.*; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; import gnu.trove.TIntArrayList; @@ -4106,7 +4103,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } private void processMouseDragged(@NotNull MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { + if (JBSwingUtilities.isRightMouseButton(e)) { return; } 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 index 5a9c704173aa..c4b9a38914cf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java @@ -56,7 +56,7 @@ public class LazyRangeMarkerFactoryImpl extends LazyRangeMarkerFactory { private void transformRangeMarkers(@NotNull DocumentEvent e) { Document document = e.getDocument(); VirtualFile file = fileDocumentManager.getFile(document); - if (file == null) { + if (file == null || myProject.isDisposed()) { return; } 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 731d1e26a289..574780a661c2 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 @@ -24,6 +24,7 @@ */ package com.intellij.openapi.editor.impl; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; @@ -36,6 +37,7 @@ import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; @@ -464,6 +466,16 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi mySelectionListeners.add(listener); } + public void addSelectionListener(final SelectionListener listener, Disposable parent) { + mySelectionListeners.add(listener); + Disposer.register(parent, new Disposable() { + @Override + public void dispose() { + mySelectionListeners.remove(listener); + } + }); + } + @Override public void removeSelectionListener(SelectionListener listener) { boolean success = mySelectionListeners.remove(listener); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileChooserDialogImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileChooserDialogImpl.java index d585ec5a7b7d..2938f419909c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileChooserDialogImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileChooserDialogImpl.java @@ -114,16 +114,32 @@ public class FileChooserDialogImpl extends DialogWrapper implements FileChooserD @Override @NotNull - public VirtualFile[] choose(@Nullable VirtualFile toSelect, Project project) { + public VirtualFile[] choose(@Nullable final Project project, @NotNull final VirtualFile... toSelect) { init(); - if (myProject == null && project != null) { + if ((myProject == null) && (project != null)) { myProject = project; } - restoreSelection(toSelect); + if (toSelect.length == 1) { + restoreSelection(toSelect[0]); + } + else if (toSelect.length == 0) { + restoreSelection(null); // select last opened file + } + else { + selectInTree(toSelect, true); + } + show(); return myChosenFiles; } + + @NotNull + @Override + public VirtualFile[] choose(@Nullable final VirtualFile toSelect, @Nullable final Project project) { + return choose(project, toSelect); + } + @Override public void choose(@Nullable VirtualFile toSelect, @NotNull Consumer<List<VirtualFile>> callback) { init(); @@ -163,7 +179,6 @@ public class FileChooserDialogImpl extends DialogWrapper implements FileChooserD if (file != null && file.getFileSystem() instanceof LocalFileSystem) { saveRecent(file.getPath()); } - } protected void saveRecent(String path) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java index 30eb78cf706f..c074a4965493 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java @@ -164,6 +164,7 @@ public abstract class FileEditorManagerEx extends FileEditorManager implements B public abstract boolean isInsideChange(); + @Override @Nullable public final Object getData(@NotNull String dataId, @NotNull Editor editor, @NotNull Caret caret) { for (final EditorDataProvider dataProvider : myDataProviders) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java index 48a5b7c9a4dc..d8a704d891bf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.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. @@ -66,7 +66,7 @@ public abstract class EditorComposite implements Disposable { /** * File for which composite is created */ - private final VirtualFile myFile; + @NotNull private final VirtualFile myFile; /** * Whether the composite is pinned or not */ diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java index 4807812e5047..101f4a7ddfed 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java @@ -54,9 +54,12 @@ public class EditorEmptyTextPainter { UIUtil.TextPainter painter = new UIUtil.TextPainter().withLineSpacing(1.5f); painter.withShadow(true, new JBColor(Gray._200.withAlpha(100), Gray._0.withAlpha(255))); - painter.appendLine("No files are open").underlined(new JBColor(Gray._150, Gray._180)); + painter.appendLine("No files are open"); - advertiseActions(splitters, painter); + if (!splitters.isPreview()) { + painter.underlined(new JBColor(Gray._150, Gray._180)); + advertiseActions(splitters, painter); + } painter.draw(g, new PairFunction<Integer, Integer, Couple<Integer>>() { @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java index 32169662ba2a..ddd5395805ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java @@ -287,7 +287,7 @@ public final class EditorHistoryManager extends AbstractProjectComponent impleme } } - public FileEditorState getState(final VirtualFile file, final FileEditorProvider provider) { + public FileEditorState getState(@NotNull VirtualFile file, final FileEditorProvider provider) { validateEntries(); final HistoryEntry entry = getEntry(file); return entry != null ? entry.getState(provider) : null; @@ -302,7 +302,7 @@ public final class EditorHistoryManager extends AbstractProjectComponent impleme return entry != null ? entry.mySelectedProvider : null; } - private HistoryEntry getEntry(final VirtualFile file){ + private HistoryEntry getEntry(@NotNull VirtualFile file){ validateEntries(); for (int i = myEntriesList.size() - 1; i >= 0; i--) { final HistoryEntry entry = myEntriesList.get(i); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.java index babf76dd3709..58f5470aca07 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.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. @@ -18,6 +18,7 @@ package com.intellij.openapi.fileEditor.impl; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; @@ -29,5 +30,5 @@ public interface EditorTabColorProvider { ExtensionPointName<EditorTabColorProvider> EP_NAME = ExtensionPointName.create("com.intellij.editorTabColorProvider"); @Nullable - Color getEditorTabColor(Project project, VirtualFile file); + Color getEditorTabColor(@NotNull Project project, @NotNull VirtualFile file); } 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 03b03faffe03..3fa0f751e9cb 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 @@ -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. @@ -381,7 +381,7 @@ public final class EditorTabbedContainer implements Disposable, CloseAction.Clos } @Nullable - public static Color calcTabColor(final Project project, final VirtualFile file) { + public static Color calcTabColor(@NotNull Project project, @NotNull VirtualFile file) { for (EditorTabColorProvider provider : Extensions.getExtensions(EditorTabColorProvider.EP_NAME)) { final Color result = provider.getEditorTabColor(project, file); if (result != null) { 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 06a3966ade1d..1bf86ea5d62c 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 @@ -287,7 +287,7 @@ public class EditorWindow { } } - public void closeFile(final VirtualFile file, final boolean disposeIfNeeded, final boolean transferFocus) { + public void closeFile(@NotNull final VirtualFile file, final boolean disposeIfNeeded, final boolean transferFocus) { final FileEditorManagerImpl editorManager = getManager(); editorManager.runChange(new FileEditorManagerChange() { @Override @@ -525,7 +525,7 @@ public class EditorWindow { myOwner.setCurrentWindow(this, requestFocus); } - public void updateFileBackgroundColor(final VirtualFile file) { + public void updateFileBackgroundColor(@NotNull VirtualFile file) { final int index = findEditorIndex(findFileComposite(file)); if (index != -1) { final Color color = EditorTabbedContainer.calcTabColor(getManager().getProject(), file); @@ -569,10 +569,10 @@ public class EditorWindow { } protected static class TComp extends JPanel implements DataProvider, EditorWindowHolder { - final EditorWithProviderComposite myEditor; + @NotNull final EditorWithProviderComposite myEditor; protected final EditorWindow myWindow; - TComp(final EditorWindow window, final EditorWithProviderComposite editor) { + TComp(@NotNull EditorWindow window, @NotNull EditorWithProviderComposite editor) { super(new BorderLayout()); myEditor = editor; myWindow = window; @@ -594,6 +594,7 @@ public class EditorWindow { }); } + @NotNull @Override public EditorWindow getEditorWindow() { return myWindow; @@ -613,7 +614,7 @@ public class EditorWindow { } protected static class TCompForTablessMode extends TComp implements CloseAction.CloseTarget { - TCompForTablessMode(final EditorWindow window, final EditorWithProviderComposite editor) { + TCompForTablessMode(@NotNull EditorWindow window, @NotNull EditorWithProviderComposite editor) { super(window, editor); } @@ -701,6 +702,7 @@ public class EditorWindow { public void setEditor(@Nullable final EditorWithProviderComposite editor, final boolean selectEditor, final boolean focusEditor) { if (editor != null) { + onBeforeSetEditor(editor.getFile()); if (myTabbedPane == null) { myPanel.removeAll (); myPanel.add (new TCompForTablessMode(this, editor), BorderLayout.CENTER); @@ -741,6 +743,9 @@ public class EditorWindow { myOwner.validate(); } + protected void onBeforeSetEditor(VirtualFile file) { + } + private boolean splitAvailable() { return getTabCount() >= 1; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java index c708d058b63b..2d27bb853531 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.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. @@ -15,9 +15,12 @@ */ package com.intellij.openapi.fileEditor.impl; +import org.jetbrains.annotations.NotNull; + /** * @author Konstantin Bulenkov */ public interface EditorWindowHolder { + @NotNull EditorWindow getEditorWindow(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index b9e38809faab..c380d38a5db9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -51,6 +51,7 @@ import com.intellij.util.Alarm; import com.intellij.util.containers.ArrayListSet; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; +import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -76,7 +77,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener private static final Key<Object> DUMMY_KEY = Key.create("EditorsSplitters.dummy.key"); - private final static EditorEmptyTextPainter ourPainter = ServiceManager.getService(EditorEmptyTextPainter.class); + private static final EditorEmptyTextPainter ourPainter = ServiceManager.getService(EditorEmptyTextPainter.class); private EditorWindow myCurrentWindow; final Set<EditorWindow> myWindows = new CopyOnWriteArraySet<EditorWindow>(); @@ -346,22 +347,18 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener return virtualFiles; } - @NotNull public FileEditor[] getSelectedEditors() { - final List<FileEditor> editors = new ArrayList<FileEditor>(); + @NotNull + public FileEditor[] getSelectedEditors() { + List<FileEditor> editors = new ArrayList<FileEditor>(); + Set<EditorWindow> windows = new THashSet<EditorWindow>(myWindows); final EditorWindow currentWindow = getCurrentWindow(); if (currentWindow != null) { - final EditorWithProviderComposite composite = currentWindow.getSelectedEditor(); - if (composite != null) { - editors.add (composite.getSelectedEditor()); - } + windows.add(currentWindow); } - - for (final EditorWindow window : myWindows) { - if (!window.equals(currentWindow)) { - final EditorWithProviderComposite composite = window.getSelectedEditor(); - if (composite != null) { - editors.add(composite.getSelectedEditor()); - } + for (final EditorWindow window : windows) { + final EditorWithProviderComposite composite = window.getSelectedEditor(); + if (composite != null) { + editors.add(composite.getSelectedEditor()); } } return editors.toArray(new FileEditor[editors.size()]); @@ -463,7 +460,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener myCurrentWindow = currentWindow; } - public void updateFileBackgroundColor(final VirtualFile file) { + public void updateFileBackgroundColor(@NotNull VirtualFile file) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++ i) { windows [i].updateFileBackgroundColor(file); @@ -614,10 +611,14 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener public void createCurrentWindow() { LOG.assertTrue(myCurrentWindow == null); - setCurrentWindow(new EditorWindow(this)); + setCurrentWindow(createEditorWindow()); add(myCurrentWindow.myPanel, BorderLayout.CENTER); } + protected EditorWindow createEditorWindow() { + return new EditorWindow(this); + } + /** * sets the window passed as a current ('focused') window among all splitters. All file openings will be done inside this * current window @@ -625,7 +626,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener * @param requestFocus whether to request focus to the editor currently selected in this window */ public void setCurrentWindow(@Nullable final EditorWindow window, final boolean requestFocus) { - final EditorWithProviderComposite newEditor = window != null? window.getSelectedEditor() : null; + final EditorWithProviderComposite newEditor = window == null ? null : window.getSelectedEditor(); Runnable fireRunnable = new Runnable() { @Override @@ -655,7 +656,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener //--------------------------------------------------------- public EditorWithProviderComposite[] getEditorsComposites() { - final ArrayList<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>(); + List<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>(); for (final EditorWindow myWindow : myWindows) { final EditorWithProviderComposite[] editors = myWindow.getEditors(); @@ -667,8 +668,8 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener //--------------------------------------------------------- @NotNull - public List<EditorWithProviderComposite> findEditorComposites(final VirtualFile file) { - final ArrayList<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>(); + public List<EditorWithProviderComposite> findEditorComposites(@NotNull VirtualFile file) { + List<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>(); for (final EditorWindow window : myWindows) { final EditorWithProviderComposite fileComposite = window.findFileComposite(file); if (fileComposite != null) { @@ -680,7 +681,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener @NotNull public List<EditorWindow> findWindows(final VirtualFile file) { - final ArrayList<EditorWindow> res = new ArrayList<EditorWindow>(); + List<EditorWindow> res = new ArrayList<EditorWindow>(); for (final EditorWindow window : myWindows) { if (window.findFileComposite(file) != null) { res.add(window); @@ -694,7 +695,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener } @NotNull public EditorWindow[] getOrderedWindows() { - final ArrayList<EditorWindow> res = new ArrayList<EditorWindow>(); + final List<EditorWindow> res = new ArrayList<EditorWindow>(); // Collector for windows in tree ordering: class Inner{ @@ -744,6 +745,10 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener return false; } + public boolean isPreview() { + return false; + } + private final class MyFocusWatcher extends FocusWatcher { @Override protected void focusedComponentChanged(final Component component, final AWTEvent cause) { @@ -816,8 +821,10 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener return processFiles(children, context); } - protected abstract @Nullable T processFiles(@NotNull List<Element> fileElements, @Nullable T context); - protected abstract @Nullable T processSplitter(@NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable T context); + @Nullable + protected abstract T processFiles(@NotNull List<Element> fileElements, @Nullable T context); + @Nullable + protected abstract T processSplitter(@NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable T context); } private class UIBuilder extends ConfigTreeReader<JPanel> { @@ -828,7 +835,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { - windowRef.set(context == null ? new EditorWindow(EditorsSplitters.this) : findWindowWith(context)); + windowRef.set(context == null ? createEditorWindow() : findWindowWith(context)); } }); final EditorWindow window = windowRef.get(); 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 65adf52041b9..abf16d54c43d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -58,7 +58,6 @@ import com.intellij.openapi.vcs.FileStatusListener; import com.intellij.openapi.vcs.FileStatusManager; import com.intellij.openapi.vfs.*; import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; @@ -91,7 +90,6 @@ import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.*; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; /** * @author Anton Katilin @@ -113,7 +111,6 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec private final Project myProject; private final List<Pair<VirtualFile, EditorWindow>> mySelectionHistory = new ArrayList<Pair<VirtualFile, EditorWindow>>(); private WeakReference<EditorComposite> myLastSelectedComposite = new WeakReference<EditorComposite>(null); - private final AtomicBoolean myPreviewBlocker = new AtomicBoolean(false); private final MergingUpdateQueue myQueue = new MergingUpdateQueue("FileEditorManagerUpdateQueue", 50, true, null); @@ -179,9 +176,9 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public Set<EditorsSplitters> getAllSplitters() { HashSet<EditorsSplitters> all = new LinkedHashSet<EditorsSplitters>(); - if (Registry.is("editor.use.preview")) { - initUI(); - all.add(myPreviewPanel.getWindow().getOwner()); + EditorWindow previewWindow = getPreviewWindow(); + if (previewWindow != null) { + all.add(previewWindow.getOwner()); } all.add(getMainSplitters()); Set<DockContainer> dockContainers = myDockManager.getContainers(); @@ -255,16 +252,23 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } } } - if (myPreviewPanel == null && Registry.is("editor.use.preview")) { + if (myPreviewPanel == null && PreviewPanel.isAvailable()) { synchronized (myInitLock) { myPreviewPanel = new PreviewPanel(myProject, this, myDockManager); } } } + @Nullable + private EditorWindow getPreviewWindow() { + if (!PreviewPanel.isAvailable()) return null; + initUI(); + return myPreviewPanel.getWindow(); + } + private static class MyBorder implements Border { @Override - public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + public void paintBorder(@NotNull Component c, @NotNull Graphics g, int x, int y, int width, int height) { if (UIUtil.isUnderAquaLookAndFeel()) { g.setColor(JBTabsImpl.MAC_AQUA_BG_COLOR); final Insets insets = getBorderInsets(c); @@ -274,6 +278,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } } + @NotNull @Override public Insets getBorderInsets(Component c) { return JBInsets.NONE; @@ -607,7 +612,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @Override @NotNull public Pair<FileEditor[], FileEditorProvider[]> openFileWithProviders(@NotNull final VirtualFile file, - final boolean focusEditor, + boolean focusEditor, final boolean searchForSplitter) { if (!file.isValid()) { throw new IllegalArgumentException("file is not valid: " + file); @@ -642,9 +647,10 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } if (wndToOpenIn == null || !wndToOpenIn.isFileOpen(file)) { - EditorWindow previewWindow = getPreviewWindow(file, focusEditor, searchForSplitter); + EditorWindow previewWindow = getPreviewWindow(); if (previewWindow != null) { wndToOpenIn = previewWindow; + focusEditor = true; } } @@ -658,31 +664,6 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return openFileImpl2(wndToOpenIn, file, focusEditor); } - @Nullable - private EditorWindow getPreviewWindow(@NotNull VirtualFile virtualFile, final boolean focusEditor, final boolean searchForSplitter) { - EditorWindow wndToOpenIn = null; - if (Registry.is("editor.use.preview") && !myPreviewBlocker.get()) { - wndToOpenIn = myPreviewPanel.getWindow(); - if (virtualFile.equals(myPreviewPanel.getCurrentFile())) return wndToOpenIn; - final VirtualFile modifiedFile = myPreviewPanel.closeCurrentFile(); - ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).activate(null, false); - if (modifiedFile != null) { - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - @Override - public void run() { - myPreviewBlocker.set(true); - try { - openFileWithProviders(modifiedFile, focusEditor, searchForSplitter); - } finally { - myPreviewBlocker.set(false); - } - } - }, "", null); - } - } - return wndToOpenIn; - } - public Pair<FileEditor[], FileEditorProvider[]> openFileInNewWindow(@NotNull VirtualFile file) { return ((DockManagerImpl)DockManager.getInstance(getProject())).createNewDockContainerFor(file, this); } @@ -805,7 +786,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec final FileEditorProvider provider = newProviders[i]; LOG.assertTrue(provider != null, "Provider for file "+file+" is null. All providers: "+Arrays.asList(newProviders)); LOG.assertTrue(provider.accept(myProject, file), "Provider " + provider + " doesn't accept file " + file); - if ((provider instanceof AsyncFileEditorProvider)) { + if (provider instanceof AsyncFileEditorProvider) { builders[i] = ((AsyncFileEditorProvider)provider).createEditorAsync(myProject, file); } } @@ -944,7 +925,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return Pair.create(compositeRef.get().getEditors(), compositeRef.get().getProviders()); } - private void clearWindowIfNeeded(EditorWindow window) { + private static void clearWindowIfNeeded(EditorWindow window) { if (UISettings.getInstance().EDITOR_TAB_PLACEMENT == UISettings.TABS_NONE || UISettings.getInstance().PRESENTATION_MODE) { for (EditorWithProviderComposite composite : window.getEditors()) { Disposer.dispose(composite); @@ -1177,9 +1158,15 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @Override public Editor getSelectedTextEditor() { - assertReadAccess(); + return getSelectedTextEditor(false); + } - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + public Editor getSelectedTextEditor(boolean lockfree) { + if (!lockfree) { + assertDispatchThread(); + } + + final EditorWindow currentWindow = lockfree ? getMainSplitters().getCurrentWindow() : getSplitters().getCurrentWindow(); if (currentWindow != null) { final EditorWithProviderComposite selectedEditor = currentWindow.getSelectedEditor(); if (selectedEditor != null && selectedEditor.getSelectedEditor() instanceof TextEditor) { @@ -1191,6 +1178,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } + @Override public boolean isFileOpen(@NotNull final VirtualFile file) { return !getEditorComposites(file).isEmpty(); @@ -1760,7 +1748,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec private final class MyEditorPropertyChangeListener implements PropertyChangeListener { @Override - public void propertyChange(final PropertyChangeEvent e) { + public void propertyChange(@NotNull final PropertyChangeEvent e) { assertDispatchThread(); final String propertyName = e.getPropertyName(); @@ -1999,6 +1987,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec mySelectionHistory.remove(Pair.create(file, window)); } + @NotNull @Override public ActionCallback getReady(@NotNull Object requestor) { return myBusyObject.getReady(requestor); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java index 752ab1228c3e..0e4f057dbeaf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java @@ -145,16 +145,12 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec } public static class RecentlyChangedFilesState { - @Transient private List<String> CHANGED_PATHS = new ArrayList<String>(); + @Transient private final List<String> CHANGED_PATHS = new ArrayList<String>(); public List<String> getChangedFiles() { return CHANGED_PATHS; } - public void setChangedFiles(List<String> changed) { - CHANGED_PATHS = changed; - } - public void register(VirtualFile file) { final String path = file.getPath(); CHANGED_PATHS.remove(path); @@ -275,7 +271,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec myCurrentChangePlace = placeInfo; if (!myChangePlaces.isEmpty()) { - final PlaceInfo lastInfo = myChangePlaces.get(myChangePlaces.size() - 1); + final PlaceInfo lastInfo = myChangePlaces.getLast(); if (isSame(placeInfo, lastInfo)) { myChangePlaces.removeLast(); } @@ -430,7 +426,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec } } - private static boolean removeInvalidFilesFrom(final LinkedList<PlaceInfo> backPlaces) { + private static boolean removeInvalidFilesFrom(@NotNull List<PlaceInfo> backPlaces) { boolean removed = false; for (Iterator<PlaceInfo> iterator = backPlaces.iterator(); iterator.hasNext();) { PlaceInfo info = iterator.next(); @@ -456,7 +452,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec myEditorManager.setSelectedEditor(info.getFile(), info.getEditorTypeId()); - final FileEditor [] editors = editorsWithProviders.getFirst(); + final FileEditor[] editors = editorsWithProviders.getFirst(); final FileEditorProvider[] providers = editorsWithProviders.getSecond(); for (int i = 0; i < editors.length; i++) { String typeId = providers [i].getEditorTypeId(); @@ -483,7 +479,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return new PlaceInfo(file, state, fileProvider.getEditorTypeId(), myEditorManager.getCurrentWindow()); } - private static void clearPlaceList(LinkedList<PlaceInfo> list) { + private static void clearPlaceList(@NotNull List<PlaceInfo> list) { list.clear(); } @@ -494,9 +490,9 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return "IdeDocumentHistory"; } - private static void putLastOrMerge(LinkedList<PlaceInfo> list, PlaceInfo next, int limitSizeLimit) { + private static void putLastOrMerge(@NotNull LinkedList<PlaceInfo> list, @NotNull PlaceInfo next, int limitSizeLimit) { if (!list.isEmpty()) { - PlaceInfo prev = list.get(list.size() - 1); + PlaceInfo prev = list.getLast(); if (isSame(prev, next)) { list.removeLast(); } @@ -522,7 +518,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec private final String myEditorTypeId; private final WeakReference<EditorWindow> myWindow; - public PlaceInfo(@NotNull VirtualFile file, FileEditorState navigationState, String editorTypeId, @Nullable EditorWindow window) { + public PlaceInfo(@NotNull VirtualFile file, @NotNull FileEditorState navigationState, @NotNull String editorTypeId, @Nullable EditorWindow window) { myNavigationState = navigationState; myFile = file; myEditorTypeId = editorTypeId; @@ -533,6 +529,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return myWindow.get(); } + @NotNull public FileEditorState getNavigationState() { return myNavigationState; } @@ -542,24 +539,23 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return myFile; } + @NotNull public String getEditorTypeId() { return myEditorTypeId; } + @Override public String toString() { return getFile().getName() + " " + getNavigationState(); } } - public LinkedList<PlaceInfo> getBackPlaces() { + @NotNull + public List<PlaceInfo> getBackPlaces() { return myBackPlaces; } - public LinkedList<PlaceInfo> getForwardPlaces() { - return myForwardPlaces; - } - @Override public final void initComponent() { } @@ -572,7 +568,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec myCmdProcessor.executeCommand(myProject, runnable, name, groupId); } - private static boolean isSame(PlaceInfo first, PlaceInfo second) { + private static boolean isSame(@NotNull PlaceInfo first, @NotNull PlaceInfo second) { if (first.getFile().equals(second.getFile())) { FileEditorState firstState = first.getNavigationState(); FileEditorState secondState = second.getNavigationState(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java index a9634d5a335a..999fe75523e9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java @@ -17,162 +17,276 @@ package com.intellij.openapi.fileEditor.impl; import com.intellij.icons.AllIcons; import com.intellij.ide.ui.UISettings; +import com.intellij.ide.ui.UISettingsListener; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.editor.event.DocumentListener; -import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManagerListener; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.ToolWindowAnchor; -import com.intellij.openapi.wm.ToolWindowId; -import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.*; import com.intellij.openapi.wm.impl.ToolWindowImpl; -import com.intellij.ui.JBColor; +import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; +import com.intellij.ui.content.Content; +import com.intellij.ui.content.ContentManager; +import com.intellij.ui.content.ContentManagerAdapter; +import com.intellij.ui.content.ContentManagerEvent; import com.intellij.ui.docking.DockManager; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.util.ArrayList; +import java.util.EnumSet; -class PreviewPanel extends JPanel implements DocumentListener, FileEditorManagerListener.Before { +class PreviewPanel extends JPanel { + + private CardLayout myLayout; + + enum ContentType {Files, Usages, Diagrams, Documentation} + + private static final Key<VirtualFile> FILE_KEY = Key.create("v_file"); private static final int HISTORY_LIMIT = 10; private final Project myProject; private final FileEditorManagerImpl myManager; private final DockManager myDockManager; private EditorWindow myWindow; - private boolean myInitialized = false; private EditorsSplitters myEditorsSplitters; private ArrayList<VirtualFile> myHistory = new ArrayList<VirtualFile>(); private VirtualFile myModifiedFile = null; private ToolWindowImpl myToolWindow; private VirtualFile myAwaitingForOpen = null; + private ContentManager myContentManager; + private Content myStubContent; + private boolean myBlocked = false; + + private EnumSet<ContentType> myTypes = EnumSet.noneOf(ContentType.class); + + static boolean isAvailable() { + return UISettings.getInstance().NAVIGATE_TO_PREVIEW; + } - public PreviewPanel(Project project, FileEditorManagerImpl manager, DockManager dockManager) { + PreviewPanel(Project project, FileEditorManagerImpl manager, DockManager dockManager) { myProject = project; myManager = manager; myDockManager = dockManager; - setOpaque(true); - setBackground(JBColor.DARK_GRAY); + } + + /* + * @return null if preview is not avalable + */ + @Nullable + EditorWindow getWindow() { + if (!isAvailable() || isBlocked() || myProject.isDisposed()) return null; + initToolWindowIfNeed(); + return myWindow; + } + + boolean isBlocked() { + return myBlocked; } private void initToolWindowIfNeed() { - if (myInitialized) return; + if (!isAvailable() || ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW) != null) return; myToolWindow = (ToolWindowImpl)ToolWindowManager.getInstance(myProject) .registerToolWindow(ToolWindowId.PREVIEW, this, ToolWindowAnchor.RIGHT, myProject, false); - myToolWindow.setIcon(AllIcons.Actions.PreviewDetails); - - myEditorsSplitters = new EditorsSplitters(myManager, myDockManager, false) { + UISettings.getInstance().addUISettingsListener(new UISettingsListener() { @Override - public void updateFileName(VirtualFile updatedFile) { - super.updateFileName(updatedFile); - if (updatedFile != null && updatedFile.equals(getCurrentFile())) { - updateWindowTitle(updatedFile); - } - } - - @Override - protected void afterFileOpen(VirtualFile file) { - if (file.equals(myAwaitingForOpen)) { - updateWindowTitle(file); - Document document = FileDocumentManager.getInstance().getDocument(file); - if (document != null) { - myModifiedFile = null; - document.addDocumentListener(PreviewPanel.this, myProject); + public void uiSettingsChanged(UISettings source) { + if (!isAvailable()) { + VirtualFile[] files = myWindow.getFiles(); + for (VirtualFile file : files) { + close(file); } + ToolWindowManager.getInstance(myProject).unregisterToolWindow(ToolWindowId.PREVIEW); } - myAwaitingForOpen = null; } - + }, myProject); + myToolWindow.setIcon(AllIcons.Actions.PreviewDetails); + myToolWindow.setContentUiType(ToolWindowContentUiType.COMBO, null); + myContentManager = myToolWindow.getContentManager(); + myStubContent = myContentManager.getContent(0); + myContentManager.addContentManagerListener(new ContentManagerAdapter() { @Override - public void setTabsPlacement(int tabPlacement) { - super.setTabsPlacement(UISettings.TABS_NONE); - } + public void selectionChanged(ContentManagerEvent event) { + final VirtualFile file = event.getContent().getUserData(FILE_KEY); + if (event.getOperation() == ContentManagerEvent.ContentOperation.remove && file != null && file.equals(myModifiedFile)) { + close(file); + return; + } - @Override - protected boolean showEmptyText() { - return false; + if (event.getOperation() != ContentManagerEvent.ContentOperation.add) return; + + if (file != null) { + event.getContent().setComponent(PreviewPanel.this);//Actually we share the same component between contents + if (!file.equals(myWindow.getSelectedFile())) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + myManager.openFileWithProviders(file, false, myWindow); + } + }); + } + } } - }; - - myProject.getMessageBus().connect().subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER, this); + }); + + myEditorsSplitters = new MyEditorsSplitters(); + + myProject.getMessageBus().connect().subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER, + new FileEditorManagerListener.Before() { + @Override + public void beforeFileOpened(@NotNull FileEditorManager source, + @NotNull VirtualFile file) { + myAwaitingForOpen = file; + VirtualFile currentFile = getCurrentFile(); + if (currentFile != null && + currentFile.equals(myModifiedFile) && + !currentFile.equals(file)) { + close(currentFile); + } + } + + @Override + public void beforeFileClosed(@NotNull FileEditorManager source, + @NotNull VirtualFile file) { + checkStubContent(); + } + }); myEditorsSplitters.createCurrentWindow(); - myWindow = myEditorsSplitters.getCurrentWindow(); myWindow.setTabsPlacement(UISettings.TABS_NONE); + myLayout = new CardLayout(); + setLayout(myLayout); + add(ContentType.Files.toString(), myEditorsSplitters); + //add(ContentType.Usages.toString(), myUsagesPreview);??? tree or editor ??? + //add(ContentType.Diagrams.toString(), myDiagramPanel); + //add(ContentType.Documentation.toString(), myDocumentationPanel);//todo + myToolWindow.setTitleActions(new MoveToEditorTabsAction()); + ArrayList<AnAction> myGearActions = new ArrayList<AnAction>(); + for (ContentType contentType : ContentType.values()) { + myGearActions.add(new ContentTypeToggleAction(contentType)); + } + myToolWindow.setAdditionalGearActions(new DefaultActionGroup("Preview", myGearActions)); + myToolWindow.hide(null); + } - setLayout(new GridLayout(1, 1)); - add(myEditorsSplitters); - - myToolWindow.setTitleActions(new MoveToEditorTabsAction(), new CloseFileAction()); - - myInitialized = true; + @Nullable + private VirtualFile getCurrentFile() { + VirtualFile[] files = myWindow.getFiles(); + return files.length == 1 ? files[0] : null; } - private void updateWindowTitle(VirtualFile file) { - if (myToolWindow == null) return; - if (file == null) { - myToolWindow.setTitle(": (empty)"); - } - else { - myToolWindow.setTitle(": " + - StringUtil.getShortened(EditorTabbedContainer.calcTabTitle(myProject, file), - UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT)); + @NotNull + private Content addContent(VirtualFile file) { + myHistory.add(file); + while (myHistory.size() > HISTORY_LIMIT) { + myHistory.remove(0); } + String title = + StringUtil.getShortened(EditorTabbedContainer.calcTabTitle(myProject, file), UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT); + + Content content = myContentManager.getFactory().createContent(this, title, false); + content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE); + content.putUserData(FILE_KEY, file); + content.setIcon(file.getFileType().getIcon()); + content.setPopupIcon(file.getFileType().getIcon()); + + myContentManager.addContent(content, 0); + checkStubContent(); + return content; } - @Override - public void beforeFileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - myAwaitingForOpen = file; + private void setSelected(VirtualFile file) { + Content content = getContent(file); + if (content == null) { + content = addContent(file); + } + myContentManager.setSelectedContent(content); + myContentManager.addContent(content, 0); } - @Override - public void beforeFileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - if (file.equals(getCurrentFile())) { - updateWindowTitle(null); - Document document = FileDocumentManager.getInstance().getDocument(file); - if (document != null) { - document.removeDocumentListener(this); + @Nullable + private Content getContent(VirtualFile file) { + Content[] contents = myContentManager.getContents(); + for (Content content : contents) { + if (file.equals(content.getUserData(FILE_KEY))) { + return content; } } + return null; } - - @Override - public void beforeDocumentChange(DocumentEvent event) { - - } - - @Override - public void documentChanged(DocumentEvent event) { - VirtualFile file = FileDocumentManager.getInstance().getFile(event.getDocument()); - if (file != null) { - myModifiedFile = file; + private void checkStubContent() { + if (myContentManager.getContents().length == 0) { + myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "false"); + myStubContent.setComponent(this); + myContentManager.addContent(myStubContent); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (myContentManager.getIndexOfContent(myStubContent) != -1) { + toggleToolWindow(false); + } + } + }); + } + else if (myContentManager.getContents().length > 1) { + myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true"); + myContentManager.removeContent(myStubContent, false); } } - EditorWindow getWindow() { - initToolWindowIfNeed(); - return myWindow; + private void close(@NotNull VirtualFile file) { + myHistory.remove(file); + if (ArrayUtil.find(myEditorsSplitters.getOpenFiles(), file) != -1) { + myEditorsSplitters.closeFile(file, false); + } + if (file.equals(myAwaitingForOpen)) { + myAwaitingForOpen = null; + } + if (file.equals(myModifiedFile)) { + myBlocked = true; + try { + myManager.openFileWithProviders(myModifiedFile, false, true); + } + finally { + myBlocked = false; + } + myModifiedFile = null; + } + Content content = getContent(file); + if (content != null) { + myContentManager.removeContent(content, false); + checkStubContent(); + } } - @Nullable - VirtualFile getCurrentFile() { - VirtualFile[] files = myWindow.getFiles(); - return files.length == 1 ? files[0] : null; + private void toggleToolWindow(boolean activate) { + ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW); + if (toolWindow != null) { + if (activate) { + toolWindow.activate(null, false); + } + else { + toolWindow.hide(null); + } + } } private class MoveToEditorTabsAction extends AnAction { + public MoveToEditorTabsAction() { - super(null, "Move to main tabs", AllIcons.Duplicates.SendToTheLeftGrayed); + super("Move to main tabs", "Move to main tabs", AllIcons.Duplicates.SendToTheLeftGrayed); } @Override @@ -182,63 +296,96 @@ class PreviewPanel extends JPanel implements DocumentListener, FileEditorManager return; } - myManager.openFileWithProviders(virtualFile, false, myManager.getCurrentWindow()); - closeCurrentFile(); + EditorWindow window = myManager.getCurrentWindow(); + if (window == null) { //main tab set is still not created, rare situation + myManager.getMainSplitters().createCurrentWindow(); + window = myManager.getCurrentWindow(); + } + myManager.openFileWithProviders(virtualFile, true, window); + close(virtualFile); + toggleToolWindow(false); + } + } + + private class ContentTypeToggleAction extends ToggleAction { + private final ContentType myContentType; + + ContentTypeToggleAction(ContentType contentType) { + super(contentType.toString()); + myContentType = contentType; } @Override - public void update(AnActionEvent e) { - super.update(e); - VirtualFile currentFile = getCurrentFile(); - e.getPresentation().setEnabled(currentFile != null); - if (currentFile == null) return; - - if (isModified(currentFile)) { - e.getPresentation().setIcon(AllIcons.Duplicates.SendToTheLeft); - } - else { - e.getPresentation().setIcon(AllIcons.Duplicates.SendToTheLeftGrayed); + public boolean isSelected(AnActionEvent e) { + return myTypes.contains(myContentType); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + if (state) { + myTypes.add(myContentType); + } else { + myTypes.remove(myContentType); } } } - private boolean isModified(@NotNull VirtualFile file) { - return file.equals(myModifiedFile); - } + private class MyEditorsSplitters extends EditorsSplitters { + public MyEditorsSplitters() { + super(myManager, myDockManager, false); + } - //returns last open file if it has "modified" status - @Nullable - VirtualFile closeCurrentFile() { - VirtualFile virtualFile = getCurrentFile(); - if (virtualFile == null) return null; - if (!myHistory.contains(virtualFile)) { - myHistory.add(virtualFile); - while (myHistory.size() > HISTORY_LIMIT) { - myHistory.remove(0); + @Override + protected void afterFileOpen(VirtualFile file) { + if (file.equals(myAwaitingForOpen)) { + setSelected(file); } + myAwaitingForOpen = null; + } + + @Override + protected void afterFileClosed(VirtualFile file) { + close(file); } - myWindow.closeFile(virtualFile); - this.revalidate(); - this.repaint(); - return isModified(virtualFile) ? virtualFile : null; - } - private class CloseFileAction extends AnAction { - public CloseFileAction() { - super(null, "Close", AllIcons.Actions.Close); + @Override + public void updateFileIcon(@NotNull VirtualFile file) { + EditorWithProviderComposite composite = myWindow.findFileComposite(file); + if (composite != null && composite.isModified()) { + myModifiedFile = file; + } } @Override - public void actionPerformed(AnActionEvent e) { - if (getCurrentFile() == null) return; - closeCurrentFile(); - ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).hide(null); + protected EditorWindow createEditorWindow() { + return new EditorWindow(this) { + @Override + protected void onBeforeSetEditor(VirtualFile file) { + VirtualFile currentFile = getCurrentFile(); + if (currentFile != null && currentFile.equals(myModifiedFile)) { + myBlocked = true; + try { + myManager.openFileWithProviders(myModifiedFile, false, true); + } + finally { + myBlocked = false; + } + } + else { + toggleToolWindow(true); + } + } + }; + } + + @Override + public void setTabsPlacement(int tabPlacement) { + super.setTabsPlacement(UISettings.TABS_NONE); } @Override - public void update(AnActionEvent e) { - super.update(e); - e.getPresentation().setEnabled(getCurrentFile() != null); + public boolean isPreview() { + return true; } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditor.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditor.java index 00f333ef4002..a676023b6590 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditor.java @@ -20,6 +20,7 @@ import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.fileEditor.impl.BaseRemoteFileEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; +import com.intellij.openapi.vfs.impl.http.RemoteFileInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,14 +29,16 @@ import javax.swing.*; /** * @author nik */ -public class HttpFileEditor extends BaseRemoteFileEditor { +class HttpFileEditor extends BaseRemoteFileEditor { private final RemoteFilePanel myPanel; public HttpFileEditor(@NotNull Project project, @NotNull HttpVirtualFile virtualFile) { super(project); myPanel = new RemoteFilePanel(project, virtualFile, this); - virtualFile.getFileInfo().download().doWhenDone(new Runnable() { + RemoteFileInfo fileInfo = virtualFile.getFileInfo(); + assert fileInfo != null; + fileInfo.download().doWhenDone(new Runnable() { @Override public void run() { ApplicationManager.getApplication().invokeLater(new Runnable() { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditorProvider.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditorProvider.java index 17354c7f4855..22234ab66a74 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditorProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditorProvider.java @@ -31,7 +31,7 @@ import org.jetbrains.annotations.NotNull; /** * @author nik */ -public class HttpFileEditorProvider implements FileEditorProvider, DumbAware { +class HttpFileEditorProvider implements FileEditorProvider, DumbAware { @Override public boolean accept(@NotNull final Project project, @NotNull final VirtualFile file) { return file instanceof HttpVirtualFile && !file.isDirectory(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java index 076d29e64bbe..4bd5549d85ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java @@ -83,6 +83,7 @@ public class RemoteFilePanel { final RemoteFileInfo remoteFileInfo = virtualFile.getFileInfo(); myDownloadingListener = new MyDownloadingListener(); + assert remoteFileInfo != null; remoteFileInfo.addDownloadingListener(myDownloadingListener); myCancelButton.addActionListener(new ActionListener() { @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java index 837626b53246..01d278302540 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java @@ -209,7 +209,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements NamedJDOME } @Override - public Document writeScheme(@NotNull final AbstractFileType fileType) throws WriteExternalException { + public Element writeScheme(@NotNull final AbstractFileType fileType) throws WriteExternalException { Element root = new Element(ELEMENT_FILETYPE); writeHeader(root, fileType); @@ -226,7 +226,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements NamedJDOME writeExtensionsMap(map, fileType, false); } - return new Document(root); + return root; } @Override @@ -263,7 +263,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements NamedJDOME }); } - private final TransferToPooledThreadQueue<Collection<VirtualFile>> reDetectQueue = new TransferToPooledThreadQueue<Collection<VirtualFile>>("File type re-detect", Condition.FALSE, -1, new Processor<Collection<VirtualFile>>() { + private final TransferToPooledThreadQueue<Collection<VirtualFile>> reDetectQueue = new TransferToPooledThreadQueue<Collection<VirtualFile>>("File type re-detect", Conditions.alwaysFalse(), -1, new Processor<Collection<VirtualFile>>() { @Override public boolean process(Collection<VirtualFile> files) { reDetect(files); diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java index 9a566e9915ef..72e24d626535 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java @@ -55,6 +55,7 @@ import com.intellij.ui.ComponentWithMnemonics; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBOptionButton; import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.ui.speedSearch.SpeedSearchSupply; import com.intellij.util.Alarm; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; @@ -140,6 +141,10 @@ public final class IdeKeyEventDispatcher implements Disposable { return false; } + if (isSpeedSearchEditing(e)) { + return false; + } + // http://www.jetbrains.net/jira/browse/IDEADEV-12372 if (e.getKeyCode() == KeyEvent.VK_CONTROL) { if (e.getID() == KeyEvent.KEY_PRESSED) { @@ -226,6 +231,18 @@ public final class IdeKeyEventDispatcher implements Disposable { } } + private static boolean isSpeedSearchEditing(KeyEvent e) { + int keyCode = e.getKeyCode(); + if (keyCode == KeyEvent.VK_BACK_SPACE) { + Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (owner instanceof JComponent) { + SpeedSearchSupply supply = SpeedSearchSupply.getSupply((JComponent)owner); + return supply != null && supply.isPopupActive(); + } + } + return false; + } + /** * @return <code>true</code> if and only if the <code>component</code> represents * modal context. 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 7c30e4faedec..177aa4babad1 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 @@ -44,11 +44,7 @@ import java.util.*; @State( name = "KeymapManager", - roamingType = RoamingType.PER_PLATFORM, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/keymap.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/keymap.xml", roamingType = RoamingType.PER_PLATFORM)} ) public class KeymapManagerImpl extends KeymapManagerEx implements PersistentStateComponent<Element>, ExportableApplicationComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.keymap.KeymapManager"); @@ -75,8 +71,8 @@ public class KeymapManagerImpl extends KeymapManagerEx implements PersistentStat } @Override - public Document writeScheme(@NotNull final KeymapImpl scheme) throws WriteExternalException { - return new Document(scheme.writeExternal()); + public Element writeScheme(@NotNull final KeymapImpl scheme) throws WriteExternalException { + return scheme.writeExternal(); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java index 449a2180e3c1..c2f3b4d72b3e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java @@ -28,8 +28,6 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.util.Collection; -import java.util.Collections; import java.util.List; public class SchemesManagerFactoryImpl extends SchemesManagerFactory implements SettingsSavingComponent { @@ -47,63 +45,10 @@ public class SchemesManagerFactoryImpl extends SchemesManagerFactory implements } IApplicationStore applicationStore = ((ApplicationImpl)application).getStateStore(); String baseDirPath = applicationStore.getStateStorageManager().expandMacros(fileSpec); - if (baseDirPath != null) { - StreamProvider provider = applicationStore.getStateStorageManager().getStreamProvider(); - SchemesManagerImpl<T, E> manager = new SchemesManagerImpl<T, E>(fileSpec, processor, roamingType, provider, new File(baseDirPath)); - myRegisteredManagers.add(manager); - return manager; - } - else { - return new AbstractSchemesManager<T, E>() { - @Override - @NotNull - public Collection<E> loadSchemes() { - return Collections.emptyList(); - } - - @Override - @NotNull - public Collection<SharedScheme<E>> loadSharedSchemes(final Collection<T> currentSchemeList) { - return Collections.emptyList(); - } - - @Override - public void exportScheme(@NotNull final E scheme, final String name, final String description) { - } - - @Override - public boolean isImportAvailable() { - return false; - } - - @Override - public boolean isShared(final Scheme scheme) { - return false; - } - - @Override - public void save() { - } - - @Override - protected void onSchemeDeleted(final Scheme toDelete) { - } - - @Override - protected void onSchemeAdded(final T scheme) { - } - - @Override - public boolean isExportAvailable() { - return false; - } - - @Override - public File getRootDirectory() { - return null; - } - }; - } + StreamProvider provider = applicationStore.getStateStorageManager().getStreamProvider(); + SchemesManagerImpl<T, E> manager = new SchemesManagerImpl<T, E>(fileSpec, processor, roamingType, provider, new File(baseDirPath)); + myRegisteredManagers.add(manager); + return manager; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java index 4ff3ebba1ee9..6142b696ff24 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java @@ -43,6 +43,7 @@ import gnu.trove.THashSet; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; +import org.jdom.Parent; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -79,7 +80,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme private boolean myListenerAdded = false; private Alarm myRefreshAlarm; - + private String mySchemeExtension = DEFAULT_EXT; private boolean myUpgradeExtension = false; @@ -535,7 +536,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme @Nullable private static Document loadGlobalScheme(final String schemePath) throws IOException { StreamProvider provider = getProvider(); - return provider != null && provider.isEnabled() ? StorageUtil.loadDocument(provider.loadContent(schemePath, RoamingType.GLOBAL)) : null; + return provider != null && provider.isEnabled() ? StorageUtil.loadDocument(provider.loadContent(schemePath, getRoamingType(provider))) : null; } private void saveFileName(String fileName, final E schemeKey) { @@ -548,12 +549,12 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme schemeKey.getExternalInfo().setCurrentFileName(fileName); } - private static long computeHashValue(final Document document) { - return JDOMUtil.getTreeHash(document); + private static long computeHashValue(Parent element) { + return JDOMUtil.getTreeHash(element instanceof Element ? (Element)element : ((Document)element).getRootElement()); } @Nullable - private Document writeSchemeToDocument(final E scheme) throws WriteExternalException { + private org.jdom.Parent writeSchemeToDocument(@NotNull E scheme) throws WriteExternalException { if (isShared(scheme)) { String originalPath = scheme.getExternalInfo().getOriginalPath(); if (originalPath != null) { @@ -562,11 +563,10 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme root.setAttribute(ORIGINAL_SCHEME_PATH, originalPath); Element localCopy = new Element(SCHEME_LOCAL_COPY); - localCopy.addContent(myProcessor.writeScheme(scheme).getRootElement().clone()); + localCopy.addContent(getClone(myProcessor.writeScheme(scheme))); root.addContent(localCopy); - - return new Document(root); + return root; } else { return null; @@ -577,8 +577,12 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } } - public void updateConfigFilesFromStreamProviders() { + @NotNull + private static Element getClone(@NotNull Parent result) { + return (result instanceof Element ? (Element)result : ((Document)result).getRootElement()).clone(); + } + public void updateConfigFilesFromStreamProviders() { } private static class SharedSchemeData { @@ -605,9 +609,9 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme Collection<String> names = new THashSet<String>(getAllSchemeNames(currentSchemeList)); Map<String, SharedScheme<E>> result = new THashMap<String, SharedScheme<E>>(); - for (String subPath : provider.listSubFiles(myFileSpec, RoamingType.GLOBAL)) { + for (String subPath : provider.listSubFiles(myFileSpec, getRoamingType(provider))) { try { - final Document subDocument = StorageUtil.loadDocument(provider.loadContent(getFileFullPath(subPath), RoamingType.GLOBAL)); + final Document subDocument = StorageUtil.loadDocument(provider.loadContent(getFileFullPath(subPath), getRoamingType(provider))); if (subDocument != null) { SharedSchemeData original = unwrap(subDocument); final E scheme = myProcessor.readScheme(original.original); @@ -669,6 +673,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return myFileSpec + '/' + subPath; } + @SuppressWarnings("deprecation") @Override public void exportScheme(@NotNull final E scheme, final String name, final String description) throws WriteExternalException, IOException { StreamProvider provider = getProvider(); @@ -676,31 +681,39 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return; } - Document document = myProcessor.writeScheme(scheme); + Parent document = myProcessor.writeScheme(scheme); if (document != null) { String fileSpec = getFileFullPath(UniqueFileNamesProvider.convertName(scheme.getName())) + mySchemeExtension; - if (!provider.isApplicable(fileSpec, RoamingType.GLOBAL)) { + if (!provider.isApplicable(fileSpec, getRoamingType(provider))) { return; } - Document wrapped = wrap(document, name, description); + Element wrapped = wrap(document, name, description); if (provider instanceof CurrentUserHolder) { wrapped = wrapped.clone(); String userName = ((CurrentUserHolder)provider).getCurrentUserName(); if (userName != null) { - wrapped.getRootElement().setAttribute(USER, userName); + wrapped.setAttribute(USER, userName); } } - StorageUtil.doSendContent(provider, fileSpec, wrapped, RoamingType.GLOBAL, false); + StorageUtil.doSendContent(provider, fileSpec, wrapped, getRoamingType(provider), false); } } - private static Document wrap(@NotNull Document original, @NotNull String name, @NotNull String description) { + @SuppressWarnings("deprecation") + @NotNull + private static RoamingType getRoamingType(@NotNull StreamProvider provider) { + // for deprecated old stream we use GLOBAL as before to preserve backward compatibility + return provider instanceof CurrentUserHolder ? RoamingType.GLOBAL : RoamingType.PER_USER; + } + + @NotNull + private static Element wrap(@NotNull Parent original, @NotNull String name, @NotNull String description) { Element sharedElement = new Element(SHARED_SCHEME_ORIGINAL); sharedElement.setAttribute(NAME, name); sharedElement.setAttribute(DESCRIPTION, description); - sharedElement.addContent(original.getRootElement().clone()); - return new Document(sharedElement); + sharedElement.addContent(getClone(original)); + return sharedElement; } @Override @@ -799,7 +812,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme deleteServerFiles(DELETED_XML); } else if (myProvider != null && myProvider.isEnabled()) { - StorageUtil.sendContent(myProvider, getFileFullPath(DELETED_XML), createDeletedDocument(), myRoamingType, true); + StorageUtil.sendContent(myProvider, getFileFullPath(DELETED_XML), createDeletedElement(), myRoamingType, true); } } finally { @@ -850,11 +863,11 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme final String fileName = getFileNameForScheme(fileNameProvider, eScheme); try { - final Document document = writeSchemeToDocument(eScheme); - if (document != null) { - long newHash = computeHashValue(document); + final Parent element = writeSchemeToDocument(eScheme); + if (element != null) { + long newHash = computeHashValue(element); Long oldHash = eScheme.getExternalInfo().getHash(); - saveIfNeeded(eScheme, fileName, document, newHash, oldHash); + saveIfNeeded(eScheme, fileName, element, newHash, oldHash); } } catch (final IOException e) { @@ -889,18 +902,18 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return fileName + mySchemeExtension; } - private void saveIfNeeded(E schemeKey, String fileName, Document document, long newHash, Long oldHash) throws IOException { + private void saveIfNeeded(E schemeKey, String fileName, Parent element, long newHash, Long oldHash) throws IOException { if (oldHash == null || newHash != oldHash.longValue() || myVFSBaseDir.findChild(fileName) == null) { - ensureFileText(fileName, StorageUtil.documentToBytes(document, true).toByteArray()); + ensureFileText(fileName, StorageUtil.elementToBytes(element, true).toByteArray()); schemeKey.getExternalInfo().setHash(newHash); saveFileName(fileName, schemeKey); - saveOnServer(fileName, document); + saveOnServer(fileName, element); } } - private void saveOnServer(final String fileName, final Document document) { + private void saveOnServer(final String fileName, @NotNull Parent element) { if (myProvider != null && myProvider.isEnabled()) { - StorageUtil.sendContent(myProvider, getFileFullPath(fileName), document, myRoamingType, true); + StorageUtil.sendContent(myProvider, getFileFullPath(fileName), element, myRoamingType, true); } } @@ -925,16 +938,15 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } } - private Document createDeletedDocument() { + @NotNull + private Element createDeletedElement() { Element root = new Element("deleted-schemes"); - Document result = new Document(root); for (String deletedName : myDeletedNames) { Element child = new Element("scheme"); root.addContent(child); child.setAttribute("name", deletedName); } - - return result; + return root; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java index 429a55b2b809..ca72bf30c541 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableExtensionPointUtil.java @@ -48,7 +48,7 @@ public class ConfigurableExtensionPointUtil { } } - final Map<String, ConfigurableWrapper> idToConfigurable = new HashMap<String, ConfigurableWrapper>(); + final Map<String, ConfigurableWrapper> idToConfigurable = new LinkedHashMap<String, ConfigurableWrapper>(); for (ConfigurableEP<Configurable> ep : extensions) { final Configurable configurable = ConfigurableWrapper.wrapConfigurable(ep); if (isSuppressed(configurable, filter)) continue; @@ -75,16 +75,13 @@ public class ConfigurableExtensionPointUtil { } } } - //leave only roots (i.e. configurables without parents) - for (final Iterator<String> iterator = idToConfigurable.keySet().iterator(); iterator.hasNext(); ) { - final String key = iterator.next(); - final ConfigurableWrapper wrapper = idToConfigurable.get(key); - final String parentId = wrapper.getParentId(); - if (parentId != null && idToConfigurable.containsKey(parentId)) { - iterator.remove(); // remove only processed parents + // add roots only (i.e. configurables without parents) + for (ConfigurableWrapper wrapper : idToConfigurable.values()) { + String parentId = wrapper.getParentId(); + if (parentId == null || !idToConfigurable.containsKey(parentId)) { + result.add(wrapper); } } - ContainerUtil.addAll(result, idToConfigurable.values()); return result; } @@ -164,7 +161,7 @@ public class ConfigurableExtensionPointUtil { @NotNull private static <T extends Configurable> T findConfigurable(ConfigurableEP<Configurable>[] extensions, Class<T> configurableClass) { for (ConfigurableEP<Configurable> extension : extensions) { - if (extension.providerClass != null || extension.instanceClass != null || extension.implementationClass != null) { + if (extension.canCreateConfigurable()) { final Configurable configurable = extension.createConfigurable(); if (configurableClass.isInstance(configurable)) { return configurableClass.cast(configurable); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableVisitor.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableVisitor.java new file mode 100644 index 000000000000..7d820a255fb3 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableVisitor.java @@ -0,0 +1,144 @@ +/* + * 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.ex; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.SearchableConfigurable; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Sergey.Malenkov + */ +public abstract class ConfigurableVisitor { + public static final ConfigurableVisitor ALL = new ConfigurableVisitor() { + @Override + protected boolean accept(Configurable configurable) { + return true; + } + }; + + protected abstract boolean accept(Configurable configurable); + + public final Configurable find(@NotNull ConfigurableGroup... groups) { + for (ConfigurableGroup group : groups) { + Configurable result = find(group.getConfigurables()); + if (result != null) { + return result; + } + } + return null; + } + + public final Configurable find(@NotNull Configurable... configurables) { + for (Configurable configurable : configurables) { + if (accept(configurable)) { + return configurable; + } + } + for (Configurable configurable : configurables) { + if (configurable instanceof Configurable.Composite) { + Configurable.Composite composite = (Configurable.Composite)configurable; + Configurable result = find(composite.getConfigurables()); + if (result != null) { + return result; + } + } + } + return null; + } + + public final List<Configurable> findAll(@NotNull ConfigurableGroup... groups) { + List<Configurable> list = new ArrayList<Configurable>(); + for (ConfigurableGroup group : groups) { + add(list, group.getConfigurables()); + } + return list; + } + + public final List<Configurable> findAll(@NotNull Configurable... configurables) { + List<Configurable> list = new ArrayList<Configurable>(); + add(list, configurables); + return list; + } + + private void add(List<Configurable> list, Configurable... configurables) { + for (Configurable configurable : configurables) { + if (accept(configurable)) { + list.add(configurable); + } + if (configurable instanceof Configurable.Composite) { + Configurable.Composite composite = (Configurable.Composite)configurable; + add(list, composite.getConfigurables()); + } + } + } + + public static final class ByID extends ConfigurableVisitor { + private final String myID; + + public ByID(@NotNull String id) { + myID = id; + } + + @Override + protected boolean accept(Configurable configurable) { + return myID.equals(getID(configurable)); + } + + public static String getID(Configurable configurable) { + return configurable instanceof SearchableConfigurable + ? ((SearchableConfigurable)configurable).getId() + : configurable.getClass().getName(); + } + } + + public static final class ByName extends ConfigurableVisitor { + private final String myName; + + public ByName(@NotNull String name) { + myName = name; + } + + @Override + protected boolean accept(Configurable configurable) { + return myName.equals(configurable.getDisplayName()); + } + } + + public static final class ByType extends ConfigurableVisitor { + private final Class<? extends Configurable> myType; + + public ByType(@NotNull Class<? extends Configurable> type) { + myType = type; + } + + @Override + protected boolean accept(Configurable configurable) { + if (myType.isInstance(configurable)) { + return true; + } + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + return myType.isInstance(wrapper.getConfigurable()); + } + return false; + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java index 3b67a3478368..dd8169e159a7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java @@ -47,6 +47,9 @@ public class ConfigurableWrapper implements SearchableConfigurable { @Nullable public static <T extends UnnamedConfigurable> T wrapConfigurable(ConfigurableEP<T> ep) { + if (!ep.canCreateConfigurable()) { + return null; + } if (ep.displayName != null || ep.key != null || ep.groupId != null) { T configurable = null; if (ep.providerClass != null) { @@ -55,9 +58,9 @@ public class ConfigurableWrapper implements SearchableConfigurable { return null; // it is allowed to return null from provider } } - return ep.children != null || ep.childrenEPName != null || ep.dynamic - ? (T)new CompositeWrapper(ep, configurable) - : (T)new ConfigurableWrapper(ep, configurable); + return !ep.dynamic && ep.children == null && ep.childrenEPName == null + ? (T)new ConfigurableWrapper(ep, configurable) + : (T)new CompositeWrapper(ep, configurable); } else { return ep.createConfigurable(); @@ -157,7 +160,16 @@ public class ConfigurableWrapper implements SearchableConfigurable { @NotNull @Override public String getId() { - return myEp.id == null ? myEp.instanceClass == null ? myEp.providerClass : myEp.instanceClass : myEp.id; + if (myEp.id != null) { + return myEp.id; + } + UnnamedConfigurable configurable = getConfigurable(); + if (configurable instanceof SearchableConfigurable) { + return ((SearchableConfigurable)configurable).getId(); + } + return myEp.instanceClass != null + ? myEp.instanceClass + : myEp.providerClass; } @NotNull diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java deleted file mode 100644 index e3404ec017cd..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.options.ex; - -import com.intellij.openapi.options.Configurable; -import com.intellij.openapi.options.ConfigurableGroup; -import com.intellij.openapi.options.ConfigurationException; -import com.intellij.openapi.options.OptionsBundle; -import com.intellij.openapi.options.SearchableConfigurable; -import com.intellij.openapi.util.text.StringUtil; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map.Entry; -import javax.swing.JComponent; - -public final class MixedConfigurableGroup implements SearchableConfigurable, ConfigurableGroup { - private final String myGroupId; - private Configurable[] myConfigurables; - - private MixedConfigurableGroup(String groupId, ArrayList<Configurable> configurables) { - myGroupId = groupId; - myConfigurables = (configurables != null) - ? configurables.toArray(new Configurable[configurables.size()]) - : new Configurable[0]; - Arrays.sort(myConfigurables, COMPARATOR); - } - - private MixedConfigurableGroup(String groupId, HashMap<String, ArrayList<Configurable>> configurables) { - this(groupId, configurables.remove(groupId)); - } - - @Override - public JComponent createComponent() { - return null; - } - - @Override - public boolean isModified() { - return false; - } - - @Override - public void apply() throws ConfigurationException { - } - - @Override - public void reset() { - } - - @Override - public void disposeUIResources() { - myConfigurables = null; - } - - @Override - public Runnable enableSearch(String option) { - return null; - } - - @NotNull - @Override - public String getId() { - return "configurable.group." + myGroupId; - } - - @Override - public String getHelpTopic() { - return "configurable.group." + myGroupId + ".help.topic"; - } - - @Override - public String getDisplayName() { - return OptionsBundle.message("configurable.group." + myGroupId + ".settings.display.name"); - } - - @Override - public String getShortName() { - return getDisplayName(); - } - - @Override - public Configurable[] getConfigurables() { - return myConfigurables; - } - - public static ConfigurableGroup[] getGroups(Configurable... configurables) { - HashMap<String, ArrayList<Configurable>> map = new HashMap<String, ArrayList<Configurable>>(); - for (Configurable configurable : configurables) { - String groupId = null; - if (configurable instanceof ConfigurableWrapper) { - groupId = ((ConfigurableWrapper)configurable).getExtensionPoint().groupId; - } - ArrayList<Configurable> list = map.get(groupId); - if (list == null) { - map.put(groupId, list = new ArrayList<Configurable>()); - } - list.add(configurable); - } - ArrayList<Configurable> buildList = map.get("build"); - if (buildList != null) { - NodeConfigurable buildTools = new NodeConfigurable("build.tools", 1000); - buildTools.add(find("MavenSettings", buildList.iterator())); - buildTools.add(find("reference.settingsdialog.project.gradle", buildList.iterator())); - buildTools.add(find("reference.settingsdialog.project.gant", buildList.iterator())); - if (buildTools.getConfigurables() != null) { - buildList.add(0, buildTools); - } - } - ArrayList<ConfigurableGroup> groups = new ArrayList<ConfigurableGroup>(map.size()); - groups.add(new MixedConfigurableGroup("appearance", map)); - groups.add(new MixedConfigurableGroup("editor", map)); - groups.add(new MixedConfigurableGroup("project", map)); - groups.add(new MixedConfigurableGroup("build", map)); - groups.add(new MixedConfigurableGroup("language", map)); - groups.add(new MixedConfigurableGroup("tools", map)); - ConfigurableGroup other = new MixedConfigurableGroup(null, map); - for(Entry<String, ArrayList<Configurable>>entry: map.entrySet()){ - groups.add(new MixedConfigurableGroup(entry.getKey(), entry.getValue())); - } - groups.add(other); - return groups.toArray(new ConfigurableGroup[groups.size()]); - } - - private static Configurable find(String id, Iterator<Configurable> iterator) { - while (iterator.hasNext()) { - Configurable configurable = iterator.next(); - if (configurable instanceof SearchableConfigurable) { - SearchableConfigurable sc = (SearchableConfigurable)configurable; - if (id.equals(sc.getId())) { - iterator.remove(); - return configurable; - } - } - } - return null; - } - - public static int getGroupWeight(Configurable configurable) { - if (configurable instanceof NodeConfigurable) { - return ((NodeConfigurable)configurable).getGroupWeight(); - } - if (configurable instanceof ConfigurableWrapper) { - return ((ConfigurableWrapper)configurable).getExtensionPoint().groupWeight; - } - return 0; - } - - private static final Comparator<Configurable> COMPARATOR = new Comparator<Configurable>() { - @Override - public int compare(Configurable configurable1, Configurable configurable2) { - if (configurable1 == null || configurable2 == null) { - return configurable2 != null ? -1 : configurable1 != null ? 1 : 0; - } - int weight1 = getGroupWeight(configurable1); - int weight2 = getGroupWeight(configurable2); - return weight1 > weight2 ? -1 : weight1 < weight2 ? 1 : StringUtil.naturalCompare(configurable1.getDisplayName(), - configurable2.getDisplayName()); - } - }; -} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java deleted file mode 100644 index 609943d02943..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.options.ex; - -import com.intellij.openapi.options.Configurable; -import com.intellij.openapi.options.OptionsBundle; -import com.intellij.openapi.options.SearchableConfigurable; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; - -public final class NodeConfigurable extends SearchableConfigurable.Parent.Abstract { - private final ArrayList<Configurable> myConfigurables = new ArrayList<Configurable>(); - private final String myId; - private final int myWeight; - - public NodeConfigurable(@NotNull String id, int weight) { - myId = id; - myWeight = weight; - } - - public int getGroupWeight() { - return myWeight; - } - - public void add(Configurable configurable) { - if (configurable != null) { - super.disposeUIResources(); - myConfigurables.add(configurable); - } - } - - @Override - public void disposeUIResources() { - super.disposeUIResources(); - myConfigurables.clear(); - } - - @NotNull - @Override - public String getId() { - return "node.configurable." + myId; - } - - @Nullable - @Override - public String getHelpTopic() { - return "node.configurable." + myId + ".help.topic"; - } - - @Nls - @Override - public String getDisplayName() { - return OptionsBundle.message("node.configurable." + myId + ".display.name"); - } - - @Override - protected Configurable[] buildConfigurables() { - int size = myConfigurables.size(); - return size == 0 ? null : myConfigurables.toArray(new Configurable[size]); - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/SortedConfigurableGroup.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/SortedConfigurableGroup.java new file mode 100644 index 000000000000..3d87b7679359 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/SortedConfigurableGroup.java @@ -0,0 +1,195 @@ +/* + * 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.ex; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.OptionsBundle; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/** + * @author Sergey.Malenkov + */ +public final class SortedConfigurableGroup + extends SearchableConfigurable.Parent.Abstract + implements SearchableConfigurable, ConfigurableGroup, Configurable.NoScroll { + + public static ConfigurableGroup getGroup(Configurable... configurables) { + SortedConfigurableGroup root = new SortedConfigurableGroup("root"); + HashMap<String, SortedConfigurableGroup> map = new HashMap<String, SortedConfigurableGroup>(); + map.put("root", root); + for (Configurable configurable : configurables) { + int weight = 0; + String groupId = null; + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + weight = wrapper.getExtensionPoint().groupWeight; + groupId = wrapper.getExtensionPoint().groupId; + } + SortedConfigurableGroup composite = map.get(groupId); + if (composite == null) { + composite = new SortedConfigurableGroup(groupId); + map.put(groupId, composite); + } + composite.add(weight, configurable); + } + // process supported groups + root.add(60, map.remove("appearance")); + root.add(50, map.remove("editor")); + root.add(40, map.remove("project")); + SortedConfigurableGroup build = map.remove("build"); + if (build == null) { + build = map.remove("build.tools"); + } + else { + build.add(1000, map.remove("build.tools")); + } + root.add(30, build); + root.add(20, map.remove("language")); + root.add(10, map.remove("tools")); + root.add(-10, map.remove(null)); + // process unsupported groups + if (1 < map.size()) { + for (SortedConfigurableGroup group : map.values()) { + if (root != group) { + group.myDisplayName = "Category: " + group.myGroupId; + root.add(0, group); + } + } + } + return root; + } + + private final ArrayList<WeightConfigurable> myList = new ArrayList<WeightConfigurable>(); + private final String myGroupId; + private String myDisplayName; + + private SortedConfigurableGroup(String groupId) { + myGroupId = groupId; + } + + public SortedConfigurableGroup(Configurable... configurables) { + myGroupId = "root"; + // create groups from configurations + HashMap<String, SortedConfigurableGroup> map = new HashMap<String, SortedConfigurableGroup>(); + map.put(myGroupId, this); + for (Configurable configurable : configurables) { + int weight = 0; + String groupId = null; + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + weight = wrapper.getExtensionPoint().groupWeight; + groupId = wrapper.getExtensionPoint().groupId; + } + SortedConfigurableGroup composite = map.get(groupId); + if (composite == null) { + composite = new SortedConfigurableGroup(groupId); + map.put(groupId, composite); + } + composite.add(weight, configurable); + } + // process supported groups + add(60, map.remove("appearance")); + add(50, map.remove("editor")); + add(40, map.remove("project")); + SortedConfigurableGroup build = map.remove("build"); + if (build == null) { + build = map.remove("build.tools"); + } + else { + build.add(1000, map.remove("build.tools")); + } + add(30, build); + add(20, map.remove("language")); + add(10, map.remove("tools")); + add(-10, map.remove(null)); + // process unsupported groups + if (1 < map.size()) { + for (SortedConfigurableGroup group : map.values()) { + if (this != group) { + group.myDisplayName = "Category: " + group.myGroupId; + add(0, group); + } + } + } + } + + private void add(int weight, Configurable configurable) { + if (configurable != null) { + myList.add(new WeightConfigurable(configurable, weight)); + } + } + + @Override + protected Configurable[] buildConfigurables() { + Collections.sort(myList); + int length = myList.size(); + Configurable[] result = new Configurable[length]; + for (int i = 0; i < result.length; i++) { + result[i] = myList.get(i).myConfigurable; + } + myList.clear(); + return result; + } + + @NotNull + @Override + public String getId() { + return "configurable.group." + myGroupId; + } + + @Nullable + @Override + public String getHelpTopic() { + return "configurable.group." + myGroupId + ".help.topic"; + } + + @Nls + @Override + public String getDisplayName() { + return myDisplayName != null ? myDisplayName : OptionsBundle.message("configurable.group." + myGroupId + ".settings.display.name"); + } + + @Override + public String getShortName() { + return getDisplayName(); + } + + private static final class WeightConfigurable implements Comparable<WeightConfigurable> { + private final Configurable myConfigurable; + private final int myWeight; + + private WeightConfigurable(@NotNull Configurable configurable, int weight) { + myConfigurable = configurable; + myWeight = weight; + } + + @Override + public int compareTo(@NotNull WeightConfigurable pair) { + return myWeight > pair.myWeight ? -1 : + myWeight < pair.myWeight ? 1 : + StringUtil.naturalCompare(myConfigurable.getDisplayName(), pair.myConfigurable.getDisplayName()); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java index c38bf6ff2033..a995ea372d99 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java @@ -16,7 +16,6 @@ package com.intellij.openapi.options.newEditor; import com.intellij.CommonBundle; -import com.intellij.ide.ui.search.SearchUtil; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ApplicationManager; @@ -25,6 +24,7 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.options.ex.ConfigurableVisitor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.ActionCallback; @@ -45,7 +45,6 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.Collection; -import java.util.List; import java.util.Map; /** @@ -114,7 +113,7 @@ public class IdeSettingsDialog extends DialogWrapper implements DataProvider { @Nullable private static Configurable getPreselectedByDisplayName(final ConfigurableGroup[] groups, final String preselectedConfigurableDisplayName, final Project project) { - Configurable result = findPreselectedByDisplayName(preselectedConfigurableDisplayName, groups); + Configurable result = new ConfigurableVisitor.ByName(preselectedConfigurableDisplayName).find(groups); return result == null ? findLastSavedConfigurable(groups, project) : result; } @@ -230,44 +229,7 @@ public class IdeSettingsDialog extends DialogWrapper implements DataProvider { final String id = PropertiesComponent.getInstance(project).getValue(LAST_SELECTED_CONFIGURABLE); if (id == null) return null; - return findConfigurableInGroups(id, groups); - } - - @Nullable - private static Configurable findConfigurableInGroups(String id, Configurable.Composite... groups) { - // avoid unnecessary group expand: check top-level configurables in all groups before looking at children - for (Configurable.Composite group : groups) { - final Configurable[] configurables = group.getConfigurables(); - for (Configurable c : configurables) { - if (c instanceof SearchableConfigurable && id.equals(((SearchableConfigurable)c).getId())) { - return c; - } - else if (id.equals(c.getClass().getName())) { - return c; - } - } - } - for (Configurable.Composite group : groups) { - final Configurable[] configurables = group.getConfigurables(); - for (Configurable c : configurables) { - if (c instanceof Configurable.Composite) { - Configurable result = findConfigurableInGroups(id, (Configurable.Composite)c); - if (result != null) { - return result; - } - } - } - } - return null; - } - - @Nullable - private static Configurable findPreselectedByDisplayName(final String preselectedConfigurableDisplayName, ConfigurableGroup[] groups) { - final List<Configurable> all = SearchUtil.expand(groups); - for (Configurable each : all) { - if (preselectedConfigurableDisplayName.equals(each.getDisplayName())) return each; - } - return null; + return new ConfigurableVisitor.ByID(id).find(groups); } @Override 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 c2119596a06e..c1e55a1419a2 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 @@ -17,7 +17,6 @@ package com.intellij.openapi.options.newEditor; import com.intellij.AbstractBundle; import com.intellij.CommonBundle; -import com.intellij.icons.AllIcons; import com.intellij.ide.ui.laf.darcula.ui.DarculaTextBorder; import com.intellij.ide.ui.laf.darcula.ui.DarculaTextFieldUI; import com.intellij.ide.ui.search.SearchUtil; @@ -1219,27 +1218,31 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat * @return default view for the specified configurable */ private JComponent createDefaultComponent(SearchableConfigurable searchable) { - JPanel box = new JPanel(); - box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); + JPanel panel = new JPanel(new BorderLayout(0, 9)); try { - box.add(new JLabel(getDefaultDescription(searchable))); + panel.add(BorderLayout.NORTH, new JLabel(getDefaultDescription(searchable))); } catch (AssertionError error) { return null; // description is not set } if (searchable instanceof Configurable.Composite) { - box.add(Box.createVerticalStrut(10)); + JPanel box = new JPanel(); + box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); + panel.add(BorderLayout.CENTER, box); + Configurable.Composite composite = (Configurable.Composite)searchable; for (final Configurable configurable : composite.getConfigurables()) { - box.add(new LinkLabel(configurable.getDisplayName(), AllIcons.Ide.Link) { + LinkLabel label = new LinkLabel(configurable.getDisplayName(), null) { @Override public void doClick() { select(configurable, null); } - }); + }; + label.setBorder(BorderFactory.createEmptyBorder(1, 17, 1, 1)); + box.add(label); } } - return box; + return panel; } @NotNull 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 aee9e889fdf1..ed52a7f50bf6 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 @@ -23,7 +23,6 @@ import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.options.ex.ConfigurableWrapper; -import com.intellij.openapi.options.ex.NodeConfigurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; @@ -383,17 +382,6 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl SimpleNode parent = base.getParent(); if (parent == myRoot) { project = getConfigurableProject(base); // show icon for top-level nodes - if (base.getConfigurable() instanceof NodeConfigurable) { // special case for custom subgroups (build.tools) - Configurable[] configurables = ((NodeConfigurable)base.getConfigurable()).getConfigurables(); - if (configurables != null) { // assume that all configurables have the same project - project = getConfigurableProject(configurables[0]); - } - } - } - else if (parent instanceof Base && ((Base)parent).getConfigurable() instanceof NodeConfigurable) { - if (((Base)base.getParent()).getConfigurable() instanceof NodeConfigurable) { - project = getConfigurableProject(base); // special case for custom subgroups - } } } if (project != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java index e10367d583bf..817a042c9bc9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java @@ -20,7 +20,7 @@ import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.Disposable; import com.intellij.openapi.options.*; import com.intellij.openapi.options.ex.ConfigurableWrapper; -import com.intellij.openapi.options.ex.NodeConfigurable; +import com.intellij.openapi.options.ex.SortedConfigurableGroup; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; @@ -49,6 +49,7 @@ import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.TreeUI; import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.*; @@ -63,6 +64,7 @@ import java.util.List; * @author Sergey.Malenkov */ final class SettingsTreeView extends JComponent implements Disposable, OptionsEditorColleague { + private static final String NODE_ICON = "settings.tree.view.icon"; private static final Color NORMAL_NODE = new JBColor(Gray._60, Gray._140); private static final Color WRONG_CONTENT = JBColor.RED; private static final Color MODIFIED_CONTENT = JBColor.BLUE; @@ -74,8 +76,9 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd private final MyRoot myRoot; private final JScrollPane myScroller; private JLabel mySeparator; - private final MyRenderer myRenderer = new MyRenderer(); private final IdentityHashMap<Configurable, MyNode> myConfigurableToNodeMap = new IdentityHashMap<Configurable, MyNode>(); + private final IdentityHashMap<UnnamedConfigurable, ConfigurableWrapper> myConfigurableToWrapperMap + = new IdentityHashMap<UnnamedConfigurable, ConfigurableWrapper>(); private final MergingUpdateQueue myQueue = new MergingUpdateQueue("SettingsTreeView", 150, false, this, this, this) .setRestartTimerOnAdd(true); @@ -96,9 +99,10 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myTree.setRowHeight(-1); myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - myTree.setCellRenderer(myRenderer); + myTree.setCellRenderer(new MyRenderer()); myTree.setRootVisible(false); myTree.setShowsRootHandles(false); + myTree.setExpandableItemsEnabled(false); myScroller = ScrollPaneFactory.createScrollPane(myTree, true); myScroller.getVerticalScrollBar().setUI(ButtonlessScrollBarUI.createTransparent()); @@ -139,7 +143,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd @NotNull String[] getPathNames(Configurable configurable) { ArrayDeque<String> path = new ArrayDeque<String>(); - MyNode node = myConfigurableToNodeMap.get(configurable); + MyNode node = findNode(configurable); while (node != null) { path.push(node.myDisplayName); SimpleNode parent = node.getParent(); @@ -157,8 +161,9 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } @Nullable - SimpleNode findNode(Configurable configurable) { - return myConfigurableToNodeMap.get(configurable); + MyNode findNode(Configurable configurable) { + ConfigurableWrapper wrapper = myConfigurableToWrapperMap.get(configurable); + return myConfigurableToNodeMap.get(wrapper != null ? wrapper : configurable); } @Nullable @@ -180,6 +185,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd if (configurable instanceof ConfigurableWrapper) { ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; configurable = wrapper.getConfigurable(); + myConfigurableToWrapperMap.put(configurable, wrapper); } if (type.isInstance(configurable)) { return type.cast(configurable); @@ -194,7 +200,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; return wrapper.getExtensionPoint().getProject(); } - return findConfigurableProject(myConfigurableToNodeMap.get(configurable)); + return findConfigurableProject(findNode(configurable)); } @Nullable @@ -214,15 +220,15 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } @Nullable - private ConfigurableGroup findConfigurableGroupAt(int x, int y) { + private String findGroupNameAt(int x, int y) { TreePath path = myTree.getClosestPathForLocation(x - myTree.getX(), y - myTree.getY()); while (path != null) { MyNode node = extractNode(path); if (node == null) { return null; } - if (node.myComposite instanceof ConfigurableGroup) { - return (ConfigurableGroup)node.myComposite; + if (myRoot == node.getParent()) { + return node.myDisplayName; } path = path.getParentPath(); } @@ -267,10 +273,10 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd mySeparator.setFont(getFont().deriveFont(Font.BOLD)); } int height = mySeparator.getPreferredSize().height; - ConfigurableGroup group = findConfigurableGroupAt(0, height); - if (group != null && group == findConfigurableGroupAt(0, -myRenderer.getSeparatorHeight())) { + String group = findGroupNameAt(0, height); + if (group != null && group.equals(findGroupNameAt(0, 0))) { mySeparator.setBorder(BorderFactory.createEmptyBorder(0, 18, 0, 0)); - mySeparator.setText(group.getDisplayName()); + mySeparator.setText(group); Rectangle bounds = myScroller.getViewport().getBounds(); if (bounds.height > height) { @@ -319,7 +325,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd public void run() { if (configurable != myQueuedConfigurable) return; - MyNode editorNode = myConfigurableToNodeMap.get(configurable); + MyNode editorNode = findNode(configurable); FilteringTreeStructure.FilteringNode editorUiNode = myBuilder.getVisibleNodeFor(editorNode); if (editorUiNode == null) return; @@ -353,7 +359,8 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } private void fireSelected(Configurable configurable, ActionCallback callback) { - myFilter.myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); + ConfigurableWrapper wrapper = myConfigurableToWrapperMap.get(configurable); + myFilter.myContext.fireSelected(wrapper != null ? wrapper : configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); } @Override @@ -396,11 +403,13 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd if (myGroups == null || myGroups.length == 0) { return NO_CHILDREN; } - SimpleNode[] result = new SimpleNode[myGroups.length]; - for (int i = 0; i < myGroups.length; i++) { - result[i] = new MyNode(this, myGroups[i]); + ArrayList<MyNode> list = new ArrayList<MyNode>(); + for (ConfigurableGroup group : myGroups) { + for (Configurable configurable : group.getConfigurables()) { + list.add(new MyNode(this, configurable, 0)); + } } - return result; + return list.toArray(new SimpleNode[list.size()]); } } @@ -408,21 +417,15 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd private final Configurable.Composite myComposite; private final Configurable myConfigurable; private final String myDisplayName; + private final int myLevel; - private MyNode(CachingSimpleNode parent, Configurable configurable) { + private MyNode(CachingSimpleNode parent, Configurable configurable, int level) { super(parent); myComposite = configurable instanceof Configurable.Composite ? (Configurable.Composite)configurable : null; myConfigurable = configurable; String name = configurable.getDisplayName(); myDisplayName = name != null ? name.replace("\n", " ") : "{ " + configurable.getClass().getSimpleName() + " }"; - } - - private MyNode(CachingSimpleNode parent, ConfigurableGroup group) { - super(parent); - myComposite = group; - myConfigurable = group instanceof Configurable ? (Configurable)group : null; - String name = group.getDisplayName(); - myDisplayName = name != null ? name.replace("\n", " ") : "{ " + group.getClass().getSimpleName() + " }"; + myLevel = level; } @Override @@ -439,7 +442,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } SimpleNode[] result = new SimpleNode[configurables.length]; for (int i = 0; i < configurables.length; i++) { - result[i] = new MyNode(this, configurables[i]); + result[i] = new MyNode(this, configurables[i], myLevel + 1); if (myConfigurable != null) { myFilter.myContext.registerKid(myConfigurable, configurables[i]); } @@ -453,25 +456,17 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } } - private final class MyRenderer extends GroupedElementsRenderer.Tree { - private JLabel myNodeIcon; - private JLabel myProjectIcon; - - protected JComponent createItemComponent() { - myTextLabel = new ErrorLabel(); - return myTextLabel; - } + private final class MyRenderer extends JPanel implements TreeCellRenderer { + private final JLabel myTextLabel = new ErrorLabel(); + private final JLabel myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); + private final JLabel myProjectIcon = new JLabel(" ", SwingConstants.LEFT); - @Override - protected void layout() { - myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); - myProjectIcon = new JLabel(" ", SwingConstants.LEFT); - myNodeIcon.setOpaque(false); - myTextLabel.setOpaque(false); - myProjectIcon.setOpaque(false); - myRendererComponent.add(BorderLayout.CENTER, myComponent); - myRendererComponent.add(BorderLayout.WEST, myNodeIcon); - myRendererComponent.add(BorderLayout.EAST, myProjectIcon); + public MyRenderer() { + super(new BorderLayout()); + myNodeIcon.setName(NODE_ICON); + add(BorderLayout.CENTER, myTextLabel); + add(BorderLayout.WEST, myNodeIcon); + add(BorderLayout.EAST, myProjectIcon); } public Component getTreeCellRendererComponent(JTree tree, @@ -482,7 +477,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd int row, boolean focused) { myTextLabel.setFont(UIUtil.getLabelFont()); - myRendererComponent.setBackground(selected ? UIUtil.getTreeSelectionBackground() : myTree.getBackground()); + setPreferredSize(null); MyNode node = extractNode(value); if (node == null) { @@ -494,28 +489,19 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd if (myRoot == node.getParent()) { myTextLabel.setFont(myTextLabel.getFont().deriveFont(Font.BOLD)); } - TreePath path = tree.getPathForRow(row); - if (path == null) { - if (value instanceof DefaultMutableTreeNode) { - path = new TreePath(((DefaultMutableTreeNode)value).getPath()); - } - } - int forcedWidth = 2000; - if (path != null && tree.isVisible()) { - Rectangle visibleRect = tree.getVisibleRect(); - - int nestingLevel = tree.isRootVisible() ? path.getPathCount() - 1 : path.getPathCount() - 2; - - int left = UIUtil.getTreeLeftChildIndent(); - int right = UIUtil.getTreeRightChildIndent(); - + if (tree.isVisible()) { + int indent = node.myLevel * (UIUtil.getTreeLeftChildIndent() + UIUtil.getTreeRightChildIndent()); Insets treeInsets = tree.getInsets(); - - int indent = (left + right) * nestingLevel + (treeInsets != null ? treeInsets.left + treeInsets.right : 0); - - forcedWidth = visibleRect.width > 0 ? visibleRect.width - indent : forcedWidth; + if (treeInsets != null) { + indent += treeInsets.left + treeInsets.right; + } + int visibleWidth = tree.getVisibleRect().width; + if (visibleWidth > indent) { + Dimension size = getPreferredSize(); + size.width = visibleWidth - indent; + //setPreferredSize(size); + } } - myRendererComponent.setPrefereedWidth(forcedWidth - 4); } // update font color for modified configurables myTextLabel.setForeground(selected ? UIUtil.getTreeSelectionForeground() : NORMAL_NODE); @@ -537,15 +523,15 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd if (parent instanceof MyNode) { if (myRoot == parent.getParent()) { project = findConfigurableProject(node); // show icon for top-level nodes - if (node.myConfigurable instanceof NodeConfigurable) { // special case for custom subgroups (build.tools) - Configurable[] configurables = ((NodeConfigurable)node.myConfigurable).getConfigurables(); + if (node.myConfigurable instanceof SortedConfigurableGroup) { // special case for custom subgroups (build.tools) + Configurable[] configurables = ((SortedConfigurableGroup)node.myConfigurable).getConfigurables(); if (configurables != null) { // assume that all configurables have the same project project = findConfigurableProject(configurables[0]); } } } - else if (((MyNode)parent).myConfigurable instanceof NodeConfigurable) { - if (((MyNode)node.getParent()).myConfigurable instanceof NodeConfigurable) { + else if (((MyNode)parent).myConfigurable instanceof SortedConfigurableGroup) { + if (((MyNode)node.getParent()).myConfigurable instanceof SortedConfigurableGroup) { project = findConfigurableProject(node); // special case for custom subgroups } } @@ -564,25 +550,20 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myProjectIcon.setVisible(false); } // configure node icon - if (value instanceof DefaultMutableTreeNode) { - DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value; - TreePath treePath = new TreePath(treeNode.getPath()); - myNodeIcon.setIcon(myTree.getHandleIcon(treeNode, treePath)); - } - else { - myNodeIcon.setIcon(null); + Icon nodeIcon = null; + if (node != null) { + if (0 == node.getChildCount()) { + nodeIcon = myTree.getEmptyHandle(); + } + else if (value instanceof DefaultMutableTreeNode) { + DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value; + nodeIcon = myTree.isExpanded(new TreePath(treeNode.getPath())) + ? myTree.getExpandedHandle() + : myTree.getCollapsedHandle(); + } } - return myRendererComponent; - } - - int getSeparatorHeight() { - return mySeparatorComponent.getParent() == null ? 0 : mySeparatorComponent.getPreferredSize().height; - } - - public boolean isUnderHandle(Point point) { - Point handlePoint = SwingUtilities.convertPoint(myRendererComponent, point, myNodeIcon); - Rectangle bounds = myNodeIcon.getBounds(); - return bounds.x < handlePoint.x && bounds.getMaxX() >= handlePoint.x; + myNodeIcon.setIcon(nodeIcon); + return this; } } @@ -616,11 +597,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd @Override public void setUI(TreeUI ui) { - TreeUI actualUI = ui; - if (!(ui instanceof MyTreeUi)) { - actualUI = new MyTreeUi(); - } - super.setUI(actualUI); + super.setUI(ui instanceof MyTreeUi ? ui : new MyTreeUi()); } @Override @@ -659,84 +636,92 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } @Override - protected void processMouseEvent(MouseEvent e) { + protected void processMouseEvent(MouseEvent event) { MyTreeUi ui = (MyTreeUi)myTree.getUI(); - boolean toggleNow = MouseEvent.MOUSE_RELEASED == e.getID() - && UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) - && !ui.isToggleEvent(e); - - if (toggleNow || MouseEvent.MOUSE_PRESSED == e.getID()) { - TreePath path = getPathForLocation(e.getX(), e.getY()); - if (path != null) { - Rectangle bounds = getPathBounds(path); - if (bounds != null && path.getLastPathComponent() instanceof DefaultMutableTreeNode) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); - boolean selected = isPathSelected(path); - boolean expanded = isExpanded(path); - Component comp = - myRenderer.getTreeCellRendererComponent(this, node, selected, expanded, node.isLeaf(), getRowForPath(path), isFocusOwner()); - - comp.setBounds(bounds); - comp.validate(); - - Point point = new Point(e.getX() - bounds.x, e.getY() - bounds.y); - if (myRenderer.isUnderHandle(point)) { - if (toggleNow) { - ui.toggleExpandState(path); - } - e.consume(); - return; - } - } - } + if (!ui.processMouseEvent(event)) { + super.processMouseEvent(event); } - - super.processMouseEvent(e); } + } - private final class MyTreeUi extends WideSelectionTreeUI { + private static final class MyTreeUi extends WideSelectionTreeUI { + boolean processMouseEvent(MouseEvent event) { + if (super.tree instanceof SimpleTree) { + SimpleTree tree = (SimpleTree)super.tree; - @Override - public void toggleExpandState(TreePath path) { - super.toggleExpandState(path); - } + boolean toggleNow = MouseEvent.MOUSE_RELEASED == event.getID() + && UIUtil.isActionClick(event, MouseEvent.MOUSE_RELEASED) + && !isToggleEvent(event); - @Override - public boolean isToggleEvent(MouseEvent event) { - return super.isToggleEvent(event); + if (toggleNow || MouseEvent.MOUSE_PRESSED == event.getID()) { + Component component = tree.getDeepestRendererComponentAt(event.getX(), event.getY()); + if (component != null && NODE_ICON.equals(component.getName())) { + if (toggleNow) { + toggleExpandState(tree.getPathForLocation(event.getX(), event.getY())); + } + event.consume(); + return true; + } + } } + return false; + } - @Override - protected boolean shouldPaintExpandControl(TreePath path, - int row, - boolean isExpanded, - boolean hasBeenExpanded, - boolean isLeaf) { - return false; - } + @Override + protected boolean shouldPaintExpandControl(TreePath path, + int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) { + return false; + } - @Override - protected void paintHorizontalPartOfLeg(Graphics g, - Rectangle clipBounds, - Insets insets, - Rectangle bounds, - TreePath path, - int row, - boolean isExpanded, - boolean hasBeenExpanded, - boolean isLeaf) { + @Override + protected void paintHorizontalPartOfLeg(Graphics g, + Rectangle clipBounds, + Insets insets, + Rectangle bounds, + TreePath path, + int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) { - } + } - @Override - protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { - } + @Override + protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { + } - @Override - public void paint(Graphics g, JComponent c) { - GraphicsUtil.setupAntialiasing(g); - super.paint(g, c); + @Override + public void paint(Graphics g, JComponent c) { + GraphicsUtil.setupAntialiasing(g); + super.paint(g, c); + } + + @Override + protected void paintRow(Graphics g, + Rectangle clipBounds, + Insets insets, + Rectangle bounds, + TreePath path, + int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) { + if (tree != null) { + int width = tree.getWidth(); + Container parent = tree.getParent(); + if (parent instanceof JViewport) { + JViewport viewport = (JViewport)parent; + width = viewport.getWidth() - viewport.getViewPosition().x; + } + width -= bounds.x; + if (bounds.width < width) { + bounds.width = width; + } } + super.paintRow(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index 308edb2d81a6..c66eb5eb1a27 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -34,7 +34,6 @@ import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.ProgressIndicatorEx; import com.intellij.openapi.wm.ex.StatusBarEx; -import com.intellij.psi.impl.DebugUtil; import com.intellij.ui.AppIcon; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; @@ -81,7 +80,7 @@ public class DumbServiceImpl extends DumbService implements Disposable { @Override public void cancelTask(@NotNull DumbModeTask task) { - if (ApplicationManager.getApplication().isInternal()) LOG.info("cancel " + task + "\n" + DebugUtil.currentStackTrace()); + if (ApplicationManager.getApplication().isInternal()) LOG.info("cancel " + task); ProgressIndicatorEx indicator = myProgresses.get(task); if (indicator != null) { indicator.cancel(); diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java index 978e20f6b532..8725dc003da5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java @@ -38,7 +38,6 @@ import com.intellij.openapi.components.impl.stores.IProjectStore; import com.intellij.openapi.components.impl.stores.StoreUtil; import com.intellij.openapi.components.impl.stores.UnknownMacroNotification; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.pico.ConstructorInjectionComponentAdapter; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressIndicator; @@ -58,6 +57,8 @@ import com.intellij.openapi.wm.impl.FrameTitleBuilder; import com.intellij.util.Function; import com.intellij.util.TimedReference; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.pico.ConstructorInjectionComponentAdapter; +import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -488,7 +489,7 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project } if (!macros2invalidate.isEmpty()) { - final Set<String> components = new HashSet<String>(); + final Set<String> components = new THashSet<String>(); for (TrackingPathMacroSubstitutor substitutor : substitutors) { components.addAll(substitutor.getComponents(macros2invalidate)); } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index e7b20959369e..b5243e7c7ec9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -57,7 +57,6 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.TimeoutUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; -import com.intellij.util.io.fs.IFile; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; @@ -922,9 +921,9 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt IProjectStore projectStore = projectImpl.getStateStore(); final String location = projectImpl.getPresentableUrl(); - final List<IFile> original; + final List<File> original; try { - final IComponentStore.SaveSession saveSession = projectStore.startSave(); + IComponentStore.SaveSession saveSession = projectStore.startSave(); original = saveSession.getAllStorageFiles(true); saveSession.finishSave(); } @@ -937,7 +936,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt application.runWriteAction(new Runnable() { @Override public void run() { - for (final IFile originalFile : original) { + for (File originalFile : original) { restoreCopy(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(originalFile)); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/popup/MultiSelectionListPopupStep.java b/platform/platform-impl/src/com/intellij/openapi/ui/popup/MultiSelectionListPopupStep.java index 815eafd56ab4..96fbccc87054 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/popup/MultiSelectionListPopupStep.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/popup/MultiSelectionListPopupStep.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. @@ -28,8 +28,8 @@ import java.util.List; public abstract class MultiSelectionListPopupStep<T> extends BaseListPopupStep<T> { private int[] myDefaultOptionIndices = ArrayUtil.EMPTY_INT_ARRAY; - protected MultiSelectionListPopupStep(@Nullable String aTitle, List<? extends T> aValues) { - super(aTitle, aValues); + protected MultiSelectionListPopupStep(@Nullable String title, List<? extends T> values) { + super(title, values); } public abstract PopupStep<?> onChosen(List<T> selectedValues, boolean finalChoice); @@ -54,8 +54,8 @@ public abstract class MultiSelectionListPopupStep<T> extends BaseListPopupStep<T } @Override - public final void setDefaultOptionIndex(int aDefaultOptionIndex) { - myDefaultOptionIndices = new int[]{aDefaultOptionIndex}; + public final void setDefaultOptionIndex(int defaultOptionIndex) { + myDefaultOptionIndices = new int[]{defaultOptionIndex}; } public int[] getDefaultOptionIndices() { diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateAction.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateAction.java index 1f331d7f9232..81b51cab9cb2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateAction.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.updateSettings.impl; +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -24,7 +25,7 @@ import com.intellij.openapi.util.SystemInfo; public class CheckForUpdateAction extends AnAction implements DumbAware { @Override public void update(AnActionEvent e) { - e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu); + e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu || !ActionPlaces.MAIN_MENU.equals(e.getPlace())); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NoUpdatesPanel.form b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NoUpdatesPanel.form index 2cee5d3e03f9..be22623c2142 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NoUpdatesPanel.form +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/NoUpdatesPanel.form @@ -25,7 +25,6 @@ </constraints> <properties> <contentType value="text/html"/> - <text value="<html> <head> </head> <body> <p style="margin-top: 0"> </p> </body> </html> "/> </properties> </component> <vspacer id="bf1d9"> diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java index 071a260fe68c..a95597514bb1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java @@ -15,12 +15,14 @@ */ package com.intellij.openapi.vfs.ex.dummy; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerAdapter; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.BidirectionalMap; @@ -32,7 +34,6 @@ import org.jetbrains.annotations.TestOnly; import java.io.IOException; import java.util.Collection; -import java.util.Iterator; import java.util.List; /** @@ -76,11 +77,6 @@ public abstract class DummyCachingFileSystem<T extends VirtualFile> extends Dumm public void projectOpened(final Project project) { onProjectOpened(project); } - - @Override - public void projectClosed(final Project project) { - onProjectClosed(project); - } }); initProjectMap(); } @@ -135,7 +131,16 @@ public abstract class DummyCachingFileSystem<T extends VirtualFile> extends Dumm clearCache(); } - public void onProjectOpened(Project project) { + public void onProjectOpened(final Project project) { + // use Disposer instead of ProjectManagerListener#projectClosed() because Disposer.dispose(project) + // is called later and some cached files should stay valid till the last moment + Disposer.register(project, new Disposable() { + @Override + public void dispose() { + onProjectClosed(project); + } + }); + clearCache(); String projectId = project.getLocationHash(); myProject2Id.put(project, projectId); @@ -157,13 +162,11 @@ public abstract class DummyCachingFileSystem<T extends VirtualFile> extends Dumm } protected void clearInvalidFiles() { - for (Iterator<String> it = myCachedFiles.keySet().iterator(); it.hasNext(); ) { - String path = it.next(); - T t = myCachedFiles.get(path); - if (t == null || !t.isValid()) { - it.remove(); - } + for (T t : myCachedFiles.notNullValues()) { + if (!t.isValid()) myCachedFiles.removeValue(t); } + //noinspection StatementWithEmptyBody + while (myCachedFiles.removeValue(null)) ; } @TestOnly diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VfsRootAccess.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VfsRootAccess.java index b6fa97092059..5a0f433c7828 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VfsRootAccess.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VfsRootAccess.java @@ -49,7 +49,7 @@ import java.util.Set; public class VfsRootAccess { private static final boolean SHOULD_PERFORM_ACCESS_CHECK = System.getenv("NO_FS_ROOTS_ACCESS_CHECK") == null; // we don't want test subclasses to accidentally remove allowed files, added by base classes - private static final Set<String> ourAdditionalRoots = new THashSet<String>(); + private static final Set<String> ourAdditionalRoots = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY); private static boolean insideGettingRoots; @TestOnly @@ -109,7 +109,7 @@ public class VfsRootAccess { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); if (openProjects.length == 0) return null; - final Set<String> allowed = new THashSet<String>(); + final Set<String> allowed = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY); allowed.add(FileUtil.toSystemIndependentName(PathManager.getHomePath())); try { diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java index b05b7a285e7b..b8a55b333203 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java @@ -29,6 +29,7 @@ import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.util.io.ByteSequence; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.newvfs.FileAttribute; import com.intellij.openapi.vfs.newvfs.impl.FileNameCache; import com.intellij.util.ArrayUtil; import com.intellij.util.SystemProperties; @@ -57,6 +58,7 @@ public class FSRecords implements Forceable { private static final Logger LOG = Logger.getInstance("#com.intellij.vfs.persistent.FSRecords"); public static final boolean weHaveContentHashes = SystemProperties.getBooleanProperty("idea.share.contents", true); + public static final boolean lazyVfsDataCleaning = SystemProperties.getBooleanProperty("idea.lazy.vfs.data.cleaning", true); private static final int VERSION = 20 + (weHaveContentHashes ? 0x10:0) + (IOUtil.ourByteBuffersUseNativeByteOrder ? 0x37:0); private static final int PARENT_OFFSET = 0; @@ -558,6 +560,7 @@ public class FSRecords implements Forceable { return newRecord; } else { + if (lazyVfsDataCleaning) deleteContentAndAttributes(free); DbConnection.cleanRecord(free); return free; } @@ -588,7 +591,33 @@ public class FSRecords implements Forceable { try { w.lock(); incModCount(id); - doDeleteRecursively(id); + if (lazyVfsDataCleaning) { + markAsDeletedRecursively(id); + } else { + doDeleteRecursively(id); + } + } + catch (Throwable e) { + throw DbConnection.handleError(e); + } + finally { + w.unlock(); + } + } + + private static void markAsDeletedRecursively(final int id) { + for (int subrecord : list(id)) { + markAsDeletedRecursively(subrecord); + } + + markAsDeleted(id); + } + + private static void markAsDeleted(final int id) { + try { + w.lock(); + DbConnection.markDirty(); + addToFreeRecordsList(id); } catch (Throwable e) { throw DbConnection.handleError(e); @@ -1206,12 +1235,26 @@ public class FSRecords implements Forceable { } @Nullable - static DataInputStream readAttributeWithLock(int fileId, String attId) { + public static DataInputStream readAttributeWithLock(int fileId, FileAttribute att) { try { - synchronized (attId) { + synchronized (att.getId()) { try { r.lock(); - return readAttribute(fileId, attId); + DataInputStream stream = readAttribute(fileId, att.getId()); + if (stream != null) { + try { + int actualVersion = DataInputOutputUtil.readINT(stream); + if (actualVersion != att.getVersion()) { + stream.close(); + return null; + } + } + catch (IOException e) { + stream.close(); + return null; + } + } + return stream; } finally { r.unlock(); @@ -1280,7 +1323,9 @@ public class FSRecords implements Forceable { private static void checkFileIsValid(int fileId) { assert fileId > 0 : fileId; // TODO: This assertion is a bit timey, will remove when bug is caught. - assert (getFlags(fileId) & FREE_RECORD_FLAG) == 0 : "Accessing attribute of a deleted page: " + fileId + ":" + getName(fileId); + if (!lazyVfsDataCleaning) { + assert (getFlags(fileId) & FREE_RECORD_FLAG) == 0 : "Accessing attribute of a deleted page: " + fileId + ":" + getName(fileId); + } } public static int acquireFileContent(int fileId) { @@ -1374,6 +1419,18 @@ public class FSRecords implements Forceable { return new AttributeOutputStream(fileId, attId, fixedSize); } + @NotNull + public static DataOutputStream writeAttribute(final int fileId, @NotNull FileAttribute att) { + DataOutputStream stream = writeAttribute(fileId, att.getId(), att.isFixedSize()); + try { + DataInputOutputUtil.writeINT(stream, att.getVersion()); + } + catch (IOException e) { + throw new RuntimeException(e); + } + return stream; + } + private static class ContentOutputStream extends DataOutputStream { protected final int myFileId; protected final boolean myFixedSize; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java index db5de4258d41..6e135018d356 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java @@ -223,13 +223,13 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone @Override @Nullable public DataInputStream readAttribute(@NotNull final VirtualFile file, @NotNull final FileAttribute att) { - return FSRecords.readAttributeWithLock(getFileId(file), att.getId()); + return FSRecords.readAttributeWithLock(getFileId(file), att); } @Override @NotNull public DataOutputStream writeAttribute(@NotNull final VirtualFile file, @NotNull final FileAttribute att) { - return FSRecords.writeAttribute(getFileId(file), att.getId(), att.isFixedSize()); + return FSRecords.writeAttribute(getFileId(file), att); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java index 30777377bc49..746030e68abd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.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,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.impl.commands.FinalizableCommand; import org.jetbrains.annotations.NotNull; @@ -124,7 +125,7 @@ public final class CommandProcessor implements Runnable { FinalizableCommand command = myList.remove(0); if (isEmpty()) { // memory leak otherwise - myExpireCondition = Condition.TRUE; + myExpireCondition = Conditions.alwaysTrue(); } return command; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java index e6c84f5e7768..ba5c59a9ac25 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java @@ -17,6 +17,7 @@ package com.intellij.openapi.wm.impl; import com.intellij.ide.actions.ResizeToolWindowAction; import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapManagerListener; @@ -28,6 +29,7 @@ import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.*; import com.intellij.ui.Gray; import com.intellij.ui.JBColor; @@ -54,8 +56,6 @@ import java.util.Map; */ public final class InternalDecorator extends JPanel implements Queryable, TypeSafeDataProvider { - private static final int DIVIDER_WIDTH = UIUtil.isUnderDarcula() ? 2 : 5; - private Project myProject; private WindowInfoImpl myInfo; private final ToolWindowImpl myToolWindow; @@ -169,7 +169,7 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa else if (ToolWindowAnchor.RIGHT == anchor) { add(myDivider, BorderLayout.WEST); } - myDivider.setPreferredSize(new Dimension(DIVIDER_WIDTH, DIVIDER_WIDTH)); + myDivider.setPreferredSize(new Dimension(0, 0)); } else { // docked and floating windows don't have divider remove(myDivider); @@ -303,10 +303,16 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa @Override public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) { - g.setColor(UIUtil.getPanelBackground()); - doPaintBorder(c, g, x, y, width, height); - g.setColor(new Color(0, 0, 0, 90)); - doPaintBorder(c, g, x, y, width, height); + if (UIUtil.isUnderDarcula()) { + g.setColor(Gray._40); + doPaintBorder(c, g, x, y, width, height); + } + else { + g.setColor(UIUtil.getPanelBackground()); + doPaintBorder(c, g, x, y, width, height); + g.setColor(Gray._155); + doPaintBorder(c, g, x, y, width, height); + } } private void doPaintBorder(Component c, Graphics g, int x, int y, int width, int height) { @@ -314,18 +320,22 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa if (insets.top > 0) { UIUtil.drawLine(g, x, y + insets.top - 1, x + width - 1, y + insets.top - 1); + UIUtil.drawLine(g, x, y + insets.top, x + width - 1, y + insets.top); } if (insets.left > 0) { - UIUtil.drawLine(g, x, y + insets.top, x, y + height - 1); + UIUtil.drawLine(g, x, y, x, y + height); + UIUtil.drawLine(g, x + 1, y, x + 1, y + height); } if (insets.right > 0) { - UIUtil.drawLine(g, x + width - 1, y + insets.top, x + width - 1, y + height - 1); + UIUtil.drawLine(g, x + width - 1, y + insets.top, x + width - 1, y + height); + UIUtil.drawLine(g, x + width, y + insets.top, x + width, y + height); } if (insets.bottom > 0) { - UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1); + UIUtil.drawLine(g, x, y + height - 1, x + width, y + height - 1); + UIUtil.drawLine(g, x, y + height, x + width, y + height); } } @@ -354,7 +364,7 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa component = parent; parent = component.getParent(); } - return new Insets(0, anchor == ToolWindowAnchor.RIGHT ? 1 : 0, 0, anchor == ToolWindowAnchor.LEFT ? 1 : 0); + return new Insets(0, anchor == ToolWindowAnchor.RIGHT ? 1 : 0, anchor == ToolWindowAnchor.TOP ? 1 : 0, anchor == ToolWindowAnchor.LEFT ? 1 : 0); } @Override @@ -447,6 +457,10 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa } } } + String separatorText = group.getTemplatePresentation().getText(); + if (children.length > 0 && !StringUtil.isEmpty(separatorText)) { + main.addAction(new Separator(separatorText), Constraints.FIRST); + } } /** @@ -609,135 +623,98 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa private final class MyDivider extends JPanel { private boolean myDragging; private Point myLastPoint; + private Disposable myDisposable; + private IdeGlassPane myGlassPane; + + private final MouseAdapter myListener = new MyMouseAdapter(); - private MyDivider() { - myDragging = false; - enableEvents(MouseEvent.MOUSE_EVENT_MASK | MouseEvent.MOUSE_MOTION_EVENT_MASK); - setBorder(new DividerBorder()); + @Override + public void addNotify() { + super.addNotify(); + myGlassPane = IdeGlassPaneUtil.find(this); + myDisposable = Disposer.newDisposable(); + myGlassPane.addMouseMotionPreprocessor(myListener, myDisposable); + myGlassPane.addMousePreprocessor(myListener, myDisposable); } @Override - protected final void processMouseMotionEvent(final MouseEvent e) { - super.processMouseMotionEvent(e); - if (MouseEvent.MOUSE_DRAGGED == e.getID()) { - myDragging = true; - final ToolWindowAnchor anchor = myInfo.getAnchor(); - final boolean isVerticalCursor = myInfo.isDocked() ? anchor.isSplitVertically() : anchor.isHorizontal(); - setCursor(isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); - final Point point = e.getPoint(); + public void removeNotify() { + super.removeNotify(); + if (myDisposable != null && !Disposer.isDisposed(myDisposable)) { + Disposer.dispose(myDisposable); + } + } + + boolean isInDragZone(MouseEvent e) { + final Point p = SwingUtilities.convertMouseEvent(e.getComponent(), e, this).getPoint(); + return Math.abs(myInfo.getAnchor().isHorizontal() ? p.y : p.x) < 6; + } + + + private class MyMouseAdapter extends MouseAdapter { + + private void updateCursor(MouseEvent e) { + if (isInDragZone(e)) { + myGlassPane.setCursor(MyDivider.this.getCursor(), MyDivider.this); + e.consume(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + myDragging = isInDragZone(e); + updateCursor(e); + } + + @Override + public void mouseClicked(MouseEvent e) { + updateCursor(e); + } + + @Override + public void mouseReleased(MouseEvent e) { + updateCursor(e); + myDragging = false; + } + @Override + public void mouseMoved(MouseEvent e) { + updateCursor(e); + } + + @Override + public void mouseDragged(MouseEvent e) { + if (!myDragging) return; + MouseEvent event = SwingUtilities.convertMouseEvent(e.getComponent(), e, MyDivider.this); + final ToolWindowAnchor anchor = myInfo.getAnchor(); + final Point point = event.getPoint(); final Container windowPane = InternalDecorator.this.getParent(); - myLastPoint = SwingUtilities.convertPoint(this, point, windowPane); + myLastPoint = SwingUtilities.convertPoint(MyDivider.this, point, windowPane); myLastPoint.x = Math.min(Math.max(myLastPoint.x, 0), windowPane.getWidth()); myLastPoint.y = Math.min(Math.max(myLastPoint.y, 0), windowPane.getHeight()); final Rectangle bounds = InternalDecorator.this.getBounds(); if (anchor == ToolWindowAnchor.TOP) { - if (myLastPoint.y < DIVIDER_WIDTH) { - myLastPoint.y = DIVIDER_WIDTH; - } InternalDecorator.this.setBounds(0, 0, bounds.width, myLastPoint.y); } else if (anchor == ToolWindowAnchor.LEFT) { - if (myLastPoint.x < DIVIDER_WIDTH) { - myLastPoint.x = DIVIDER_WIDTH; - } InternalDecorator.this.setBounds(0, 0, myLastPoint.x, bounds.height); } else if (anchor == ToolWindowAnchor.BOTTOM) { - if (myLastPoint.y > windowPane.getHeight() - DIVIDER_WIDTH) { - myLastPoint.y = windowPane.getHeight() - DIVIDER_WIDTH; - } InternalDecorator.this.setBounds(0, myLastPoint.y, bounds.width, windowPane.getHeight() - myLastPoint.y); } else if (anchor == ToolWindowAnchor.RIGHT) { - if (myLastPoint.x > windowPane.getWidth() - DIVIDER_WIDTH) { - myLastPoint.x = windowPane.getWidth() - DIVIDER_WIDTH; - } InternalDecorator.this.setBounds(myLastPoint.x, 0, windowPane.getWidth() - myLastPoint.x, bounds.height); } InternalDecorator.this.validate(); + e.consume(); } } @Override - protected final void processMouseEvent(final MouseEvent e) { - super.processMouseEvent(e); + public Cursor getCursor() { final boolean isVerticalCursor = myInfo.isDocked() ? myInfo.getAnchor().isSplitVertically() : myInfo.getAnchor().isHorizontal(); - switch (e.getID()) { - case MouseEvent.MOUSE_MOVED: - default: - break; - case MouseEvent.MOUSE_ENTERED: - setCursor( - isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); - break; - case MouseEvent.MOUSE_EXITED: - if (!myDragging) { - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - break; - case MouseEvent.MOUSE_PRESSED: - setCursor( - isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); - break; - case MouseEvent.MOUSE_RELEASED: - myDragging = false; - myLastPoint = null; - break; - case MouseEvent.MOUSE_CLICKED: - break; - } - } - - private final class DividerBorder implements Border { - @Override - public final void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) { - final ToolWindowAnchor anchor = myInfo.getAnchor(); - final boolean isVertical = !anchor.isSplitVertically(); - final JBColor outer = new JBColor(Color.white, Color.darkGray); - if (isVertical) { - if (anchor == ToolWindowAnchor.TOP) { - g.setColor(outer); - UIUtil.drawLine(g, x, y, x + width - 1, y); - g.setColor(Color.darkGray); - UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1); - } - else { - g.setColor(Color.darkGray); - UIUtil.drawLine(g, x, y, x + width - 1, y); - g.setColor(outer); - UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1); - } - } - else { - if (anchor == ToolWindowAnchor.LEFT) { - g.setColor(outer); - UIUtil.drawLine(g, x, y, x, y + height - 1); - g.setColor(Color.darkGray); - UIUtil.drawLine(g, x + width - 1, y, x + width - 1, y + height - 1); - } - else { - g.setColor(Color.darkGray); - UIUtil.drawLine(g, x, y, x, y + height - 1); - g.setColor(outer); - UIUtil.drawLine(g, x + width - 1, y, x + width - 1, y + height - 1); - } - } - } - - @Override - public final Insets getBorderInsets(final Component c) { - if (c instanceof MyDivider) { - return new Insets(1, 1, 1, 1); - } - return new Insets(0, 0, 0, 0); - } - - @Override - public final boolean isBorderOpaque() { - return true; - } + return isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java index 19131753b5ba..e1824f775e82 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java @@ -67,7 +67,6 @@ import java.util.Set; */ @State( name = "WindowManager", - roamingType = RoamingType.GLOBAL, storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/window.manager.xml")}) public final class WindowManagerImpl extends WindowManagerEx implements NamedComponent, PersistentStateComponent<Element> { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.WindowManagerImpl"); diff --git a/platform/platform-impl/src/com/intellij/ui/TableExpandableItemsHandler.java b/platform/platform-impl/src/com/intellij/ui/TableExpandableItemsHandler.java index b7704a687441..6099561c7283 100644 --- a/platform/platform-impl/src/com/intellij/ui/TableExpandableItemsHandler.java +++ b/platform/platform-impl/src/com/intellij/ui/TableExpandableItemsHandler.java @@ -16,6 +16,7 @@ package com.intellij.ui; import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -105,8 +106,11 @@ public class TableExpandableItemsHandler extends AbstractExpandableItemsHandler< return myComponent.getCellRect(tableCellKey.row, tableCellKey.column, false); } + @Nullable public Pair<Component, Rectangle> getCellRendererAndBounds(TableCell key) { - if (key.row < 0 || key.row >= myComponent.getRowCount() || key.column < 0 || key.column >= myComponent.getColumnCount()) { + if (key.row < 0 || key.row >= myComponent.getRowCount() || + key.column < 0 || key.column >= myComponent.getColumnCount() || + key.row == myComponent.getEditingRow() && key.column == myComponent.getEditingColumn()) { return null; } diff --git a/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java index d0b110f083bf..2d8bffe98d04 100644 --- a/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java @@ -33,9 +33,8 @@ import com.intellij.ui.components.panels.Wrapper; import com.intellij.ui.content.*; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.SmartList; - +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -191,7 +190,11 @@ public class ContentManagerImpl implements ContentManager, PropertyChangeListene private void doAddContent(@NotNull final Content content, final int index) { ApplicationManager.getApplication().assertIsDispatchThread(); - if (myContents.contains(content)) return; + if (myContents.contains(content)) { + myContents.remove(content); + myContents.add(index == -1 ? myContents.size() : index, content); + return; + } ((ContentImpl)content).setManager(this); final int insertIndex = index == -1 ? myContents.size() : index; @@ -703,6 +706,7 @@ public class ContentManagerImpl implements ContentManager, PropertyChangeListene myContentWithChangedComponent.clear(); myUI = null; myListeners.clear(); + dataProviders.clear(); } @Override diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index ee586418dbd6..37646a19292f 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.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. @@ -94,6 +94,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo myProject = project; } + @Override public void register(final DockContainer container) { myContainers.add(container); Disposer.register(container, new Disposable() { @@ -147,6 +148,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo return wnd != null ? key + "#" + wnd.myId : key; } + @Override public DockContainer getContainerFor(Component c) { if (c == null) return null; @@ -401,7 +403,8 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo }); } - public Pair<FileEditor[], FileEditorProvider[]> createNewDockContainerFor(@NotNull VirtualFile file, FileEditorManagerImpl fileEditorManager) { + @NotNull + public Pair<FileEditor[], FileEditorProvider[]> createNewDockContainerFor(@NotNull VirtualFile file, @NotNull FileEditorManagerImpl fileEditorManager) { DockContainer container = getFactory(DockableEditorContainerFactory.TYPE).createContainer(null); register(container); diff --git a/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java b/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java index 2a70b61c6e8a..47ebb829adc2 100644 --- a/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java @@ -190,7 +190,7 @@ public class JBMacMessages extends MacMessagesEmulation { SheetMessage sheetMessage = new SheetMessage(window, title, message, UIUtil.getQuestionIcon(), new String [] {yesButton, noButton}, doNotAskDialogOption, yesButton, noButton); int result = sheetMessage.getResult().equals(yesButton) ? Messages.YES : Messages.NO; - if (doNotAskDialogOption != null) { + if (doNotAskDialogOption != null && (result == Messages.YES || doNotAskDialogOption.shouldSaveOptionsOnCancel())) { doNotAskDialogOption.setToBeShown(sheetMessage.toBeShown(), result); } return result; diff --git a/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java index 1b9190f6a740..d8b512367f26 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java @@ -189,7 +189,7 @@ public abstract class WizardPopup extends AbstractPopup implements ActionListene targetBounds.x = getParent().getBounds().x - targetBounds.width - STEP_X_PADDING; } } else { - ScreenUtil.moveToFit(targetBounds, ScreenUtil.getScreenRectangle(aScreenX, aScreenY), null); + ScreenUtil.moveToFit(targetBounds, ScreenUtil.getScreenRectangle(aScreenX + 1, aScreenY + 1), null); } if (getParent() == null) { diff --git a/platform/platform-impl/src/com/intellij/ui/win/RecentProjectApplication.java b/platform/platform-impl/src/com/intellij/ui/win/RecentProjectApplication.java index 9561c77b02d6..3ece5a6b77cd 100644 --- a/platform/platform-impl/src/com/intellij/ui/win/RecentProjectApplication.java +++ b/platform/platform-impl/src/com/intellij/ui/win/RecentProjectApplication.java @@ -17,6 +17,7 @@ package com.intellij.ui.win; import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.diff.ApplicationStarterBase; +import org.jetbrains.annotations.Nullable; /** @@ -33,7 +34,7 @@ public class RecentProjectApplication extends ApplicationStarterBase { } @Override - protected void processCommand(String[] args) throws Exception { + protected void processCommand(String[] args, @Nullable String currentDirectory) throws Exception { ProjectUtil.openProject(args[1], null, false); } }
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java b/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java index 60c5f90c8b73..bd0efda42c48 100644 --- a/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java +++ b/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java @@ -22,6 +22,7 @@ import com.intellij.ui.EditorTextField; import com.intellij.ui.components.OrphanGuardian; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FilteringIterator; +import com.intellij.util.ui.JBSwingUtilities; import com.intellij.util.ui.UIUtil; import gnu.trove.TIntStack; import org.jetbrains.annotations.NotNull; @@ -34,7 +35,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; -public class IJSwingUtilities { +public class IJSwingUtilities extends JBSwingUtilities { public static void invoke(Runnable runnable) { if (ApplicationManager.getApplication().isDispatchThread()) { runnable.run(); |