diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ShowReferringObjectsAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ShowReferringObjectsAction.java index 2df05b6b7fe0..68275af275c4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/ShowReferringObjectsAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/ShowReferringObjectsAction.java @@ -47,8 +47,15 @@ public class ShowReferringObjectsAction extends XDebuggerTreeActionBase { @Override protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { - if (node.getValueContainer() instanceof JavaValue) { - JavaValue javaValue = ((JavaValue)node.getValueContainer()); + XValue container = node.getValueContainer(); + JavaValue javaValue = null; + if (container instanceof ReferringObjectsValue) { + javaValue = ((ReferringObjectsValue)container).myJavaValue; + } + else if (container instanceof JavaValue) { + javaValue = ((JavaValue)container); + } + if (javaValue != null) { XDebuggerTree tree = XDebuggerTree.getTree(e.getDataContext()); XInspectDialog dialog = new XInspectDialog(tree.getProject(), tree.getEditorsProvider(), diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplementTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplementTest.java index f9f70d655310..f35df4d389f9 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplementTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/OverrideImplementTest.java @@ -81,7 +81,7 @@ public class OverrideImplementTest extends LightCodeInsightTestCase { CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject()).clone(); try { CommonCodeStyleSettings javaSettings = codeStyleSettings.getCommonSettings(JavaLanguage.INSTANCE); - codeStyleSettings.RIGHT_MARGIN = 80; + javaSettings.RIGHT_MARGIN = 80; javaSettings.KEEP_LINE_BREAKS = true; codeStyleSettings.GENERATE_FINAL_PARAMETERS = true; javaSettings.METHOD_PARAMETERS_WRAP = CommonCodeStyleSettings.WRAP_ON_EVERY_ITEM; @@ -97,7 +97,7 @@ public class OverrideImplementTest extends LightCodeInsightTestCase { CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject()).clone(); try { CommonCodeStyleSettings javaSettings = codeStyleSettings.getCommonSettings(JavaLanguage.INSTANCE); - codeStyleSettings.RIGHT_MARGIN = 80; + javaSettings.RIGHT_MARGIN = 80; javaSettings.KEEP_LINE_BREAKS = false; codeStyleSettings.GENERATE_FINAL_PARAMETERS = false; javaSettings.METHOD_PARAMETERS_WRAP = CommonCodeStyleSettings.WRAP_ON_EVERY_ITEM; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java index a6ac897a1d2d..05c185862399 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java @@ -165,6 +165,10 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget getCommits() { return myCommits; } - - public boolean hasErrors() { - return !myErrors.isEmpty(); - } } diff --git a/platform/dvcs-api/src/com/intellij/dvcs/push/PushSource.java b/platform/dvcs-api/src/com/intellij/dvcs/push/PushSource.java index 8e756d8fe76a..7ae7a33dd6e2 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/push/PushSource.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/push/PushSource.java @@ -15,9 +15,12 @@ */ package com.intellij.dvcs.push; +import org.jetbrains.annotations.NotNull; + /** * Source to push from. For example, local branch for git or branch/bookmark for mercurial. */ public interface PushSource { + @NotNull String getPresentation(); } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java index 5d6b839e5912..2a409e12c854 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java @@ -198,20 +198,20 @@ public class PushController implements Disposable { @Override public void onSuccess() { OutgoingResult outgoing = result.get(); - if (outgoing.hasErrors()) { - final CommitLoader loader = new CommitLoader() { - @Override - public void reloadCommits() { - loadCommits(model, node, false); - } - }; - myPushLog.setChildren(node, ContainerUtil.map(outgoing.getErrors(), new Function() { + List errors = outgoing.getErrors(); + if (!errors.isEmpty()) { + myPushLog.setChildren(node, ContainerUtil.map(errors, new Function() { @Override public DefaultMutableTreeNode fun(final VcsError error) { VcsLinkedText errorLinkText = new VcsLinkedText(error.getText(), new VcsLinkListener() { @Override public void hyperlinkActivated(@NotNull DefaultMutableTreeNode sourceNode) { - error.handleError(loader); + error.handleError(new CommitLoader() { + @Override + public void reloadCommits() { + loadCommits(model, node, false); + } + }); } }); return new TextWithLinkNode(errorLinkText); @@ -347,13 +347,13 @@ public class PushController implements Disposable { return additionalPanels; } - private boolean hasRepoForPushSupport(@NotNull PushSupport support) { - for (MyRepoModel model : myView2Model.values()) { - if (support.equals(model.getSupport())) { - return true; + private boolean hasRepoForPushSupport(@NotNull final PushSupport support) { + return ContainerUtil.exists(myView2Model.values(), new Condition() { + @Override + public boolean value(MyRepoModel model) { + return support.equals(model.getSupport()); } - } - return false; + }); } private static class MyRepoModel { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index 3b935745a212..44b4c8c5c3f0 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -34,13 +34,10 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -/** - * @author Nadya Zabrodina - */ public class BranchActionGroupPopup extends PopupFactoryImpl.ActionGroupPopup { public BranchActionGroupPopup(@NotNull String title, @NotNull Project project, @NotNull Condition preselectActionCondition, @NotNull ActionGroup actions) { - super(title, actions, SimpleDataContext.getProjectContext(project), false, false, false, false, null, -1, + super(title, actions, SimpleDataContext.getProjectContext(project), false, false, true, false, null, -1, preselectActionCondition, null); } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/NewBranchAction.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/NewBranchAction.java index 29481e236c2a..92ba9515b5d6 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/NewBranchAction.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/NewBranchAction.java @@ -25,9 +25,6 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -/** - * @author Nadya Zabrodina - */ public abstract class NewBranchAction extends DumbAwareAction { protected final List myRepositories; protected Project myProject; @@ -43,7 +40,7 @@ public abstract class NewBranchAction extends DumbAwareAct public void update(AnActionEvent e) { if (DvcsUtil.anyRepositoryIsFresh(myRepositories)) { e.getPresentation().setEnabled(false); - e.getPresentation().setDescription("Checkout of a new branch is not possible before the first commit."); + e.getPresentation().setDescription("Checkout of a new branch is not possible before the first commit"); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/EditorLinePainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/EditorLinePainter.java index 7e669808d0d6..420a0fdf23a7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/EditorLinePainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/EditorLinePainter.java @@ -18,6 +18,7 @@ package com.intellij.openapi.editor; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -27,5 +28,5 @@ import java.util.Collection; public abstract class EditorLinePainter { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.editor.linePainter"); - public abstract Collection getLineExtensions(Project project, VirtualFile file, int lineNumber); + public abstract Collection getLineExtensions(@NotNull Project project, @NotNull VirtualFile file, int lineNumber); } 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 2a1d8829bc27..6160ea58dcdf 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 @@ -2767,16 +2767,19 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (collapsedFolderAt == null) { int i = drawStringWithSoftWraps(g, chars, start, lEnd - lIterator.getSeparatorLength(), position, clip, effectColor, effectType, fontType, currentColor, logicalPosition); - for (EditorLinePainter painter : EditorLinePainter.EP_NAME.getExtensions()) { - Collection extensions = painter.getLineExtensions(myProject, getVirtualFile(), lIterator.getLineNumber()); - if (extensions != null && !extensions.isEmpty()) { - for (LineExtensionInfo info : extensions) { - drawStringWithSoftWraps(g, info.getText(), 0, info.getText().length(), position, clip, - info.getEffectColor() == null ? effectColor : info.getEffectColor(), - info.getEffectType() == null ? effectType : info.getEffectType(), - info.getFontType(), - info.getColor() == null ? currentColor : info.getColor(), - logicalPosition); + final VirtualFile file = getVirtualFile(); + if (myProject != null && file != null && !isOneLineMode()) { + for (EditorLinePainter painter : EditorLinePainter.EP_NAME.getExtensions()) { + Collection extensions = painter.getLineExtensions(myProject, file, lIterator.getLineNumber()); + if (extensions != null && !extensions.isEmpty()) { + for (LineExtensionInfo info : extensions) { + drawStringWithSoftWraps(g, info.getText(), 0, info.getText().length(), position, clip, + info.getEffectColor() == null ? effectColor : info.getEffectColor(), + info.getEffectType() == null ? effectType : info.getEffectType(), + info.getFontType(), + info.getColor() == null ? currentColor : info.getColor(), + logicalPosition); + } } } } 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 07e5a93670e6..271539f038a0 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 @@ -49,6 +49,7 @@ import com.intellij.ui.navigation.History; import com.intellij.ui.navigation.Place; import com.intellij.ui.speedSearch.ElementFilter; import com.intellij.ui.treeStructure.SimpleNode; +import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.MergingUpdateQueue; @@ -87,6 +88,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat private final History myHistory = new History(this); private final OptionsTree myTree; + private final SettingsTreeView myTreeView; private final MySearchField mySearch; private final Splitter myMainSplitter; //[back/forward] JComponent myToolbarComponent; @@ -126,7 +128,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat mySearch = new MySearchField() { @Override protected void onTextKeyEvent(final KeyEvent e) { - myTree.processTextEvent(e); + if (myTreeView != null) { + myTreeView.myTree.processKeyEvent(e); + } + else { + myTree.processTextEvent(e); + } } }; @@ -144,12 +151,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } }); - myTree = new OptionsTree(myProject, groups, getContext()) { + final KeyListener listener = new KeyListener() { @Override - protected void onTreeKeyEvent(final KeyEvent e) { + public void keyTyped(KeyEvent event) { myFilterDocumentWasChanged = false; try { - mySearch.keyEventToTextField(e); + mySearch.keyEventToTextField(event); } finally { if (myFilterDocumentWasChanged && !isFilterFieldVisible()) { @@ -157,10 +164,33 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } } } - }; - getContext().addColleague(myTree); - Disposer.register(this, myTree); + @Override + public void keyPressed(KeyEvent event) { + keyTyped(event); + } + + @Override + public void keyReleased(KeyEvent event) { + keyTyped(event); + } + }; + if (Registry.is("ide.file.settings.tree.new")) { + myTreeView = new SettingsTreeView(listener, getContext(), groups); + myTree = null; + } + else { + myTreeView = null; + myTree = new OptionsTree(myProject, groups, getContext()) { + @Override + protected void onTreeKeyEvent(final KeyEvent e) { + listener.keyTyped(e); + } + }; + } + + getContext().addColleague(myTreeView != null ? myTreeView : myTree); + Disposer.register(this, myTreeView != null ? myTreeView : myTree); mySearch.addDocumentListener(new DocumentAdapter() { @Override protected void textChanged(DocumentEvent e) { @@ -198,7 +228,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat @Override public Dimension getMinimumSize() { Dimension dimension = super.getMinimumSize(); - dimension.width = Math.max(myTree.getMinimumSize().width, mySearchWrapper.getPreferredSize().width); + JComponent component = myTreeView != null ? myTreeView : myTree; + dimension.width = Math.max(component.getMinimumSize().width, mySearchWrapper.getPreferredSize().width); return dimension; } }; @@ -211,7 +242,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat */ myLeftSide.add(mySearchWrapper, BorderLayout.NORTH); - myLeftSide.add(myTree, BorderLayout.CENTER); + myLeftSide.add(myTreeView != null ? myTreeView : myTree, BorderLayout.CENTER); setLayout(new BorderLayout()); @@ -233,9 +264,19 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat mySpotlightUpdate = new MergingUpdateQueue("OptionsSpotlight", 200, false, this, this, this); if (preselectedConfigurable != null) { - myTree.select(preselectedConfigurable); + if (myTreeView != null) { + myTreeView.select(preselectedConfigurable); + } + else { + myTree.select(preselectedConfigurable); + } } else { - myTree.selectFirst(); + if (myTreeView != null) { + myTreeView.selectFirst(); + } + else { + myTree.selectFirst(); + } } Toolkit.getDefaultToolkit().addAWTEventListener(this, @@ -295,12 +336,16 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat @Deprecated @Nullable public T findConfigurable(Class configurableClass) { - return myTree.findConfigurable(configurableClass); + return myTreeView != null + ? myTreeView.findConfigurable(configurableClass) + : myTree.findConfigurable(configurableClass); } @Nullable public SearchableConfigurable findConfigurableById(@NotNull String configurableId) { - return myTree.findConfigurableById(configurableId); + return myTreeView != null + ? myTreeView.findConfigurableById(configurableId) + : myTree.findConfigurableById(configurableId); } public ActionCallback clearSearchAndSelect(Configurable configurable) { @@ -318,7 +363,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat public ActionCallback select(Configurable configurable, final String text) { myFilter.refilterFor(text, false, true); - return myTree.select(configurable); + return myTreeView != null + ? myTreeView.select(configurable) + : myTree.select(configurable); } private float readProportion(final float defaultValue, final String propertyName) { @@ -367,7 +414,10 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myOwnDetails.setContent(myContentWrapper); myOwnDetails.setBannerMinHeight(mySearchWrapper.getHeight()); myOwnDetails.setText(getBannerText(configurable)); - if (Registry.is("ide.file.settings.order.new")) { + if (myTreeView != null) { + myOwnDetails.forProject(myTreeView.findConfigurableProject(configurable)); + } + else if (Registry.is("ide.file.settings.order.new")) { myOwnDetails.forProject(myTree.getConfigurableProject(configurable)); } @@ -385,7 +435,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat checkModified(oldConfigurable); checkModified(configurable); - if (myTree.myBuilder.getSelectedElements().size() == 0) { + FilteringTreeBuilder builder = myTreeView != null ? myTreeView.myBuilder : myTree.myBuilder; + if (builder.getSelectedElements().size() == 0) { select(configurable).notify(result); } else { result.setDone(); @@ -507,6 +558,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } private String[] getBannerText(Configurable configurable) { + if (myTreeView != null) { + return myTreeView.getPathNames(configurable); + } final List list = myTree.getPathToRoot(configurable); final String[] result = new String[list.size()]; int add = 0; @@ -795,7 +849,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat getContext().fireErrorsChanged(errors, null); if (!errors.isEmpty()) { - myTree.select(errors.keySet().iterator().next()); + if (myTreeView != null) { + myTreeView.select(errors.keySet().iterator().next()); + } + else { + myTree.select(errors.keySet().iterator().next()); + } } } @@ -835,7 +894,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat return myFiltered.contains(node.getConfigurable()) || isChildOfNameHit(node); } - return true; + return SettingsTreeView.isFiltered(myFiltered, myHits, value); } private boolean isChildOfNameHit(OptionsTree.EditorNode node) { @@ -929,7 +988,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myLastSelected = current; } - final ActionCallback callback = fireUpdate(adjustSelection ? myTree.findNodeFor(toSelect) : null, adjustSelection, now); + SimpleNode node = !adjustSelection ? null : myTreeView != null ? myTreeView.findNode(toSelect) : myTree.findNodeFor(toSelect); + final ActionCallback callback = fireUpdate(node, adjustSelection, now); myFilterDocumentWasChanged = true; @@ -965,7 +1025,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myFilter.refilterFor(filter, false, true).doWhenDone(new Runnable() { @Override public void run() { - myTree.select(config).notifyWhenDone(result); + if (myTreeView != null) { + myTreeView.select(config).notifyWhenDone(result); + } + else { + myTree.select(config).notifyWhenDone(result); + } } }); 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 new file mode 100644 index 000000000000..9269da7ab967 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java @@ -0,0 +1,864 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.options.newEditor; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.ui.search.ConfigurableHit; +import com.intellij.ide.util.treeView.NodeDescriptor; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.options.*; +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; +import com.intellij.ui.*; +import com.intellij.ui.treeStructure.*; +import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; +import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ui.GraphicsUtil; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.tree.TreeUtil; +import com.intellij.util.ui.update.MergingUpdateQueue; +import com.intellij.util.ui.update.Update; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; +import javax.swing.*; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.plaf.TreeUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +/** + * @author Sergey.Malenkov + */ +final class SettingsTreeView extends JComponent implements Disposable, OptionsEditorColleague { + final SimpleTree myTree; + final FilteringTreeBuilder myBuilder; + + private final OptionsEditorContext myContext; + private final MyRoot myRoot; + private final JScrollPane myScroller; + private JLabel mySeparator; + private final MyRenderer myRenderer = new MyRenderer(); + private final IdentityHashMap myConfigurableToNodeMap = new IdentityHashMap(); + private final MergingUpdateQueue myQueue = new MergingUpdateQueue("OptionsTree", 150, false, this, this, this).setRestartTimerOnAdd(true); + + private Configurable myQueuedConfigurable; + + SettingsTreeView(final KeyListener listener, OptionsEditorContext context, ConfigurableGroup... groups) { + myContext = context; + myRoot = new MyRoot(groups); + + myTree = new MyTree(); + myTree.getInputMap().clear(); + TreeUtil.installActions(myTree); + + myTree.setOpaque(true); + myTree.setBorder(BorderFactory.createEmptyBorder(0, 1, 0, 0)); + + myTree.setRowHeight(-1); + myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + + myTree.setCellRenderer(myRenderer); + myTree.setRootVisible(false); + myTree.setShowsRootHandles(false); + + myScroller = ScrollPaneFactory.createScrollPane(myTree); + myScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + add(myScroller); + + myTree.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + myBuilder.revalidateTree(); + } + + @Override + public void componentMoved(ComponentEvent e) { + myBuilder.revalidateTree(); + } + + @Override + public void componentShown(ComponentEvent e) { + myBuilder.revalidateTree(); + } + }); + + myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { + public void valueChanged(TreeSelectionEvent event) { + MyNode node = extractNode(event.getNewLeadSelectionPath()); + select(node == null ? null : node.myConfigurable); + } + }); + + myTree.addKeyListener(new KeyListener() { + public void keyTyped(KeyEvent event) { + if (listener != null && isValid(event)) { + listener.keyTyped(event); + } + } + + public void keyPressed(KeyEvent event) { + if (listener != null && isValid(event)) { + listener.keyPressed(event); + } + } + + public void keyReleased(KeyEvent event) { + if (listener != null && isValid(event)) { + listener.keyReleased(event); + } + } + + private boolean isValid(KeyEvent event) { + return null == myTree.getInputMap().get(KeyStroke.getKeyStrokeForEvent(event)); + } + }); + myBuilder = new MyBuilder(new SimpleTreeStructure.Impl(myRoot)); + myBuilder.setFilteringMerge(300, null); + Disposer.register(this, myBuilder); + } + + @NotNull + String[] getPathNames(Configurable configurable) { + ArrayDeque path = new ArrayDeque(); + MyNode node = myConfigurableToNodeMap.get(configurable); + while (node != null) { + path.push(node.myDisplayName); + SimpleNode parent = node.getParent(); + node = parent instanceof MyNode + ? (MyNode)parent + : null; + } + return ArrayUtil.toStringArray(path); + } + + @Nullable + SimpleNode findNode(Configurable toSelect) { + return myConfigurableToNodeMap.get(toSelect); + } + + @Nullable + SearchableConfigurable findConfigurableById(@NotNull String id) { + for (Configurable configurable : myConfigurableToNodeMap.keySet()) { + if (configurable instanceof SearchableConfigurable) { + SearchableConfigurable searchable = (SearchableConfigurable)configurable; + if (id.equals(searchable.getId())) { + return searchable; + } + } + } + return null; + } + + @Nullable + T findConfigurable(@NotNull Class type) { + for (UnnamedConfigurable configurable : myConfigurableToNodeMap.keySet()) { + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + configurable = wrapper.getConfigurable(); + } + if (type.isInstance(configurable)) { + return type.cast(configurable); + } + } + return null; + } + + @Nullable + Project findConfigurableProject(@Nullable Configurable configurable) { + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + return wrapper.getExtensionPoint().getProject(); + } + return findConfigurableProject(myConfigurableToNodeMap.get(configurable)); + } + + @Nullable + private static Project findConfigurableProject(@Nullable MyNode node) { + if (node != null) { + Configurable configurable = node.myConfigurable; + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + return wrapper.getExtensionPoint().getProject(); + } + SimpleNode parent = node.getParent(); + if (parent instanceof MyNode) { + return findConfigurableProject((MyNode)parent); + } + } + return null; + } + + @Nullable + private ConfigurableGroup findConfigurableGroupAt(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; + } + path = path.getParentPath(); + } + return null; + } + + @Nullable + private static MyNode extractNode(@Nullable Object object) { + if (object instanceof TreePath) { + TreePath path = (TreePath)object; + object = path.getLastPathComponent(); + } + if (object instanceof DefaultMutableTreeNode) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode)object; + object = node.getUserObject(); + } + if (object instanceof FilteringTreeStructure.FilteringNode) { + FilteringTreeStructure.FilteringNode node = (FilteringTreeStructure.FilteringNode)object; + object = node.getDelegate(); + } + return object instanceof MyNode + ? (MyNode)object + : null; + } + + static boolean isFiltered(Set configurables, ConfigurableHit hits, SimpleNode value) { + if (value instanceof MyNode && !configurables.contains(((MyNode)value).myConfigurable)) { + if (hits != null) { + configurables = hits.getNameFullHits(); + while (value != null) { + if (value instanceof MyNode) { + if (configurables.contains(((MyNode)value).myConfigurable)) { + return true; + } + } + value = value.getParent(); + } + } + return false; + } + return true; + } + + @Override + public void doLayout() { + myScroller.setBounds(0, 0, getWidth(), getHeight()); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + + if (mySeparator == null) { + mySeparator = new JLabel(); + mySeparator.setFont(UIUtil.getLabelFont()); + mySeparator.setFont(getFont().deriveFont(Font.BOLD)); + } + ConfigurableGroup group = findConfigurableGroupAt(0, 5 + mySeparator.getFont().getSize()); + if (group != null && group == findConfigurableGroupAt(0, -5)) { + int offset = UIUtil.isUnderNativeMacLookAndFeel() ? 1 : 3; + mySeparator.setBorder(BorderFactory.createEmptyBorder(offset, 18, offset, 3)); + mySeparator.setText(group.getDisplayName()); + + Rectangle bounds = myScroller.getViewport().getBounds(); + int height = mySeparator.getPreferredSize().height; + if (bounds.height > height) { + bounds.height = height; + } + g.setColor(myTree.getBackground()); + if (g instanceof Graphics2D) { + int h = bounds.height / 4; + int y = bounds.y + bounds.height - h; + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height - h); + ((Graphics2D)g).setPaint(UIUtil.getGradientPaint( + 0, y, g.getColor(), + 0, y + h, ColorUtil.toAlpha(g.getColor(), 0))); + g.fillRect(bounds.x, y, bounds.width, h + h); + } + else { + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + } + mySeparator.setSize(bounds.width - 1, bounds.height); + mySeparator.paint(g.create(bounds.x + 1, bounds.y, bounds.width - 1, bounds.height)); + } + } + + void selectFirst() { + for (ConfigurableGroup eachGroup : myRoot.myGroups) { + Configurable[] kids = eachGroup.getConfigurables(); + if (kids.length > 0) { + select(kids[0]); + return; + } + } + } + + ActionCallback select(@Nullable final Configurable configurable) { + if (myBuilder.isSelectionBeingAdjusted()) { + return new ActionCallback.Rejected(); + } + final ActionCallback callback = new ActionCallback(); + myQueuedConfigurable = configurable; + myQueue.queue(new Update(this) { + public void run() { + if (configurable == myQueuedConfigurable) { + if (configurable == null) { + fireSelected(null, callback); + } + else { + myBuilder.getReady(this).doWhenDone(new Runnable() { + @Override + public void run() { + if (configurable != myQueuedConfigurable) return; + + MyNode editorNode = myConfigurableToNodeMap.get(configurable); + FilteringTreeStructure.FilteringNode editorUiNode = myBuilder.getVisibleNodeFor(editorNode); + if (editorUiNode == null) return; + + if (!myBuilder.getSelectedElements().contains(editorUiNode)) { + myBuilder.select(editorUiNode, new Runnable() { + public void run() { + fireSelected(configurable, callback); + } + }); + } + else { + myBuilder.scrollSelectionToVisible(new Runnable() { + public void run() { + fireSelected(configurable, callback); + } + }, false); + } + } + }); + } + } + } + + @Override + public void setRejected() { + super.setRejected(); + callback.setRejected(); + } + }); + return callback; + } + + private void fireSelected(Configurable configurable, ActionCallback callback) { + myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); + } + + @Override + public void dispose() { + myQueuedConfigurable = null; + } + + @Override + public ActionCallback onSelected(@Nullable Configurable configurable, Configurable oldConfigurable) { + return select(configurable); + } + + @Override + public ActionCallback onModifiedAdded(Configurable configurable) { + myTree.repaint(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback onModifiedRemoved(Configurable configurable) { + myTree.repaint(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback onErrorsChanged() { + return new ActionCallback.Done(); + } + + private final class MyRoot extends CachingSimpleNode { + private final ConfigurableGroup[] myGroups; + + private MyRoot(ConfigurableGroup[] groups) { + super(null); + myGroups = groups; + } + + @Override + protected SimpleNode[] buildChildren() { + 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]); + } + return result; + } + } + + private final class MyNode extends CachingSimpleNode { + private final Configurable.Composite myComposite; + private final Configurable myConfigurable; + private final String myDisplayName; + + private MyNode(CachingSimpleNode parent, Configurable configurable) { + 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() + " }"; + + myConfigurableToNodeMap.put(configurable, this); + } + + private MyNode(CachingSimpleNode parent, ConfigurableGroup group) { + super(parent); + myComposite = group; + myConfigurable = null; + String name = group.getDisplayName(); + myDisplayName = name != null ? name.replace("\n", " ") : "{ " + group.getClass().getSimpleName() + " }"; + } + + @Override + protected SimpleNode[] buildChildren() { + if (myComposite == null) { + return NO_CHILDREN; + } + Configurable[] configurables = myComposite.getConfigurables(); + if (configurables == null || configurables.length == 0) { + return NO_CHILDREN; + } + SimpleNode[] result = new SimpleNode[configurables.length]; + for (int i = 0; i < configurables.length; i++) { + result[i] = new MyNode(this, configurables[i]); + if (myConfigurable != null) { + myContext.registerKid(myConfigurable, configurables[i]); + } + } + return result; + } + + @Override + public boolean isAlwaysLeaf() { + return myComposite == null; + } + + @Override + public int getWeight() { + return WeightBasedComparator.UNDEFINED_WEIGHT; + } + } + + private final class MyRenderer extends GroupedElementsRenderer.Tree { + private JLabel myNodeIcon; + private JLabel myProjectIcon; + + protected JComponent createItemComponent() { + myTextLabel = new ErrorLabel(); + return myTextLabel; + } + + @Override + protected void layout() { + myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); + myProjectIcon = new JLabel(" ", SwingConstants.LEFT); + myProjectIcon.setOpaque(true); + myRendererComponent.add(BorderLayout.NORTH, mySeparatorComponent); + myRendererComponent.add(BorderLayout.CENTER, myComponent); + myRendererComponent.add(BorderLayout.WEST, myNodeIcon); + myRendererComponent.add(BorderLayout.EAST, myProjectIcon); + } + + public Component getTreeCellRendererComponent(JTree tree, + Object value, + boolean selected, + boolean expanded, + boolean leaf, + int row, + boolean focused) { + myTextLabel.setOpaque(selected); + myTextLabel.setFont(UIUtil.getLabelFont()); + + String text; + boolean hasSeparatorAbove = false; + int preferredForcedWidth = -1; + + MyNode node = extractNode(value); + if (node == null) { + text = value.toString(); + } + else { + text = node.myDisplayName; + // show groups in bold + if (myRoot == node.getParent()) { + hasSeparatorAbove = node != myRoot.getChildAt(0); + myTextLabel.setFont(myTextLabel.getFont().deriveFont(Font.BOLD)); + } + TreePath path = tree.getPathForRow(row); + 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(); + + Insets treeInsets = tree.getInsets(); + + int indent = (left + right) * nestingLevel + (treeInsets != null ? treeInsets.left + treeInsets.right : 0); + + forcedWidth = visibleRect.width > 0 ? visibleRect.width - indent : forcedWidth; + } + preferredForcedWidth = forcedWidth - 4; + } + Component result = configureComponent(text, null, null, null, selected, hasSeparatorAbove, null, preferredForcedWidth); + // update font color for modified configurables + if (!selected && node != null) { + Configurable configurable = node.myConfigurable; + if (configurable != null) { + if (myContext.getErrors().containsKey(configurable)) { + myTextLabel.setForeground(JBColor.RED); + } + else if (myContext.getModified().contains(configurable)) { + myTextLabel.setForeground(JBColor.BLUE); + } + } + } + // configure project icon + Project project = null; + if (node != null) { + SimpleNode parent = node.getParent(); + 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 (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) { + project = findConfigurableProject(node); // special case for custom subgroups + } + } + } + } + if (project != null) { + myProjectIcon.setIcon(selected + ? AllIcons.General.ProjectConfigurableSelected + : AllIcons.General.ProjectConfigurable); + myProjectIcon.setToolTipText(OptionsBundle.message(project.isDefault() + ? "configurable.default.project.tooltip" + : "configurable.current.project.tooltip")); + myProjectIcon.setBackground(myTextLabel.getBackground()); + myProjectIcon.setVisible(true); + } + else { + 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); + } + return result; + } + + + 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; + } + } + + private final class MyTree extends SimpleTree { + @Override + public String getToolTipText(MouseEvent event) { + if (event != null) { + Component component = getDeepestRendererComponentAt(event.getX(), event.getY()); + if (component instanceof JLabel) { + JLabel label = (JLabel)component; + if (label.getIcon() != null) { + String text = label.getToolTipText(); + if (text != null) { + return text; + } + } + } + } + return super.getToolTipText(event); + } + + @Override + protected boolean paintNodes() { + return false; + } + + @Override + protected boolean highlightSingleNode() { + return false; + } + + @Override + public void setUI(TreeUI ui) { + TreeUI actualUI = ui; + if (!(ui instanceof MyTreeUi)) { + actualUI = new MyTreeUi(); + } + super.setUI(actualUI); + } + + @Override + protected boolean isCustomUI() { + return true; + } + + @Override + protected void configureUiHelper(TreeUIHelper helper) { + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + + @Override + public void processKeyEvent(KeyEvent e) { + TreePath path = myTree.getSelectionPath(); + if (path != null) { + if (e.getKeyCode() == KeyEvent.VK_LEFT) { + if (isExpanded(path)) { + collapsePath(path); + return; + } + } + else if (e.getKeyCode() == KeyEvent.VK_RIGHT) { + if (isCollapsed(path)) { + expandPath(path); + return; + } + } + } + super.processKeyEvent(e); + } + + @Override + protected void processMouseEvent(MouseEvent e) { + 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; + } + } + } + } + + super.processMouseEvent(e); + } + + private final class MyTreeUi extends BasicTreeUI { + + @Override + public void toggleExpandState(TreePath path) { + super.toggleExpandState(path); + } + + @Override + public boolean isToggleEvent(MouseEvent event) { + return super.isToggleEvent(event); + } + + @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 paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { + } + + @Override + public void paint(Graphics g, JComponent c) { + GraphicsUtil.setupAntialiasing(g); + super.paint(g, c); + } + } + } + + private final class MyBuilder extends FilteringTreeBuilder { + + List myToExpandOnResetFilter; + boolean myRefilteringNow; + boolean myWasHoldingFilter; + + public MyBuilder(SimpleTreeStructure structure) { + super(myTree, myContext.getFilter(), structure, new WeightBasedComparator(false)); + myTree.addTreeExpansionListener(new TreeExpansionListener() { + public void treeExpanded(TreeExpansionEvent event) { + invalidateExpansions(); + } + + public void treeCollapsed(TreeExpansionEvent event) { + invalidateExpansions(); + } + }); + } + + private void invalidateExpansions() { + if (!myRefilteringNow) { + myToExpandOnResetFilter = null; + } + } + + @Override + protected boolean isSelectable(Object object) { + return object instanceof MyNode; + } + + @Override + public boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) { + return myContext.isHoldingFilter(); + } + + @Override + public boolean isToEnsureSelectionOnFocusGained() { + return false; + } + + @Override + protected ActionCallback refilterNow(Object preferredSelection, boolean adjustSelection) { + final List toRestore = new ArrayList(); + if (myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { + myToExpandOnResetFilter = myBuilder.getUi().getExpandedElements(); + } + else if (!myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { + toRestore.addAll(myToExpandOnResetFilter); + myToExpandOnResetFilter = null; + } + + myWasHoldingFilter = myContext.isHoldingFilter(); + + ActionCallback result = super.refilterNow(preferredSelection, adjustSelection); + myRefilteringNow = true; + return result.doWhenDone(new Runnable() { + public void run() { + myRefilteringNow = false; + if (!myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { + restoreExpandedState(toRestore); + } + } + }); + } + + private void restoreExpandedState(List toRestore) { + TreePath[] selected = myTree.getSelectionPaths(); + if (selected == null) { + selected = new TreePath[0]; + } + + List toCollapse = new ArrayList(); + + for (int eachRow = 0; eachRow < myTree.getRowCount(); eachRow++) { + if (!myTree.isExpanded(eachRow)) continue; + + TreePath eachVisiblePath = myTree.getPathForRow(eachRow); + if (eachVisiblePath == null) continue; + + Object eachElement = myBuilder.getElementFor(eachVisiblePath.getLastPathComponent()); + if (toRestore.contains(eachElement)) continue; + + + for (TreePath eachSelected : selected) { + if (!eachVisiblePath.isDescendant(eachSelected)) { + toCollapse.add(eachVisiblePath); + } + } + } + + for (TreePath each : toCollapse) { + myTree.collapsePath(each); + } + } + } +} diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 6d85b6498843..e9802f2321ae 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -394,6 +394,7 @@ console.too.much.text.buffer.ratio.description=Used for disabling of console pro when there is too much of text to process.\n\ The ratio is used against the console cycle buffer size (idea.cycle.buffer.size/theRatio=maxTextLength). ide.file.settings.order.new=false +ide.file.settings.tree.new=false ide.new.project.settings=true ide.new.project.settings.description=Temporary key for new project settings dialog UI diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java index 68448f86806c..a3aa8ec43fc5 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java @@ -27,6 +27,7 @@ import com.intellij.xdebugger.frame.presentation.XValuePresentation; import com.intellij.xdebugger.impl.frame.XVariablesView; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueTextRendererImpl; +import org.jetbrains.annotations.NotNull; import java.awt.*; import java.util.ArrayList; @@ -39,7 +40,7 @@ import java.util.Set; */ public class XDebuggerEditorLinePainter extends EditorLinePainter { @Override - public Collection getLineExtensions(Project project, VirtualFile file, int lineNumber) { + public Collection getLineExtensions(@NotNull Project project, @NotNull VirtualFile file, int lineNumber) { if (!Registry.is("ide.debugger.inline")) { return null; } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugView.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugView.java index d26258d39d2a..0007ec2f9997 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugView.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugView.java @@ -20,7 +20,7 @@ import com.intellij.ide.DataManager; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.ui.content.ContentManager; -import com.intellij.util.Alarm; +import com.intellij.util.SingleAlarm; import com.intellij.xdebugger.XDebugSession; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,26 +34,29 @@ import java.util.EventObject; public abstract class XDebugView implements Disposable { public enum SessionEvent {PAUSED, BEFORE_RESUME, RESUMED, STOPPED, FRAME_CHANGED, SETTINGS_CHANGED} - private final Alarm myUpdateAlarm; - private static final int VIEW_UPDATE_DELAY = 100; //ms + private final SingleAlarm myClearAlarm; + private static final int VIEW_CLEAR_DELAY = 100; //ms public XDebugView() { - myUpdateAlarm = new Alarm(this); + myClearAlarm = new SingleAlarm(new Runnable() { + @Override + public void run() { + clear(); + } + }, VIEW_CLEAR_DELAY, this); + } + + protected final void requestClear() { + myClearAlarm.cancelAndRequest(); + } + + protected final void cancelClear() { + myClearAlarm.cancel(); } protected abstract void clear(); - public void onSessionEvent(@NotNull final SessionEvent event) { - myUpdateAlarm.cancelAllRequests(); - myUpdateAlarm.addRequest(new Runnable() { - @Override - public void run() { - processSessionEvent(event); - } - }, VIEW_UPDATE_DELAY); - } - - protected abstract void processSessionEvent(@NotNull SessionEvent event); + public abstract void processSessionEvent(@NotNull SessionEvent event); @Nullable protected static XDebugSession getSession(@NotNull EventObject e) { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugViewSessionListener.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugViewSessionListener.java index 2e21ed0f2248..d790113b1816 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugViewSessionListener.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugViewSessionListener.java @@ -36,7 +36,7 @@ public class XDebugViewSessionListener extends XDebugSessionAdapter { AppUIUtil.invokeLaterIfProjectAlive(session.getProject(), new Runnable() { @Override public void run() { - myDebugView.onSessionEvent(event); + myDebugView.processSessionEvent(event); } }); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java index 6ab8acd1152e..b98440b5d69f 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java @@ -221,11 +221,13 @@ public class XFramesView extends XDebugView { mySelectedStack = null; XSuspendContext suspendContext = session == null ? null : session.getSuspendContext(); if (suspendContext == null) { - clear(); + requestClear(); return; } if (event == SessionEvent.PAUSED) { + // clear immediately + cancelClear(); clear(); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesView.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesView.java index c1232b572433..df43b1147419 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesView.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesView.java @@ -57,10 +57,11 @@ public class XVariablesView extends XVariablesViewBase { tree.markNodesObsolete(); if (stackFrame != null) { + cancelClear(); buildTreeAndRestoreState(stackFrame); } else { - clear(); + requestClear(); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java index b8d088d79231..c510cd17c90a 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java @@ -275,6 +275,7 @@ public class XWatchesViewImpl extends XDebugView implements DnDNativeTarget, XWa XDebugSession session = getSession(getMainPanel()); XStackFrame stackFrame = session == null ? null : session.getCurrentStackFrame(); if (stackFrame != null) { + cancelClear(); tree.setSourcePosition(stackFrame.getSourcePosition()); myRootNode.updateWatches(stackFrame.getEvaluator()); if (myTreeState != null) { @@ -282,7 +283,7 @@ public class XWatchesViewImpl extends XDebugView implements DnDNativeTarget, XWa } } else { - clear(); + requestClear(); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java index d7e2354835fd..4c3766f23f5d 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java @@ -197,7 +197,7 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { @Override public void run() { for (XDebugView view : myViews) { - view.onSessionEvent(XDebugView.SessionEvent.SETTINGS_CHANGED); + view.processSessionEvent(XDebugView.SessionEvent.SETTINGS_CHANGED); } } }); diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index 03803fbf985c..f89657535f8a 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -416,20 +416,6 @@ unstash.unstashing=Unstashing... unstash.view.tooltip=View selected stash unstash.view=&View unstashing.title=UnStashing changes... -update.locally.modified.files.tooltip=Locally modified files. -update.locally.modified.files=&Files: -update.locally.modified.git.root=Git Root: -update.locally.modified.message=

The following files under this root are locally modified.
\ - Possible reasons: uncommitted changes; a problem with crlf conversion; {0} configuration file auto-save.

\ -

    \ -
  • Press Revert Files to discard these local changes and continue the update process.
  • \ -
  • Press Cancel to cancel the update process.
    Use Auto-Stash option to stash local changes before update and restore them after it.
  • \ -

\ - -update.locally.modified.rescan.tooltip=Rescan the repository to check for locally modified files again.
Use this button if you have resolved the problem manually. -update.locally.modified.rescan=Re&scan -update.locally.modified.revert=Revert Files -update.locally.modified.title=Locally modified files are detected update.options.display.name=Git Update Settings update.options.no.commit=No &Commit update.options.save.before.update=Clean working tree before update diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaser.java b/plugins/git4idea/src/git4idea/rebase/GitRebaser.java index aabf913fbf61..d5a0470d1667 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaser.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaser.java @@ -30,6 +30,7 @@ import git4idea.commands.*; import git4idea.merge.GitConflictResolver; import git4idea.update.GitUpdateResult; import git4idea.util.GitUIUtil; +import git4idea.util.LocalChangesWouldBeOverwrittenHelper; import git4idea.util.StringScanner; import git4idea.util.UntrackedFilesNotifier; import org.jetbrains.annotations.NotNull; @@ -43,6 +44,8 @@ import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.CHECKOUT; + /** * @author Kirill Likhodedov */ @@ -77,7 +80,9 @@ public class GitRebaser { final GitRebaseProblemDetector rebaseConflictDetector = new GitRebaseProblemDetector(); rebaseHandler.addLineListener(rebaseConflictDetector); GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root); + GitLocalChangesWouldBeOverwrittenDetector localChangesDetector = new GitLocalChangesWouldBeOverwrittenDetector(root, CHECKOUT); rebaseHandler.addLineListener(untrackedFilesDetector); + rebaseHandler.addLineListener(localChangesDetector); String progressTitle = "Rebasing"; GitTask rebaseTask = new GitTask(myProject, rebaseHandler, progressTitle); @@ -108,7 +113,7 @@ public class GitRebaser { }); if (failure.get()) { - updateResult.set(handleRebaseFailure(rebaseHandler, root, rebaseConflictDetector, untrackedFilesDetector)); + updateResult.set(handleRebaseFailure(rebaseHandler, root, rebaseConflictDetector, untrackedFilesDetector, localChangesDetector)); } } finally { @@ -333,19 +338,27 @@ public class GitRebaser { } @NotNull - public GitUpdateResult handleRebaseFailure(@NotNull GitLineHandler handler, @NotNull VirtualFile root, + public GitUpdateResult handleRebaseFailure(@NotNull GitLineHandler handler, + @NotNull VirtualFile root, @NotNull GitRebaseProblemDetector rebaseConflictDetector, - @NotNull GitMessageWithFilesDetector untrackedWouldBeOverwrittenDetector) { + @NotNull GitMessageWithFilesDetector untrackedWouldBeOverwrittenDetector, + @NotNull GitLocalChangesWouldBeOverwrittenDetector localChangesDetector) { if (rebaseConflictDetector.isMergeConflict()) { LOG.info("handleRebaseFailure merge conflict"); final boolean allMerged = new GitRebaser.ConflictResolver(myProject, myGit, root, this).merge(); return allMerged ? GitUpdateResult.SUCCESS_WITH_RESOLVED_CONFLICTS : GitUpdateResult.INCOMPLETE; - } else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) { + } + else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) { LOG.info("handleRebaseFailure: untracked files would be overwritten by checkout"); UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, root, untrackedWouldBeOverwrittenDetector.getRelativeFilePaths(), "rebase", null); return GitUpdateResult.ERROR; - } else { + } + else if (localChangesDetector.wasMessageDetected()) { + LocalChangesWouldBeOverwrittenHelper.showErrorNotification(myProject, root, "rebase", localChangesDetector.getRelativeFilePaths()); + return GitUpdateResult.ERROR; + } + else { LOG.info("handleRebaseFailure error " + handler.errors()); GitUIUtil.notifyImportantError(myProject, "Rebase error", GitUIUtil.stringifyErrors(handler.errors())); return GitUpdateResult.ERROR; diff --git a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java index eccaa446b0ed..44e4f1fab4b9 100644 --- a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java +++ b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java @@ -51,6 +51,7 @@ import git4idea.merge.GitConflictResolver; import git4idea.repo.GitRepository; import git4idea.stash.GitStashUtils; import git4idea.util.GitUIUtil; +import git4idea.util.LocalChangesWouldBeOverwrittenHelper; import git4idea.util.UntrackedFilesNotifier; import git4idea.validators.GitBranchNameValidator; import org.jetbrains.annotations.NotNull; @@ -68,6 +69,8 @@ import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.MERGE; + /** * The unstash dialog */ @@ -349,7 +352,9 @@ public class GitUnstashDialog extends DialogWrapper { } }); GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root); + GitLocalChangesWouldBeOverwrittenDetector localChangesDetector = new GitLocalChangesWouldBeOverwrittenDetector(root, MERGE); h.addLineListener(untrackedFilesDetector); + h.addLineListener(localChangesDetector); GitUtil.workingTreeChangeStarted(myProject); try { @@ -371,6 +376,8 @@ public class GitUnstashDialog extends DialogWrapper { } else if (untrackedFilesDetector.wasMessageDetected()) { UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, root, untrackedFilesDetector.getRelativeFilePaths(), "unstash", null); + } else if (localChangesDetector.wasMessageDetected()) { + LocalChangesWouldBeOverwrittenHelper.showErrorDialog(myProject, root, "unstash", localChangesDetector.getRelativeFilePaths()); } else if (!res.success()) { GitUIUtil.showOperationErrors(myProject, h.errors(), h.printableCommandLine()); } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java index bd9c8fb3a1b3..1cb7cf6a66c9 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java @@ -45,14 +45,8 @@ import javax.swing.event.HyperlinkEvent; import java.util.List; /** - *

* The popup which allows to quickly switch and control Git branches. - *

- *

- * Use {@link #asListPopup()} to achieve the {@link ListPopup} itself. - *

- * - * @author Kirill Likhodedov + *

*/ class GitBranchPopup { @@ -160,32 +154,28 @@ class GitBranchPopup { } private void notifyAboutSyncedBranches() { - VcsNotifier.getInstance(myProject).notifyImportantInfo("Synchronous branch control enabled", - "You have several Git roots in the project and they all are checked out at the same branch. " + - "We've enabled synchronous branch control for the project.
" + - "If you wish to control branches in different roots separately, you may disable the setting.", - new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, - @NotNull HyperlinkEvent event) { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - ShowSettingsUtil.getInstance().showSettingsDialog(myProject, myVcs - .getConfigurable().getDisplayName()); - if (myVcsSettings.getSyncSetting() == GitBranchSyncSetting.DONT) { - notification.expire(); - } - } - } - } - ); + String description = "You have several Git roots in the project and they all are checked out at the same branch. " + + "We've enabled synchronous branch control for the project.
" + + "If you wish to control branches in different roots separately, " + + "you may disable the setting."; + NotificationListener listener = new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + ShowSettingsUtil.getInstance().showSettingsDialog(myProject, myVcs.getConfigurable().getDisplayName()); + if (myVcsSettings.getSyncSetting() == GitBranchSyncSetting.DONT) { + notification.expire(); + } + } + } + }; + VcsNotifier.getInstance(myProject).notifyImportantInfo("Synchronous branch control enabled", description, listener); } private ActionGroup createActions() { DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); - GitRepositoryManager repositoryManager = myRepositoryManager; if (repositoryManager.moreThanOneRoot()) { - if (userWantsSyncControl()) { fillWithCommonRepositoryActions(popupGroup, repositoryManager); } @@ -196,7 +186,6 @@ class GitBranchPopup { else { fillPopupWithCurrentRepositoryActions(popupGroup, null); } - popupGroup.addSeparator(); return popupGroup; } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java index dbd3f8c902e5..b3cba24540de 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java @@ -36,10 +36,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -/** - * - * @author Kirill Likhodedov - */ class GitBranchPopupActions { private final Project myProject; @@ -123,7 +119,7 @@ class GitBranchPopupActions { public void update(AnActionEvent e) { if (myRepository.isFresh()) { e.getPresentation().setEnabled(false); - e.getPresentation().setDescription("Checkout is not possible before the first commit."); + e.getPresentation().setDescription("Checkout is not possible before the first commit"); } } } diff --git a/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java b/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java index 04ed3ac845ca..e22c15076170 100644 --- a/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java +++ b/plugins/git4idea/src/git4idea/update/GitRebaseUpdater.java @@ -18,12 +18,10 @@ package git4idea.update; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ui.UIUtil; import git4idea.GitBranch; import git4idea.GitUtil; import git4idea.branch.GitBranchPair; @@ -78,42 +76,12 @@ public class GitRebaseUpdater extends GitUpdater { return dest.getName(); } - // TODO - //if (!checkLocallyModified(myRoot)) { - // cancel(); - // updateSucceeded.set(false); - //} - - - // TODO: show at any case of update successfullibility, also don't show here but for all roots - //if (mySkippedCommits.size() > 0) { - // GitSkippedCommits.showSkipped(myProject, mySkippedCommits); - //} - public void cancel() { myRebaser.abortRebase(myRoot); myProgressIndicator.setText2("Refreshing files for the root " + myRoot.getPath()); myRoot.refresh(false, true); } - /** - * Check and process locally modified files - * - * @param root the project root - * @param ex the exception holder - */ - protected boolean checkLocallyModified(final VirtualFile root) throws VcsException { - final Ref cancelled = new Ref(false); - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - public void run() { - if (!GitUpdateLocallyModifiedDialog.showIfNeeded(myProject, root)) { - cancelled.set(true); - } - } - }); - return !cancelled.get(); - } - @Override public String toString() { return "Rebase updater"; diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateLocallyModifiedDialog.form b/plugins/git4idea/src/git4idea/update/GitUpdateLocallyModifiedDialog.form deleted file mode 100644 index a97507f24ec9..000000000000 --- a/plugins/git4idea/src/git4idea/update/GitUpdateLocallyModifiedDialog.form +++ /dev/null @@ -1,71 +0,0 @@ - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateLocallyModifiedDialog.java b/plugins/git4idea/src/git4idea/update/GitUpdateLocallyModifiedDialog.java deleted file mode 100644 index c3384cea20b0..000000000000 --- a/plugins/git4idea/src/git4idea/update/GitUpdateLocallyModifiedDialog.java +++ /dev/null @@ -1,211 +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 git4idea.update; - -import com.intellij.openapi.application.ApplicationNamesInfo; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ui.UIUtil; -import com.intellij.vcsUtil.VcsUtil; -import git4idea.GitUtil; -import git4idea.commands.GitCommand; -import git4idea.commands.GitSimpleHandler; -import git4idea.util.StringScanner; -import git4idea.i18n.GitBundle; -import git4idea.rollback.GitRollbackEnvironment; -import git4idea.util.GitUIUtil; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * The dialog that displays locally modified files during update process - */ -public class GitUpdateLocallyModifiedDialog extends DialogWrapper { - /** - * The rescan button - */ - private JButton myRescanButton; - /** - * The list of files to revert - */ - private JList myFilesList; - - private JLabel myDescriptionLabel; - /** - * The git root label - */ - private JLabel myGitRoot; - /** - * The root panel - */ - private JPanel myRootPanel; - /** - * The collection with locally modified files - */ - private final List myLocallyModifiedFiles; - - /** - * The constructor - * - * @param project the current project - * @param root the vcs root - * @param locallyModifiedFiles the collection of locally modified files to use - */ - protected GitUpdateLocallyModifiedDialog(final Project project, final VirtualFile root, List locallyModifiedFiles) { - super(project, true); - myLocallyModifiedFiles = locallyModifiedFiles; - setTitle(GitBundle.getString("update.locally.modified.title")); - myGitRoot.setText(root.getPresentableUrl()); - myFilesList.setModel(new DefaultListModel()); - setOKButtonText(GitBundle.getString("update.locally.modified.revert")); - syncListModel(); - myRescanButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - myLocallyModifiedFiles.clear(); - try { - scanFiles(project, root, myLocallyModifiedFiles); - } - catch (VcsException ex) { - GitUIUtil.showOperationError(project, ex, "Checking for locally modified files"); - } - } - }); - myDescriptionLabel - .setText(GitBundle.message("update.locally.modified.message", ApplicationNamesInfo.getInstance().getFullProductName())); - init(); - } - - /** - * Refresh list model according to the current content of the collection - */ - private void syncListModel() { - DefaultListModel listModel = (DefaultListModel)myFilesList.getModel(); - listModel.removeAllElements(); - for (String p : myLocallyModifiedFiles) { - listModel.addElement(p); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected JComponent createCenterPanel() { - return myRootPanel; - } - - /** - * {@inheritDoc} - */ - @Override - protected String getDimensionServiceKey() { - return getClass().getName(); - } - - /** - * Scan working tree and detect locally modified files - * - * @param project the project to scan - * @param root the root to scan - * @param files the collection with files - * @throws VcsException if there problem with running git or working tree is dirty in unsupported way - */ - private static void scanFiles(Project project, VirtualFile root, List files) throws VcsException { - String rootPath = root.getPath(); - GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.DIFF); - h.addParameters("--name-status"); - h.setSilent(true); - h.setStdoutSuppressed(true); - StringScanner s = new StringScanner(h.run()); - while (s.hasMoreData()) { - if (s.isEol()) { - s.line(); - continue; - } - if (s.tryConsume("M\t")) { - String path = rootPath + "/" + GitUtil.unescapePath(s.line()); - files.add(path); - } - else { - throw new VcsException("Working tree is dirty in unsupported way: " + s.line()); - } - } - } - - - /** - * Show the dialog if needed - * - * @param project the project - * @param root the vcs root - * @return true if showing is not needed or operation completed successfully - */ - public static boolean showIfNeeded(final Project project, final VirtualFile root) { - final ArrayList files = new ArrayList(); - try { - scanFiles(project, root, files); - final AtomicBoolean rc = new AtomicBoolean(true); - if (!files.isEmpty()) { - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - public void run() { - GitUpdateLocallyModifiedDialog d = new GitUpdateLocallyModifiedDialog(project, root, files); - d.show(); - rc.set(d.isOK()); - } - }); - if (rc.get()) { - if (!files.isEmpty()) { - revertFiles(project, root, files); - } - } - } - return rc.get(); - } - catch (final VcsException e) { - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - public void run() { - GitUIUtil.showOperationError(project, e, "Checking for locally modified files"); - } - }); - return false; - } - } - - /** - * Revert files from the list - * - * @param project the project - * @param root the vcs root - * @param files the files to revert - */ - private static void revertFiles(Project project, VirtualFile root, ArrayList files) throws VcsException { - // TODO consider deleted files - GitRollbackEnvironment rollback = GitRollbackEnvironment.getInstance(project); - ArrayList list = new ArrayList(files.size()); - for (String p : files) { - list.add(VcsUtil.getFilePath(p)); - } - rollback.revert(root, list); - } -} diff --git a/plugins/git4idea/src/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java b/plugins/git4idea/src/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java index 021ce824b9a7..7659c50e362a 100644 --- a/plugins/git4idea/src/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java +++ b/plugins/git4idea/src/git4idea/util/LocalChangesWouldBeOverwrittenHelper.java @@ -35,12 +35,12 @@ import java.util.List; public class LocalChangesWouldBeOverwrittenHelper { @NotNull - public static String getErrorNotificationDescription() { + private static String getErrorNotificationDescription() { return getErrorDescription(true); } @NotNull - public static String getErrorDialogDescription() { + private static String getErrorDialogDescription() { return getErrorDescription(false); } @@ -56,7 +56,7 @@ public class LocalChangesWouldBeOverwrittenHelper { } } - public static void showErrorNotification(@NotNull final Project project, @NotNull VirtualFile root, @NotNull final String operationName, + public static void showErrorNotification(@NotNull final Project project, @NotNull final VirtualFile root, @NotNull final String operationName, @NotNull final Collection relativeFilePaths) { final Collection absolutePaths = GitUtil.toAbsolute(root, relativeFilePaths); final List changes = GitUtil.findLocalChangesForPaths(project, root, absolutePaths, false); @@ -66,20 +66,33 @@ public class LocalChangesWouldBeOverwrittenHelper { @Override protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) { - String title = "Local Changes Prevent from " + StringUtil.capitalize(operationName); - String description = getErrorDialogDescription(); - if (changes.isEmpty()) { - GitUtil.showPathsInDialog(project, absolutePaths, title, description); - } - else { - DialogBuilder builder = new DialogBuilder(project); - builder.setNorthPanel(new MultiLineLabel(description)); - builder.setCenterPanel(new ChangesBrowserWithRollback(project, changes)); - builder.addOkAction(); - builder.setTitle(title); - builder.show(); - } + showErrorDialog(project, operationName, changes, absolutePaths); } }); } + + public static void showErrorDialog(@NotNull Project project, @NotNull VirtualFile root, @NotNull String operationName, + @NotNull Collection relativeFilePaths) { + Collection absolutePaths = GitUtil.toAbsolute(root, relativeFilePaths); + List changes = GitUtil.findLocalChangesForPaths(project, root, absolutePaths, false); + showErrorDialog(project, operationName, changes, absolutePaths); + } + + private static void showErrorDialog(@NotNull Project project, @NotNull String operationName, @NotNull List changes, + @NotNull Collection absolutePaths) { + String title = "Local Changes Prevent from " + StringUtil.capitalize(operationName); + String description = getErrorDialogDescription(); + if (changes.isEmpty()) { + GitUtil.showPathsInDialog(project, absolutePaths, title, description); + } + else { + DialogBuilder builder = new DialogBuilder(project); + builder.setNorthPanel(new MultiLineLabel(description)); + builder.setCenterPanel(new ChangesBrowserWithRollback(project, changes)); + builder.addOkAction(); + builder.setTitle(title); + builder.show(); + } + } + } diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java index bfceb0ceec39..719f8b956432 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java @@ -48,6 +48,7 @@ import org.jetbrains.plugins.github.util.*; import java.io.IOException; import java.util.Collections; +import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.CHECKOUT; import static org.jetbrains.plugins.github.util.GithubUtil.setVisibleEnabled; /** @@ -256,7 +257,9 @@ public class GithubRebaseAction extends DumbAwareAction { final GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root); + final GitLocalChangesWouldBeOverwrittenDetector localChangesDetector = new GitLocalChangesWouldBeOverwrittenDetector(root, CHECKOUT); handler.addLineListener(untrackedFilesDetector); + handler.addLineListener(localChangesDetector); GitTask pullTask = new GitTask(project, handler, "Rebasing from upstream/master"); pullTask.setProgressIndicator(indicator); @@ -271,7 +274,8 @@ public class GithubRebaseAction extends DumbAwareAction { @Override protected void onFailure() { - GitUpdateResult result = rebaser.handleRebaseFailure(handler, root, rebaseConflictDetector, untrackedFilesDetector); + GitUpdateResult result = rebaser.handleRebaseFailure(handler, root, rebaseConflictDetector, + untrackedFilesDetector, localChangesDetector); repositoryManager.updateRepository(root); if (result == GitUpdateResult.NOTHING_TO_UPDATE || result == GitUpdateResult.SUCCESS || diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/WrappingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/WrappingTest.groovy index 79d79edc42de..4d585dbf7f6f 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/WrappingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/WrappingTest.groovy @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.lang.formatter import com.intellij.psi.codeStyle.CommonCodeStyleSettings +import org.jetbrains.plugins.groovy.GroovyLanguage /** * @author Max Medvedev @@ -24,7 +25,7 @@ class WrappingTest extends GroovyFormatterTestCase { @Override protected void setUp() throws Exception { super.setUp() - myTempSettings.RIGHT_MARGIN = 10 + myTempSettings.setRightMargin(GroovyLanguage.INSTANCE, 10); } void testWrapChainedMethodCalls() { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java deleted file mode 100644 index 18294df94a66..000000000000 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgPusher.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2000-2011 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 org.zmlx.hg4idea; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.VcsNotifier; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.zmlx.hg4idea.action.HgCommandResultNotifier; -import org.zmlx.hg4idea.command.HgPushCommand; -import org.zmlx.hg4idea.execution.HgCommandResult; -import org.zmlx.hg4idea.execution.HgCommandResultHandler; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class HgPusher { - - private static final Logger LOG = Logger.getInstance(HgPusher.class); - private static final String ONE = "one"; - private static Pattern PUSH_COMMITS_PATTERN = Pattern.compile(".*(?:added|pushed) (\\d+|" + ONE + ") changeset.*"); - // hg push command has definite exit values for some cases: - // mercurial returns 0 if push was successful, 1 if nothing to push. see hg push --help - private static int PUSH_SUCCEEDED_EXIT_VALUE = 0; - private static int NOTHING_TO_PUSH_EXIT_VALUE = 1; - - public static void push(final Project project, HgPushCommand command) { - final VirtualFile repo = command.getRepo(); - command.execute(new HgCommandResultHandler() { - @Override - public void process(@Nullable HgCommandResult result) { - if (result == null) { - return; - } - - if (result.getExitValue() == PUSH_SUCCEEDED_EXIT_VALUE) { - int commitsNum = getNumberOfPushedCommits(result); - String successTitle = "Pushed successfully"; - String successDescription = String.format("Pushed %d %s [%s]", commitsNum, StringUtil.pluralize("commit", commitsNum), - repo.getPresentableName()); - VcsNotifier.getInstance(project).notifySuccess(successTitle, successDescription); - } - else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) { - VcsNotifier.getInstance(project).notifySuccess("Nothing to push"); - } - else { - new HgCommandResultNotifier(project).notifyError(result, "Push failed", - "Failed to push to [" + repo.getPresentableName() + "]"); - } - } - }); - } - - private static int getNumberOfPushedCommits(@NotNull HgCommandResult result) { - int numberOfCommitsInAllSubrepos = 0; - final List outputLines = result.getOutputLines(); - for (String outputLine : outputLines) { - outputLine = outputLine.trim(); - final Matcher matcher = PUSH_COMMITS_PATTERN.matcher(outputLine); - if (matcher.matches()) { - try { - String numberOfCommits = matcher.group(1); - numberOfCommitsInAllSubrepos += ONE.equals(numberOfCommits) ? 1 : Integer.parseInt(numberOfCommits); - } - catch (NumberFormatException e) { - LOG.error("getNumberOfPushedCommits ", e); - return -1; - } - } - } - return numberOfCommitsInAllSubrepos; - } -} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgOutgoingCommitsProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgOutgoingCommitsProvider.java index 00977ee31cb0..70055cafe1a1 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgOutgoingCommitsProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgOutgoingCommitsProvider.java @@ -71,7 +71,7 @@ public class HgOutgoingCommitsProvider extends OutgoingCommitsProvider { if (HgErrorUtil.isAbortLine(error)) { if (HgErrorUtil.isAuthorizationError(error)) { VcsError authorizationError = - new VcsError(error + "" + LOGIN_AND_REFRESH_LINK + "", new VcsErrorHandler() { + new VcsError(error + "" + LOGIN_AND_REFRESH_LINK + "", new VcsErrorHandler() { public void handleError(@NotNull CommitLoader commitLoader) { commitLoader.reloadCommits(); } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgSource.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSource.java similarity index 68% rename from plugins/hg4idea/src/org/zmlx/hg4idea/push/HgSource.java rename to plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSource.java index 292e2df7244c..babd49c9b31d 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgSource.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSource.java @@ -16,16 +16,23 @@ package org.zmlx.hg4idea.push; import com.intellij.dvcs.push.PushSource; +import org.jetbrains.annotations.NotNull; -public class HgSource implements PushSource { - String mySource; +public class HgPushSource implements PushSource { + @NotNull private String myBranch; - public HgSource(String branch) { - mySource = branch; + public HgPushSource(@NotNull String branch) { + myBranch = branch; } + @NotNull @Override public String getPresentation() { - return mySource; + return myBranch; + } + + @NotNull + public String getBranch() { + return myBranch; // presentation may differ from branch } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java index d2a320297c2f..4bfa134a5e26 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java @@ -80,9 +80,9 @@ public class HgPushSupport extends PushSupport { @NotNull @Override - public HgSource getSource(@NotNull HgRepository repository) { + public HgPushSource getSource(@NotNull HgRepository repository) { String localBranch = HgUtil.getActiveBranchName(repository); - return new HgSource(localBranch); + return new HgPushSource(localBranch); } @Override diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPusher.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPusher.java index dcbfa314d03e..f019035a373f 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPusher.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPusher.java @@ -19,16 +19,34 @@ import com.intellij.dvcs.push.PushSpec; import com.intellij.dvcs.push.Pusher; import com.intellij.dvcs.push.VcsPushOptionValue; import com.intellij.dvcs.repo.Repository; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsNotifier; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.action.HgCommandResultNotifier; import org.zmlx.hg4idea.command.HgPushCommand; +import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.execution.HgCommandResultHandler; import org.zmlx.hg4idea.repo.HgRepository; +import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class HgPusher extends Pusher { + private static final Logger LOG = Logger.getInstance(HgPusher.class); + private static final String ONE = "one"; + private static Pattern PUSH_COMMITS_PATTERN = Pattern.compile(".*(?:added|pushed) (\\d+|" + ONE + ") changeset.*"); + // hg push command has definite exit values for some cases: + // mercurial returns 0 if push was successful, 1 if nothing to push. see hg push --help + private static int PUSH_SUCCEEDED_EXIT_VALUE = 0; + private static int NOTHING_TO_PUSH_EXIT_VALUE = 1; + @Override public void push(@NotNull Map pushSpecs, @Nullable VcsPushOptionValue vcsPushOptionValue, boolean force) { for (Map.Entry entry : pushSpecs.entrySet()) { @@ -39,23 +57,71 @@ public class HgPusher extends Pusher { if (destination == null) { continue; } - HgSource source = (HgSource)hgSpec.getSource(); + HgPushSource source = (HgPushSource)hgSpec.getSource(); Project project = repository.getProject(); final HgPushCommand pushCommand = new HgPushCommand(project, repository.getRoot(), destination.myTarget); pushCommand.setIsNewBranch(true); // set always true, because it just allow mercurial to create a new one if needed pushCommand.setForce(force); - if (source.mySource.equals(hgRepository.getCurrentBookmark())) { + String branchName = source.getBranch(); + if (branchName.equals(hgRepository.getCurrentBookmark())) { if (vcsPushOptionValue == HgVcsPushOptionValue.Current) { - pushCommand.setBookmarkName(source.mySource); + pushCommand.setBookmarkName(branchName); } else { - pushCommand.setRevision(source.mySource); + pushCommand.setRevision(branchName); } } else { - pushCommand.setBranchName(source.mySource); + pushCommand.setBranchName(branchName); } - org.zmlx.hg4idea.HgPusher.push(project, pushCommand); + push(project, pushCommand); } } + + public static void push(@NotNull final Project project, @NotNull HgPushCommand command) { + final VirtualFile repo = command.getRepo(); + command.execute(new HgCommandResultHandler() { + @Override + public void process(@Nullable HgCommandResult result) { + if (result == null) { + return; + } + + if (result.getExitValue() == PUSH_SUCCEEDED_EXIT_VALUE) { + int commitsNum = getNumberOfPushedCommits(result); + String successTitle = "Pushed successfully"; + String successDescription = String.format("Pushed %d %s [%s]", commitsNum, StringUtil.pluralize("commit", commitsNum), + repo.getPresentableName()); + VcsNotifier.getInstance(project).notifySuccess(successTitle, successDescription); + } + else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) { + VcsNotifier.getInstance(project).notifySuccess("Nothing to push"); + } + else { + new HgCommandResultNotifier(project).notifyError(result, "Push failed", + "Failed to push to [" + repo.getPresentableName() + "]"); + } + } + }); + } + + private static int getNumberOfPushedCommits(@NotNull HgCommandResult result) { + int numberOfCommitsInAllSubrepos = 0; + final List outputLines = result.getOutputLines(); + for (String outputLine : outputLines) { + outputLine = outputLine.trim(); + final Matcher matcher = PUSH_COMMITS_PATTERN.matcher(outputLine); + if (matcher.matches()) { + try { + String numberOfCommits = matcher.group(1); + numberOfCommitsInAllSubrepos += ONE.equals(numberOfCommits) ? 1 : Integer.parseInt(numberOfCommits); + } + catch (NumberFormatException e) { + LOG.error("getNumberOfPushedCommits ", e); + return -1; + } + } + } + return numberOfCommitsInAllSubrepos; + } } diff --git a/python/edu/build/desktop.ini b/python/edu/build/desktop.ini new file mode 100644 index 000000000000..f56d43c998bf --- /dev/null +++ b/python/edu/build/desktop.ini @@ -0,0 +1,100 @@ +[Settings] +NumFields=6 + +[Field 1] +Type=checkbox +Left=5 +Right=100 +Top=10 +Bottom=20 +State=0 + +[Field 2] +Type=checkbox +Left=120 +Right=-1 +Top=10 +Bottom=20 +State=0 + +[Field 3] +Type=GroupBox +Left=1 +Right=-1 +Top=35 +Bottom=65 +Text=Choice Python version + +[Field 4] +Type=RadioButton +Left=5 +Right=45 +Top=50 +Bottom=60 +State=1 +Text=Python 2 + +[Field 5] +Type=RadioButton +Left=95 +Right=135 +Top=50 +Bottom=60 +State=0 +Text=Python 3 + +[Field 6] +Type=GroupBox +Left=1 +Right=-1 +Top=75 +Bottom=105 +Text=Create Associations + +[Field 7] +Type=checkbox +Left=5 +Right=45 +Top=90 +Bottom=100 +State=0 + +[Field 8] +Type=checkbox +Left=50 +Right=90 +Top=90 +Bottom=100 +State=0 + +[Field 9] +Type=checkbox +Left=95 +Right=135 +Top=90 +Bottom=100 +State=0 + +[Field 10] +Type=checkbox +Left=140 +Right=180 +Top=90 +Bottom=100 +State=0 + +[Field 11] +Type=checkbox +Left=185 +Right=225 +Top=90 +Bottom=100 +State=0 + +[Field 12] +Type=checkbox +Left=230 +Right=270 +Top=90 +Bottom=100 +State=0 diff --git a/python/edu/build/idea.nsi b/python/edu/build/idea.nsi new file mode 100644 index 000000000000..d9903c94de5e --- /dev/null +++ b/python/edu/build/idea.nsi @@ -0,0 +1,1228 @@ +!verbose 2 + +!include "paths.nsi" +!include "strings.nsi" +!include "Registry.nsi" +!include "version.nsi" + +; Product with version (IntelliJ IDEA #xxxx). + +; Used in registry to put each build info into the separate subkey +; Add&Remove programs doesn't understand subkeys in the Uninstall key, +; thus ${PRODUCT_WITH_VER} is used for uninstall registry information +!define PRODUCT_REG_VER "${MUI_PRODUCT}\${VER_BUILD}" + +!define INSTALL_OPTION_ELEMENTS 7 +Name "${MUI_PRODUCT}" +SetCompressor lzma +; http://nsis.sourceforge.net/Shortcuts_removal_fails_on_Windows_Vista +RequestExecutionLevel user + +;------------------------------------------------------------------------------ +; include "Modern User Interface" +;------------------------------------------------------------------------------ +!include "MUI2.nsh" +!include "FileFunc.nsh" +!include UAC.nsh +!include "InstallOptions.nsh" +!include StrFunc.nsh +!include LogicLib.nsh + +${UnStrStr} +${UnStrLoc} +${UnStrRep} +${StrRep} + +ReserveFile "desktop.ini" +ReserveFile "DeleteSettings.ini" +ReserveFile '${NSISDIR}\Plugins\InstallOptions.dll' +!insertmacro MUI_RESERVEFILE_LANGDLL + +!define MUI_ICON "${IMAGES_LOCATION}\${PRODUCT_ICON_FILE}" +!define MUI_UNICON "${IMAGES_LOCATION}\${PRODUCT_UNINST_ICON_FILE}" + +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP "${IMAGES_LOCATION}\${PRODUCT_HEADER_FILE}" +!define MUI_WELCOMEFINISHPAGE_BITMAP "${IMAGES_LOCATION}\${PRODUCT_LOGO_FILE}" + +;------------------------------------------------------------------------------ +; on GUI initialization installer checks whether IDEA is already installed +;------------------------------------------------------------------------------ + +!define MUI_CUSTOMFUNCTION_GUIINIT GUIInit + +Var baseRegKey +Var IS_UPGRADE_60 + +!define MUI_LANGDLL_REGISTRY_ROOT "HKCU" +!define MUI_LANGDLL_REGISTRY_KEY "Software\JetBrains\${MUI_PRODUCT}\${VER_BUILD}\" +!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" + +;check if the window is win7 or newer +!macro INST_UNINST_SWITCH un + Function ${un}winVersion + ;The platform is returned into $0, minor version into $1. + ;Windows 7 is equals values of 6 as platform and 1 as minor version. + ;Windows 8 is equals values of 6 as platform and 2 as minor version. + nsisos::osversion + ${If} $0 == "6" + ${AndIf} $1 >= "1" + StrCpy $0 "1" + ${else} + StrCpy $0 "0" + ${EndIf} + FunctionEnd + + Function ${un}compareFileInstallationTime + StrCpy $9 "" + get_first_file: + Pop $7 + IfFileExists "$7" get_next_file 0 + StrCmp $7 "Complete" complete get_first_file + get_next_file: + Pop $8 + StrCmp $8 "Complete" 0 +2 + ; check if there is only one property file + StrCmp $9 "no changes" complete different + IfFileExists "$8" 0 get_next_file + ClearErrors + ${GetTime} "$7" "M" $0 $1 $2 $3 $4 $5 $6 + ${GetTime} "$8" "M" $R0 $R1 $R2 $R3 $R4 $R5 $R6 + StrCmp $0 $R0 0 different + StrCmp $1 $R1 0 different + StrCmp $2 $R2 0 different + StrCmp $4 $R4 0 different + StrCmp $5 $R5 0 different + StrCmp $6 $R6 0 different + StrCpy $9 "no changes" + Goto get_next_file + different: + StrCpy $9 "Modified" + complete: +FunctionEnd + +Function ${un}SplitStr +Exch $0 ; str +Push $1 ; inQ +Push $3 ; idx +Push $4 ; tmp +StrCpy $1 0 +StrCpy $3 0 +loop: + StrCpy $4 $0 1 $3 + ${If} $4 == '"' + ${If} $1 <> 0 + StrCpy $0 $0 "" 1 + IntOp $3 $3 - 1 + ${EndIf} + IntOp $1 $1 ! + ${EndIf} + ${If} $4 == '' ; The end? + StrCpy $1 0 + StrCpy $4 ',' + ${EndIf} + ${If} $4 == ',' + ${AndIf} $1 = 0 + StrCpy $4 $0 $3 + StrCpy $1 $4 "" -1 + ${IfThen} $1 == '"' ${|} StrCpy $4 $4 -1 ${|} + killspace: + IntOp $3 $3 + 1 + StrCpy $0 $0 "" $3 + StrCpy $1 $0 1 + StrCpy $3 0 + StrCmp $1 ',' killspace + Push $0 ; Remaining + Exch 4 + Pop $0 + StrCmp $4 "" 0 moreleft + Pop $4 + Pop $3 + Pop $1 + Return + moreleft: + Exch $4 + Exch 2 + Pop $1 + Pop $3 + Return + ${EndIf} + IntOp $3 $3 + 1 + Goto loop +FunctionEnd + +!macroend +!insertmacro INST_UNINST_SWITCH "" +!insertmacro INST_UNINST_SWITCH "un." + +Function InstDirState + !define InstDirState `!insertmacro InstDirStateCall` + + !macro InstDirStateCall _PATH _RESULT + Push `${_PATH}` + Call InstDirState + Pop ${_RESULT} + !macroend + + Exch $0 + Push $1 + ClearErrors + + FindFirst $1 $0 '$0\*.*' + IfErrors 0 +3 + StrCpy $0 -1 + goto end + StrCmp $0 '.' 0 +4 + FindNext $1 $0 + StrCmp $0 '..' 0 +2 + FindNext $1 $0 + FindClose $1 + IfErrors 0 +3 + StrCpy $0 0 + goto end + StrCpy $0 1 + + end: + Pop $1 + Exch $0 +FunctionEnd + +Function SplitFirstStrPart + Exch $R0 + Exch + Exch $R1 + Push $R2 + Push $R3 + StrCpy $R3 $R1 + StrLen $R1 $R0 + IntOp $R1 $R1 + 1 + loop: + IntOp $R1 $R1 - 1 + StrCpy $R2 $R0 1 -$R1 + StrCmp $R1 0 exit0 + StrCmp $R2 $R3 exit1 loop + exit0: + StrCpy $R1 "" + Goto exit2 + exit1: + IntOp $R1 $R1 - 1 + StrCmp $R1 0 0 +3 + StrCpy $R2 "" + Goto +2 + StrCpy $R2 $R0 "" -$R1 + IntOp $R1 $R1 + 1 + StrCpy $R0 $R0 -$R1 + StrCpy $R1 $R2 + exit2: + Pop $R3 + Pop $R2 + Exch $R1 ;rest + Exch + Exch $R0 ;first +FunctionEnd + +Function VersionSplit + !define VersionSplit `!insertmacro VersionSplitCall` + + !macro VersionSplitCall _FULL _PRODUCT _BRANCH _BUILD + Push `${_FULL}` + Call VersionSplit + Pop ${_PRODUCT} + Pop ${_BRANCH} + Pop ${_BUILD} + !macroend + + Pop $R0 + Push "-" + Push $R0 + Call SplitFirstStrPart + Pop $R0 + Pop $R1 + Push "." + Push $R1 + Call SplitFirstStrPart + Push $R0 +FunctionEnd + +Function OnDirectoryPageLeave + StrCpy $IS_UPGRADE_60 "0" + ${InstDirState} "$INSTDIR" $R0 + IntCmp $R0 1 check_build skip_abort skip_abort +check_build: + FileOpen $R1 "$INSTDIR\build.txt" "r" + IfErrors do_abort + FileRead $R1 $R2 + FileClose $R1 + IfErrors do_abort + ${VersionSplit} ${MIN_UPGRADE_BUILD} $R3 $R4 $R5 + ${VersionSplit} ${MAX_UPGRADE_BUILD} $R6 $R7 $R8 + ${VersionSplit} $R2 $R9 $R2 $R0 + StrCmp $R9 $R3 0 do_abort + IntCmp $R2 $R4 0 do_abort + IntCmp $R0 $R5 do_accept do_abort + + StrCmp $R9 $R6 0 do_abort + IntCmp $R2 $R7 0 0 do_abort + IntCmp $R0 $R8 do_abort do_accept do_abort + +do_accept: + StrCpy $IS_UPGRADE_60 "1" + FileClose $R1 + Goto skip_abort + +do_abort: + ;check + ; - if there are no files into $INSTDIR (recursively) just excepted property files + ; - if property files have the same installation time. + StrCpy $9 "$INSTDIR" + Call instDirEmpty + StrCmp $9 "not empty" abort 0 + Push "Complete" + Push "$INSTDIR\bin\${PRODUCT_EXE_FILE}.vmoptions" + Push "$INSTDIR\bin\idea.properties" + ${StrRep} $0 ${PRODUCT_EXE_FILE} ".exe" "64.exe.vmoptions" + Push "$INSTDIR\bin\$0" + Call compareFileInstallationTime + StrCmp $9 "Modified" abort skip_abort +abort: + MessageBox MB_OK|MB_ICONEXCLAMATION "$(empty_or_upgrade_folder)" + Abort +skip_abort: +FunctionEnd + + +;check if there are no files into $INSTDIR recursively just except property files. +Function instDirEmpty + Push $0 + Push $1 + Push $2 + ClearErrors + FindFirst $1 $2 "$9\*.*" +nextElemement: + ;is the element a folder? + StrCmp $2 "." getNextElement + StrCmp $2 ".." getNextElement + IfFileExists "$9\$2\*.*" 0 nextFile + Push $9 + StrCpy "$9" "$9\$2" + Call instDirEmpty + StrCmp $9 "not empty" done 0 + Pop $9 + Goto getNextElement +nextFile: + ;is it the file property? + ${If} $2 != "idea.properties" + ${AndIf} $2 != "${PRODUCT_EXE_FILE}.vmoptions" + ${StrRep} $0 ${PRODUCT_EXE_FILE} ".exe" "64.exe.vmoptions" + ${AndIf} $2 != $0 + StrCpy $9 "not empty" + Goto done + ${EndIf} +getNextElement: + FindNext $1 $2 + IfErrors 0 nextElemement +done: + FindClose $1 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +;------------------------------------------------------------------------------ +; Variables +;------------------------------------------------------------------------------ + Var STARTMENU_FOLDER + Var config_path + Var system_path + +;------------------------------------------------------------------------------ +; configuration +;------------------------------------------------------------------------------ + +!insertmacro MUI_PAGE_WELCOME + +Page custom uninstallOldVersionDialog + +Var control_fields +Var max_fields + +!ifdef LICENSE_FILE +!insertmacro MUI_PAGE_LICENSE "$(myLicenseData)" +!endif + +!define MUI_PAGE_CUSTOMFUNCTION_LEAVE OnDirectoryPageLeave +!insertmacro MUI_PAGE_DIRECTORY + +Page custom ConfirmDesktopShortcut + !define MUI_STARTMENUPAGE_NODISABLE + !define MUI_STARTMENUPAGE_DEFAULTFOLDER "JetBrains" + +!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER +!define MUI_ABORTWARNING +!insertmacro MUI_PAGE_INSTFILES +!define MUI_FINISHPAGE_RUN_NOTCHECKED +!define MUI_FINISHPAGE_RUN +!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun +!insertmacro MUI_PAGE_FINISH + +!define MUI_UNINSTALLER +;!insertmacro MUI_UNPAGE_CONFIRM +UninstPage custom un.ConfirmDeleteSettings +!insertmacro MUI_UNPAGE_INSTFILES + +OutFile "${OUT_DIR}\${OUT_FILE}.exe" + +InstallDir "$PROGRAMFILES\${MANUFACTURER}\${PRODUCT_WITH_VER}" +!define MUI_BRANDINGTEXT " " +BrandingText " " + +Function PageFinishRun +!insertmacro UAC_AsUser_ExecShell "" "$INSTDIR\bin\${PRODUCT_EXE_FILE}" "" "" "" +FunctionEnd + +;------------------------------------------------------------------------------ +; languages +;------------------------------------------------------------------------------ +!insertmacro MUI_LANGUAGE "English" +;!insertmacro MUI_LANGUAGE "Japanese" +!include "idea_en.nsi" +;!include "idea_jp.nsi" + +!ifdef LICENSE_FILE +LicenseLangString myLicenseData ${LANG_ENGLISH} "${LICENSE_FILE}.txt" +LicenseLangString myLicenseData ${LANG_JAPANESE} "${LICENSE_FILE}.txt" +!endif + +Function .onInit + StrCpy $baseRegKey "HKCU" + IfSilent UAC_Done +UAC_Elevate: + !insertmacro UAC_RunElevated + StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - continue install under user + StrCmp 0 $0 0 UAC_Err ; Error? + StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? + Quit +UAC_Err: + Abort +UAC_ElevationAborted: + StrCpy $INSTDIR "$APPDATA\${MANUFACTURER}\${PRODUCT_WITH_VER}" + goto UAC_Done +UAC_Success: + StrCmp 1 $3 UAC_Admin ;Admin? + StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? + goto UAC_Elevate +UAC_Admin: + StrCpy $INSTDIR "$PROGRAMFILES\${MANUFACTURER}\${PRODUCT_WITH_VER}" + SetShellVarContext all + StrCpy $baseRegKey "HKLM" +UAC_Done: +; !insertmacro MUI_LANGDLL_DISPLAY +FunctionEnd + +Function checkVersion + StrCpy $2 "" + StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" +; ${If} $0 == "HKLM" +; StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" +; Push $0 +; call winVersion +; ${If} $0 == "1" +; StrCpy $1 "Software\Wow6432Node\${MANUFACTURER}\${PRODUCT_REG_VER}" +; ${Else} +; StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" +; ${EndIf} +; Pop $0 +; ${EndIf} + Call OMReadRegStr + IfFileExists $3\bin\${PRODUCT_EXE_FILE} check_version + Goto Done +check_version: + StrCpy $2 "Build" + Call OMReadRegStr + StrCmp $3 "" Done + IntCmpU $3 ${VER_BUILD} ask_Install_Over Done ask_Install_Over +ask_Install_Over: + MessageBox MB_YESNO|MB_ICONQUESTION "$(current_version_already_installed)" IDYES continue IDNO exit_installer +exit_installer: + Abort +continue: + StrCpy $0 "complete" +Done: +FunctionEnd + + +Function searchCurrentVersion + ; search current version of IDEA + StrCpy $0 "HKCU" + Call checkVersion + StrCmp $0 "complete" Done + StrCpy $0 "HKLM" + Call checkVersion +Done: +FunctionEnd + + +Function uninstallOldVersion + ;check if the uninstalled application is running +remove_previous_installation: + ;prepare a copy of launcher + CopyFiles "$3\bin\${PRODUCT_EXE_FILE}" "$3\bin\${PRODUCT_EXE_FILE}_copy" + ClearErrors + ;copy launcher to itself + CopyFiles "$3\bin\${PRODUCT_EXE_FILE}_copy" "$3\bin\${PRODUCT_EXE_FILE}" + Delete "$3\bin\${PRODUCT_EXE_FILE}_copy" + IfErrors 0 +3 + MessageBox MB_OKCANCEL|MB_ICONQUESTION|MB_TOPMOST "$(application_running)" IDOK remove_previous_installation IDCANCEL complete + goto complete + ; uninstallation mode + !insertmacro INSTALLOPTIONS_READ $9 "UninstallOldVersions.ini" "Field 2" "State" + ${If} $9 == "1" + ExecWait '"$3\bin\Uninstall.exe" /S' + ${else} + ExecWait '"$3\bin\Uninstall.exe" _?=$3\bin' + ${EndIf} + IfFileExists $3\bin\${PRODUCT_EXE_FILE} 0 uninstall + goto complete +uninstall: + ;previous installation has been removed + ;customer decided to keep properties? + IfFileExists $3\bin\idea.properties saveProperties fullRemove +saveProperties: + Delete "$3\bin\Uninstall.exe" + Goto complete +fullRemove: + RmDir /r "$3" +complete: +FunctionEnd + + +Function checkProductVersion +;$8 - count of already added fields to the dialog +;$3 - an old version which will be checked if the one should be added too +StrCpy $7 $control_fields +StrCpy $6 "" +loop: + IntOp $7 $7 + 1 + ${If} $8 >= $7 + !insertmacro INSTALLOPTIONS_READ $6 "UninstallOldVersions.ini" "Field $7" "Text" + ${If} $6 == $3 + ;found the same value in list of installations + StrCpy $6 "duplicated" + Goto finish + ${EndIf} + Goto loop + ${EndIf} +finish: +FunctionEnd + + +Function uninstallOldVersionDialog + StrCpy $control_fields 2 + StrCpy $max_fields 13 + StrCpy $0 "HKLM" + StrCpy $4 0 + ReserveFile "UninstallOldVersions.ini" + !insertmacro INSTALLOPTIONS_EXTRACT "UninstallOldVersions.ini" + StrCpy $8 $control_fields + +get_installation_info: + StrCpy $1 "Software\${MANUFACTURER}\${MUI_PRODUCT}" + StrCpy $5 "\bin\${PRODUCT_EXE_FILE}" + StrCpy $2 "" + Call getInstallationPath + StrCmp $3 "complete" next_registry_root + ;check if the old installation could be uninstalled + IfFileExists $3\bin\Uninstall.exe uninstall_dialog get_next_key +uninstall_dialog: + Call checkProductVersion + ${If} $6 != "duplicated" + IntOp $8 $8 + 1 + !insertmacro INSTALLOPTIONS_WRITE "UninstallOldVersions.ini" "Field $8" "Text" "$3" + StrCmp $8 $max_fields complete + ${EndIf} +get_next_key: + IntOp $4 $4 + 1 ;to check next record from registry + goto get_installation_info + +next_registry_root: +${If} $0 == "HKLM" + StrCpy $0 "HKCU" + StrCpy $4 0 + Goto get_installation_info +${EndIf} +complete: +!insertmacro INSTALLOPTIONS_WRITE "UninstallOldVersions.ini" "Settings" "NumFields" "$8" +${If} $8 > $control_fields + ;$2 used in prompt text + StrCpy $2 "s" + StrCpy $7 $control_fields + IntOp $7 $7 + 1 + StrCmp $8 $7 0 +2 + StrCpy $2 "" + !insertmacro MUI_HEADER_TEXT "$(uninstall_previous_installations_title)" "$(uninstall_previous_installations)" + !insertmacro INSTALLOPTIONS_WRITE "UninstallOldVersions.ini" "Field 1" "Text" "$(uninstall_previous_installations_prompt)" + !insertmacro INSTALLOPTIONS_WRITE "UninstallOldVersions.ini" "Field 3" "Flags" "FOCUS" + !insertmacro INSTALLOPTIONS_DISPLAY "UninstallOldVersions.ini" + ;uninstall chosen installation(s) + + ;no disabled controls. StrCmp $2 "OK" loop finish +loop: + !insertmacro INSTALLOPTIONS_READ $0 "UninstallOldVersions.ini" "Field $8" "State" + !insertmacro INSTALLOPTIONS_READ $3 "UninstallOldVersions.ini" "Field $8" "Text" + ${If} $0 == "1" + Call uninstallOldVersion + ${EndIf} + IntOp $8 $8 - 1 + StrCmp $8 $control_fields finish loop + ${EndIf} +finish: +FunctionEnd + + +Function getInstallationPath + Push $1 + Push $2 + Push $5 +loop: + Call OMEnumRegKey + StrCmp $3 "" 0 getPath + StrCpy $3 "complete" + goto done +getPath: + Push $1 + StrCpy $1 "$1\$3" + Call OMReadRegStr + Pop $1 + IfFileExists $3$5 done 0 + IntOp $4 $4 + 1 + goto loop +done: + Pop $5 + Pop $2 + Pop $1 +FunctionEnd + + +Function GUIInit + Push $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + +; is the current version of IDEA installed? + Call searchCurrentVersion + +; search old versions of IDEA + StrCpy $4 0 + StrCpy $0 "HKCU" + StrCpy $1 "Software\${MANUFACTURER}\${MUI_PRODUCT}" + StrCpy $5 "\bin\${PRODUCT_EXE_FILE}" + StrCpy $2 "" + Call getInstallationPath + StrCmp $3 "complete" all_users + IfFileExists $3\bin\${PRODUCT_EXE_FILE} old_version_located all_users +all_users: + StrCpy $4 0 + StrCpy $0 "HKLM" + Call getInstallationPath + StrCmp $3 "complete" success + IfFileExists $3\bin\${PRODUCT_EXE_FILE} 0 success +old_version_located: +; MessageBox MB_YESNO|MB_ICONQUESTION "$(previous_installations)" IDYES uninstall IDNO success +;uninstall: +; Call uninstallOldVersions + +success: + IntCmp ${SHOULD_SET_DEFAULT_INSTDIR} 0 end_enum_versions_hklm + StrCpy $3 "0" # latest build number + StrCpy $0 "0" # registry key index + +enum_versions_hkcu: + EnumRegKey $1 "HKCU" "Software\${MANUFACTURER}\${MUI_PRODUCT}" $0 + StrCmp $1 "" end_enum_versions_hkcu + IntCmp $1 $3 continue_enum_versions_hkcu continue_enum_versions_hkcu + StrCpy $3 $1 + ReadRegStr $INSTDIR "HKCU" "Software\${MANUFACTURER}\${MUI_PRODUCT}\$3" "" + +continue_enum_versions_hkcu: + IntOp $0 $0 + 1 + Goto enum_versions_hkcu + +end_enum_versions_hkcu: + + StrCpy $0 "0" # registry key index + +enum_versions_hklm: + EnumRegKey $1 "HKLM" "Software\${MANUFACTURER}\${MUI_PRODUCT}" $0 + StrCmp $1 "" end_enum_versions_hklm + IntCmp $1 $3 continue_enum_versions_hklm continue_enum_versions_hklm + StrCpy $3 $1 + ReadRegStr $INSTDIR "HKLM" "Software\${MANUFACTURER}\${MUI_PRODUCT}\$3" "" + +continue_enum_versions_hklm: + IntOp $0 $0 + 1 + Goto enum_versions_hklm + +end_enum_versions_hklm: + + StrCmp $INSTDIR "" 0 skip_default_instdir + StrCpy $INSTDIR "$PROGRAMFILES\${MANUFACTURER}\${MUI_PRODUCT} ${MUI_VERSION_MAJOR}.${MUI_VERSION_MINOR}" +skip_default_instdir: + + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 + !insertmacro INSTALLOPTIONS_EXTRACT "Desktop.ini" + +FunctionEnd + +Function DoAssociation + ; back up old value of an association + ReadRegStr $1 HKCR $R4 "" + StrCmp $1 "" skip_backup + StrCmp $1 ${PRODUCT_PATHS_SELECTOR} skip_backup + WriteRegStr HKCR $R4 "backup_val" $1 +skip_backup: + WriteRegStr HKCR $R4 "" "${PRODUCT_PATHS_SELECTOR}" + ReadRegStr $0 HKCR ${PRODUCT_PATHS_SELECTOR} "" + StrCmp $0 "" 0 command_exists + WriteRegStr HKCR ${PRODUCT_PATHS_SELECTOR} "" "${PRODUCT_FULL_NAME}" + WriteRegStr HKCR "${PRODUCT_PATHS_SELECTOR}\shell" "" "open" + WriteRegStr HKCR "${PRODUCT_PATHS_SELECTOR}\DefaultIcon" "" "$INSTDIR\bin\${PRODUCT_EXE_FILE},0" +command_exists: + WriteRegStr HKCR "${PRODUCT_PATHS_SELECTOR}\shell\open\command" "" \ + '$INSTDIR\bin\${PRODUCT_EXE_FILE} "%1"' +FunctionEnd + +;------------------------------------------------------------------------------ +; Installer sections +;------------------------------------------------------------------------------ +Section "IDEA Files" CopyIdeaFiles +; StrCpy $baseRegKey "HKCU" +; !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 3" "State" +; StrCmp $R2 1 continue_for_current_user +; SetShellVarContext all +; StrCpy $baseRegKey "HKLM" +; continue_for_current_user: + +; create shortcuts + + !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 4" "State" + StrCmp $R2 1 "" python3 + StrCpy $R2 "2.7" + goto check_python +python3: + StrCpy $R2 "3.4" +check_python: + ReadRegStr $1 "HKCU" "Software\Python\PythonCore\$R2\InstallPath" $0 + StrCmp $1 "" installation_for_all_users + goto verefy_python_launcher +installation_for_all_users: + ReadRegStr $1 "HKLM" "Software\Python\PythonCore\$R2\InstallPath" $0 + StrCmp $1 "" get_python +verefy_python_launcher: + IfFileExists $1\python.exe python_exists get_python + +get_python: + CreateDirectory "$INSTDIR\python" + StrCmp $R2 "2.7" get_python2 + inetc::get "https://www.python.org/ftp/python/3.4.1/python-3.4.1.amd64.msi" "$INSTDIR\python\python_$R2.msi" + goto validate_download +get_python2: + inetc::get "http://www.python.org/ftp/python/2.7.8/python-2.7.8.msi" "$INSTDIR\python\python_$R2.msi" +validate_download: + Pop $0 + ${If} $0 == "OK" + ExecCmd::exec 'msiexec /i "$INSTDIR\python\python_$R2.msi" /quiet /qn /norestart /log "$INSTDIR\python\python_$R2_silent.log"' + ${EndIf} + +python_exists: + !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 1" "State" + StrCmp $R2 1 "" skip_desktop_shortcut + CreateShortCut "$DESKTOP\${PRODUCT_FULL_NAME_WITH_VER}.lnk" \ + "$INSTDIR\bin\${PRODUCT_EXE_FILE}" "" "" "" SW_SHOWNORMAL + +skip_desktop_shortcut: + ; OS is not win7 + Call winVersion + ${If} $0 == "0" + !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 2" "State" + StrCmp $R2 1 "" skip_quicklaunch_shortcut + CreateShortCut "$QUICKLAUNCH\${PRODUCT_FULL_NAME_WITH_VER}.lnk" \ + "$INSTDIR\bin\${PRODUCT_EXE_FILE}" "" "" "" SW_SHOWNORMAL + ${EndIf} +skip_quicklaunch_shortcut: + + !insertmacro INSTALLOPTIONS_READ $R1 "Desktop.ini" "Settings" "NumFields" + IntCmp $R1 ${INSTALL_OPTION_ELEMENTS} do_association done do_association +do_association: + StrCpy $R2 ${INSTALL_OPTION_ELEMENTS} +get_user_choice: + !insertmacro INSTALLOPTIONS_READ $R3 "Desktop.ini" "Field $R2" "State" + StrCmp $R3 1 "" next_association + !insertmacro INSTALLOPTIONS_READ $R4 "Desktop.ini" "Field $R2" "Text" + call DoAssociation +next_association: + IntOp $R2 $R2 + 1 + IntCmp $R1 $R2 get_user_choice done get_user_choice + +done: +!insertmacro MUI_STARTMENU_WRITE_BEGIN Application +; $STARTMENU_FOLDER stores name of IDEA folder in Start Menu, +; save it name in the "MenuFolder" RegValue + CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" + + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\${PRODUCT_FULL_NAME_WITH_VER}.lnk" \ + "$INSTDIR\bin\${PRODUCT_EXE_FILE}" "" "" "" SW_SHOWNORMAL +; CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall ${PRODUCT_FULL_NAME_WITH_VER}.lnk" \ +; "$INSTDIR\bin\Uninstall.exe" + StrCpy $0 $baseRegKey + StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" + StrCpy $2 "MenuFolder" + StrCpy $3 "$STARTMENU_FOLDER" + Call OMWriteRegStr +!insertmacro MUI_STARTMENU_WRITE_END + + StrCmp ${IPR} "false" skip_ipr + + ; back up old value of .ipr +!define Index "Line${__LINE__}" + ReadRegStr $1 HKCR ".ipr" "" + StrCmp $1 "" "${Index}-NoBackup" + StrCmp $1 "IntelliJIdeaProjectFile" "${Index}-NoBackup" + WriteRegStr HKCR ".ipr" "backup_val" $1 +"${Index}-NoBackup:" + WriteRegStr HKCR ".ipr" "" "IntelliJIdeaProjectFile" + ReadRegStr $0 HKCR "IntelliJIdeaProjectFile" "" + StrCmp $0 "" 0 "${Index}-Skip" + WriteRegStr HKCR "IntelliJIdeaProjectFile" "" "IntelliJ IDEA Project File" + WriteRegStr HKCR "IntelliJIdeaProjectFile\shell" "" "open" + WriteRegStr HKCR "IntelliJIdeaProjectFile\DefaultIcon" "" "$INSTDIR\bin\idea.exe,0" +"${Index}-Skip:" + WriteRegStr HKCR "IntelliJIdeaProjectFile\shell\open\command" "" \ + '$INSTDIR\bin\${PRODUCT_EXE_FILE} "%1"' +!undef Index + +skip_ipr: + +; readonly section + SectionIn RO +!include "idea_win.nsh" + + IntCmp $IS_UPGRADE_60 1 skip_properties + SetOutPath $INSTDIR\bin + File "${PRODUCT_PROPERTIES_FILE}" + File "${PRODUCT_VM_OPTIONS_FILE}" +skip_properties: + + StrCpy $0 $baseRegKey + StrCpy $1 "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" + StrCpy $2 "" + StrCpy $3 "$INSTDIR" + Call OMWriteRegStr + StrCpy $2 "Build" + StrCpy $3 ${VER_BUILD} + Call OMWriteRegStr + +; write uninstaller & add it to add/remove programs in control panel + WriteUninstaller "$INSTDIR\bin\Uninstall.exe" + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "DisplayName" "${PRODUCT_FULL_NAME_WITH_VER}" + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "UninstallString" "$INSTDIR\bin\Uninstall.exe" + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "InstallLocation" "$INSTDIR" + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "DisplayIcon" "$INSTDIR\bin\${PRODUCT_EXE_FILE}" + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "DisplayVersion" "${VER_BUILD}" + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "Publisher" "JetBrains s.r.o." + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "URLInfoAbout" "http://www.jetbrains.com/products" + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "InstallType" "$baseRegKey" + WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "NoModify" 1 + WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \ + "NoRepair" 1 + + ExecWait "$INSTDIR\jre\jre\bin\javaw.exe -Xshare:dump" + SetOutPath $INSTDIR\bin + ; set the current time for installation files under $INSTDIR\bin + ExecCmd::exec 'copy "$INSTDIR\bin\*.*s" +,,' + call winVersion + ${If} $0 == "1" + ;ExecCmd::exec 'icacls "$INSTDIR" /grant %username%:F /T >"$INSTDIR"\installation_log.txt 2>"$INSTDIR"\installation_error.txt' + AccessControl::GrantOnFile \ + "$INSTDIR" "(S-1-5-32-545)" "GenericRead + GenericExecute" + ${EndIf} +SectionEnd + +;------------------------------------------------------------------------------ +; Descriptions of sections +;------------------------------------------------------------------------------ +; LangString DESC_CopyRuntime ${LANG_ENGLISH} "${MUI_PRODUCT} files" + +;------------------------------------------------------------------------------ +; custom install pages +;------------------------------------------------------------------------------ + +Function ConfirmDesktopShortcut + !insertmacro MUI_HEADER_TEXT "$(installation_options)" "$(installation_options_prompt)" + !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 1" "Text" "$(create_desktop_shortcut)" + call winVersion + !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 2" "Text" "$(create_quick_launch_shortcut)" + ${If} $0 == "1" + !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 2" "Flags" "DISABLED" + ${EndIf} + StrCmp "${ASSOCIATION}" "NoAssociation" skip_association + StrCpy $R0 6 + push "${ASSOCIATION}" +loop: + call SplitStr + Pop $0 + StrCmp $0 "" done + IntOp $R0 $R0 + 1 + !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field $R0" "Text" "$0" + goto loop +skip_association: + StrCpy $R0 2 + call winVersion + ${If} $0 == "1" + IntOp $R0 $R0 - 1 + ${EndIf} +done: + !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Settings" "NumFields" "$R0" + !insertmacro INSTALLOPTIONS_DISPLAY "Desktop.ini" +FunctionEnd + + +;------------------------------------------------------------------------------ +; custom uninstall functions +;------------------------------------------------------------------------------ + +Function un.onInit + ;admin perm. is required to uninstall? + ${UnStrStr} $R0 $INSTDIR $PROGRAMFILES + StrCmp $R0 $INSTDIR requred_admin_perm UAC_Done + +requred_admin_perm: + ;the user has admin rights? + UserInfo::GetAccountType + Pop $R2 + StrCmp $R2 "Admin" UAC_Admin uninstall_location + +uninstall_location: + ;check if the uninstallation is running from the product location + IfFileExists $APPDATA\${PRODUCT_PATHS_SELECTOR}_${VER_BUILD}_Uninstall.exe UAC_Elevate copy_uninstall + +copy_uninstall: + ;do copy for unistall.exe + CopyFiles "$OUTDIR\Uninstall.exe" "$APPDATA\${PRODUCT_PATHS_SELECTOR}_${VER_BUILD}_Uninstall.exe" + ExecWait '"$APPDATA\${PRODUCT_PATHS_SELECTOR}_${VER_BUILD}_Uninstall.exe" _?=$INSTDIR' + Delete "$APPDATA\${PRODUCT_PATHS_SELECTOR}_${VER_BUILD}_Uninstall.exe" + Quit + +UAC_Elevate: + !insertmacro UAC_RunElevated + StrCmp 1223 $0 UAC_ElevationAborted ; UAC dialog aborted by user? - continue install under user + StrCmp 0 $0 0 UAC_Err ; Error? + StrCmp 1 $1 0 UAC_Success ;Are we the real deal or just the wrapper? + Quit +UAC_ElevationAborted: +UAC_Err: + Abort +UAC_Success: + StrCmp 1 $3 UAC_Admin ;Admin? + StrCmp 3 $1 0 UAC_ElevationAborted ;Try again? + goto UAC_Elevate +UAC_Admin: + SetShellVarContext all + StrCpy $baseRegKey "HKLM" +UAC_Done: + !insertmacro MUI_UNGETLANGUAGE + !insertmacro INSTALLOPTIONS_EXTRACT "DeleteSettings.ini" +FunctionEnd + +Function OMEnumRegKey + StrCmp $0 "HKCU" hkcu + EnumRegKey $3 HKLM $1 $4 + goto done +hkcu: + EnumRegKey $3 HKCU $1 $4 +done: +FunctionEnd + +Function un.OMReadRegStr + StrCmp $0 "HKCU" hkcu + ReadRegStr $3 HKLM $1 $2 + goto done +hkcu: + ReadRegStr $3 HKCU $1 $2 +done: +FunctionEnd + +Function un.OMDeleteRegValue + StrCmp $0 "HKCU" hkcu + DeleteRegValue HKLM $1 $2 + goto done +hkcu: + DeleteRegValue HKCU $1 $2 +done: +FunctionEnd + +Function un.ReturnBackupRegValue + ;replace Default str with the backup value (if there is the one) and then delete backup + ; $1 - key (for example ".java") + ; $2 - name (for example "backup_val") + Push $0 + ReadRegStr $0 HKCR $1 $2 + StrCmp $0 "" "noBackup" + WriteRegStr HKCR $1 "" $0 + DeleteRegValue HKCR $1 $2 +noBackup: + Pop $0 +FunctionEnd + +Function un.OMDeleteRegKeyIfEmpty + StrCmp $0 "HKCU" hkcu + DeleteRegKey /ifempty HKLM $1 + goto done +hkcu: + DeleteRegKey /ifempty HKCU $1 +done: +FunctionEnd + +Function un.OMDeleteRegKey + StrCmp $0 "HKCU" hkcu + DeleteRegKey /ifempty HKLM $1 + goto done +hkcu: + DeleteRegKey /ifempty HKCU $1 +done: +FunctionEnd + +Function un.OMWriteRegStr + StrCmp $0 "HKCU" hkcu + WriteRegStr HKLM $1 $2 $3 + goto done +hkcu: + WriteRegStr HKCU $1 $2 $3 +done: +FunctionEnd + + +;------------------------------------------------------------------------------ +; custom uninstall pages +;------------------------------------------------------------------------------ + +Function un.ConfirmDeleteSettings + !insertmacro MUI_HEADER_TEXT "$(uninstall_options)" "$(uninstall_options_prompt)" + !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 1" "Text" "$(prompt_delete_settings)" + !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 2" "Text" $INSTDIR + !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 3" "Text" "$(text_delete_settings)" + !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 4" "Text" "$(confirm_delete_caches)" + !insertmacro INSTALLOPTIONS_WRITE "DeleteSettings.ini" "Field 5" "Text" "$(confirm_delete_settings)" + !insertmacro INSTALLOPTIONS_DISPLAY "DeleteSettings.ini" +FunctionEnd + + +Function un.PrepareCustomPath + ;Input: + ;$0 - name of variable + ;$1 - value of the variable + ;$2 - line from the property file + push $3 + push $5 + ${UnStrLoc} $3 $2 $0 ">" + StrCmp $3 "" not_found + StrLen $5 $0 + IntOp $3 $3 + $5 + StrCpy $2 $2 "" $3 + IfFileExists "$1$2\\*.*" not_found + StrCpy $2 $1$2 + goto complete +not_found: + StrCpy $0 "" +complete: + pop $5 + pop $3 +FunctionEnd + + +Function un.getCustomPath + push $0 + push $1 + StrCpy $0 "${user.home}/" + StrCpy $1 "$PROFILE/" + Call un.PrepareCustomPath + StrCmp $0 "" check_idea_var + goto complete +check_idea_var: + StrCpy $0 "${idea.home}/" + StrCpy $1 "$INSTDIR/" + Call un.PrepareCustomPath + StrCmp $2 "" +1 +2 + StrCpy $2 "" +complete: + pop $1 + pop $0 +FunctionEnd + + +Function un.getPath +; The function read lines from idea.properties and search the substring and prepare the path to settings or caches. + ClearErrors + FileOpen $3 $INSTDIR\bin\idea.properties r + IfErrors complete ;file can not be open. not sure if a message should be displayed in this case. + StrLen $5 $1 +read_line: + FileRead $3 $4 + StrCmp $4 "" complete + ${UnStrLoc} $6 $4 $1 ">" + StrCmp $6 "" read_line ; there is no substring in a string from the file. go for next one. + IntOp $6 $6 + $5 + ${unStrStr} $7 $4 "#" ;check if the property has been customized + StrCmp $7 "" custom + StrCpy $2 "$PROFILE/${PRODUCT_SETTINGS_DIR}/$0" ;no. use the default value. + goto complete +custom: + StrCpy $2 $4 "" $6 + Call un.getCustomPath +complete: + FileClose $3 + ${UnStrRep} $2 $2 "/" "\" +FunctionEnd + + +Section "Uninstall" + StrCpy $baseRegKey "HKCU" + ; Uninstaller is in the \bin directory, we need upper level dir + StrCpy $INSTDIR $INSTDIR\.. + + !insertmacro INSTALLOPTIONS_READ $R2 "DeleteSettings.ini" "Field 4" "State" + DetailPrint "Data: $DOCUMENTS\..\${PRODUCT_SETTINGS_DIR}\" + StrCmp $R2 1 "" skip_delete_caches + ;find the path to caches (system) folder + StrCpy $0 "system" + StrCpy $1 "idea.system.path=" + Call un.getPath + StrCmp $2 "" skip_delete_caches + StrCpy $system_path $2 + RmDir /r "$system_path" + RmDir "$system_path\\.." ; remove parent of system dir if the dir is empty +; RmDir /r $DOCUMENTS\..\${PRODUCT_SETTINGS_DIR}\system +skip_delete_caches: + + !insertmacro INSTALLOPTIONS_READ $R3 "DeleteSettings.ini" "Field 5" "State" + StrCmp $R3 1 "" skip_delete_settings + ;find the path to settings (config) folder + StrCpy $0 "config" + StrCpy $1 "idea.config.path=" + Call un.getPath + StrCmp $2 "" skip_delete_settings + StrCpy $config_path $2 + RmDir /r "$config_path" +; RmDir /r $DOCUMENTS\..\${PRODUCT_SETTINGS_DIR}\config + Delete "$INSTDIR\bin\${PRODUCT_VM_OPTIONS_NAME}" + Delete "$INSTDIR\bin\idea.properties" + StrCmp $R2 1 "" skip_delete_settings + RmDir "$config_path\\.." ; remove parent of config dir if the dir is empty +; RmDir $DOCUMENTS\..\${PRODUCT_SETTINGS_DIR} +skip_delete_settings: + +; Delete uninstaller itself + Delete "$INSTDIR\bin\Uninstall.exe" + Delete "$INSTDIR\jre\jre\bin\client\classes.jsa" + + Push "Complete" + Push "$INSTDIR\bin\${PRODUCT_EXE_FILE}.vmoptions" + Push "$INSTDIR\bin\idea.properties" + ${UnStrRep} $0 ${PRODUCT_EXE_FILE} ".exe" "64.exe.vmoptions" + Push "$INSTDIR\bin\$0" + Call un.compareFileInstallationTime + ${If} $9 != "Modified" + RMDir /r "$INSTDIR" + ${Else} + !include "unidea_win.nsh" + RMDir "$INSTDIR" + ${EndIf} + + ReadRegStr $R9 HKCU "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" "MenuFolder" + StrCmp $R9 "" "" clear_shortcuts + ReadRegStr $R9 HKLM "Software\${MANUFACTURER}\${PRODUCT_REG_VER}" "MenuFolder" + StrCmp $R9 "" clear_Registry + StrCpy $baseRegKey "HKLM" + StrCpy $5 "Software\${MANUFACTURER}" +; call un.winVersion +; ${If} $0 == "1" +; StrCpy $5 "Software\Wow6432Node\${MANUFACTURER}" +; ${EndIf} +clear_shortcuts: + ;the user has the admin rights +; UserInfo::GetAccountType +; Pop $R2 + IfFileExists "$DESKTOP\${PRODUCT_FULL_NAME_WITH_VER}.lnk" keep_current_user + SetShellVarContext all +keep_current_user: + DetailPrint "Start Menu: $SMPROGRAMS\$R9\${PRODUCT_FULL_NAME_WITH_VER}" + + Delete "$SMPROGRAMS\$R9\${PRODUCT_FULL_NAME_WITH_VER}.lnk" +; Delete "$SMPROGRAMS\$R9\Uninstall ${PRODUCT_FULL_NAME_WITH_VER}.lnk" +; Delete only if empty (last IDEA version is uninstalled) + RMDir "$SMPROGRAMS\$R9" + + Delete "$DESKTOP\${PRODUCT_FULL_NAME_WITH_VER}.lnk" + Delete "$QUICKLAUNCH\${PRODUCT_FULL_NAME_WITH_VER}.lnk" + +clear_Registry: + StrCpy $5 "Software\${MANUFACTURER}" +; call un.winVersion +; ${If} $0 == "1" +; StrCpy $5 "Software\Wow6432Node\${MANUFACTURER}" +; ${EndIf} + + StrCpy $0 $baseRegKey + StrCpy $1 "$5\${PRODUCT_REG_VER}" + StrCpy $2 "MenuFolder" + Call un.OMDeleteRegValue + + StrCmp "${ASSOCIATION}" "NoAssociation" finish_uninstall + push "${ASSOCIATION}" +loop: + call un.SplitStr + Pop $0 + StrCmp $0 "" finish_uninstall + StrCpy $1 $0 + StrCpy $2 "backup_val" + Call un.ReturnBackupRegValue + goto loop +finish_uninstall: + StrCpy $1 "$5\${PRODUCT_REG_VER}" + StrCpy $2 "Build" + Call un.OMDeleteRegValue + StrCpy $2 "" + Call un.OMDeleteRegValue + + StrCpy $1 "$5\${PRODUCT_REG_VER}" + Call un.OMDeleteRegKeyIfEmpty + + StrCpy $1 "$5\${MUI_PRODUCT}" + Call un.OMDeleteRegKeyIfEmpty + + StrCpy $1 "$5" + Call un.OMDeleteRegKeyIfEmpty + + DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" + +; UNCOMMENT THIS IN RELEASE BUILD +; ExecShell "" "http://www.jetbrains.com/idea/uninstall/" + +SectionEnd diff --git a/python/edu/learn-python/gen/icons/StudyIcons.java b/python/edu/learn-python/gen/icons/StudyIcons.java index 9223fb353350..28409103ea00 100644 --- a/python/edu/learn-python/gen/icons/StudyIcons.java +++ b/python/edu/learn-python/gen/icons/StudyIcons.java @@ -16,7 +16,6 @@ public class StudyIcons { public static final Icon Add = load("/icons/com/jetbrains/python/edu/add.png"); // 16x16 public static final Icon Checked = load("/icons/com/jetbrains/python/edu/checked.png"); // 32x32 public static final Icon Failed = load("/icons/com/jetbrains/python/edu/failed.png"); // 32x32 - public static final Icon FatalError = load("/icons/com/jetbrains/python/edu/fatalError.png"); // 16x16 public static final Icon Next = load("/icons/com/jetbrains/python/edu/next.png"); // 24x24 public static final Icon Playground = load("/icons/com/jetbrains/python/edu/playground.png"); // 32x28 public static final Icon Prev = load("/icons/com/jetbrains/python/edu/prev.png"); // 24x24 @@ -24,7 +23,6 @@ public class StudyIcons { public static final Icon Refresh24 = load("/icons/com/jetbrains/python/edu/refresh24.png"); // 24x24 public static final Icon Resolve = load("/icons/com/jetbrains/python/edu/resolve.png"); // 24x24 public static final Icon Run = load("/icons/com/jetbrains/python/edu/Run.png"); // 24x24 - public static final Icon ShortcutReminder = load("/icons/com/jetbrains/python/edu/ShortcutReminder.png"); // 24x24 public static final Icon ShowHint = load("/icons/com/jetbrains/python/edu/showHint.png"); // 24x24 public static final Icon Unchecked = load("/icons/com/jetbrains/python/edu/unchecked.png"); // 32x32 public static final Icon WatchInput = load("/icons/com/jetbrains/python/edu/WatchInput.png"); // 24x24 diff --git a/python/edu/learn-python/resources/courses/introduction_course.zip b/python/edu/learn-python/resources/courses/introduction_course.zip index 0664e5b86523..f3b24f24b1e2 100644 Binary files a/python/edu/learn-python/resources/courses/introduction_course.zip and b/python/edu/learn-python/resources/courses/introduction_course.zip differ diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/ShortcutReminder.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/ShortcutReminder.png deleted file mode 100644 index 6efcb97c56a7..000000000000 Binary files a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/ShortcutReminder.png and /dev/null differ diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png index 72461343aa25..4105a01f1353 100644 Binary files a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png and b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png differ diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png index d3815dd80867..e2aaa556056e 100644 Binary files a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png and b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png differ diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/fatalError.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/fatalError.png deleted file mode 100755 index 7ca7e03b12cf..000000000000 Binary files a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/fatalError.png and /dev/null differ diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png index 63d1810bc94b..d12a751c0c40 100644 Binary files a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png and b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png differ diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png index a3586004a491..2145982cf2be 100644 Binary files a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png and b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png differ diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java index 4a516aa36398..d3ac1dadf98e 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java @@ -19,6 +19,7 @@ import com.jetbrains.python.edu.course.TaskWindow; import com.jetbrains.python.edu.editor.StudyEditor; import com.jetbrains.python.edu.ui.StudyToolWindowFactory; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.Collection; @@ -50,9 +51,11 @@ public class StudyUtils { } @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + @Nullable public static String getFileText(String parentDir, String fileName, boolean wrapHTML) { File inputFile = parentDir !=null ? new File(parentDir, fileName) : new File(fileName); + if (!inputFile.exists()) return null; StringBuilder taskText = new StringBuilder(); BufferedReader reader = null; try { diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java index ad3df265ccda..f8e10c9c4521 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java @@ -11,6 +11,8 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; @@ -33,7 +35,8 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; import java.io.*; -import java.util.Map; +import java.util.*; +import java.util.List; public class StudyCheckAction extends DumbAwareAction { @@ -111,13 +114,26 @@ public class StudyCheckAction extends DumbAwareAction { if (openedFile != null) { StudyTaskManager taskManager = StudyTaskManager.getInstance(project); final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile); + List filesToDelete = new ArrayList(); if (selectedTaskFile != null) { - VirtualFile windowsDescription = StudyUtils.flushWindows(selectedEditor.getDocument(), selectedTaskFile, openedFile); - FileDocumentManager.getInstance().saveAllDocuments(); final VirtualFile taskDir = openedFile.getParent(); Task currentTask = selectedTaskFile.getTask(); + StudyStatus oldStatus = currentTask.getStatus(); + Map taskFiles = selectedTaskFile.getTask().getTaskFiles(); + for (Map.Entry entry : taskFiles.entrySet()) { + String name = entry.getKey(); + TaskFile taskFile = entry.getValue(); + VirtualFile virtualFile = taskDir.findChild(name); + if (virtualFile == null) { + continue; + } + VirtualFile windowFile = StudyUtils.flushWindows(FileDocumentManager.getInstance().getDocument(virtualFile), taskFile, virtualFile); + filesToDelete.add(windowFile); + FileDocumentManager.getInstance().saveAllDocuments(); + } + StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID); - if (runAction != null) { + if (runAction != null && currentTask.getTaskFiles().size() == 1) { runAction.run(project); } final StudyTestRunner testRunner = new StudyTestRunner(currentTask, taskDir); @@ -131,36 +147,68 @@ public class StudyCheckAction extends DumbAwareAction { if (testProcess != null) { String failedMessage = testRunner.getPassedTests(testProcess); if (failedMessage.equals(StudyTestRunner.TEST_OK)) { - currentTask.setStatus(StudyStatus.Solved); + currentTask.setStatus(StudyStatus.Solved, oldStatus); StudyUtils.updateStudyToolWindow(project); selectedTaskFile.drawAllWindows(selectedEditor); ProjectView.getInstance(project).refresh(); + for (VirtualFile file:filesToDelete) { + try { + file.delete(this); + } + catch (IOException e) { + LOG.error(e); + } + } createTestResultPopUp("Congratulations!", JBColor.GREEN, project); return; } - - final TaskFile taskFileCopy = new TaskFile(); - final VirtualFile copyWithAnswers = getCopyWithAnswers(taskDir, openedFile, selectedTaskFile, taskFileCopy); - for (final TaskWindow taskWindow : taskFileCopy.getTaskWindows()) { - if (!taskWindow.isValid(selectedEditor.getDocument())) { + for (Map.Entry entry : taskFiles.entrySet()) { + String name = entry.getKey(); + TaskFile taskFile = entry.getValue(); + TaskFile answerTaskFile = new TaskFile(); + VirtualFile virtualFile = taskDir.findChild(name); + if (virtualFile == null) { continue; } - check(project, taskWindow, copyWithAnswers, taskFileCopy, selectedTaskFile, selectedEditor.getDocument(), testRunner, - openedFile); + VirtualFile answerFile = getCopyWithAnswers(taskDir, virtualFile, taskFile, answerTaskFile); + for (TaskWindow taskWindow : answerTaskFile.getTaskWindows()) { + Document document = FileDocumentManager.getInstance().getDocument(virtualFile); + if (document == null) { + continue; + } + if (!taskWindow.isValid(document)) { + continue; + } + check(project, taskWindow, answerFile, answerTaskFile, taskFile, document, testRunner, virtualFile); + } + FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile); + Editor editor = null; + if (fileEditor instanceof StudyEditor) { + StudyEditor studyEditor = (StudyEditor) fileEditor; + editor = studyEditor.getEditor(); + } + + if (editor != null) { + taskFile.drawAllWindows(editor); + StudyUtils.synchronize(); + } + try { + answerFile.delete(this); + } + catch (IOException e) { + LOG.error(e); + } } - try { - copyWithAnswers.delete(this); + for (VirtualFile file:filesToDelete) { + try { + file.delete(this); + } + catch (IOException e) { + LOG.error(e); + } } - catch (IOException e) { - LOG.error(e); - } - try { - windowsDescription.delete(this); - } - catch (IOException e) { - LOG.error("failed to delete windows description", e); - } - selectedTaskFile.drawAllWindows(selectedEditor); + currentTask.setStatus(StudyStatus.Failed, oldStatus); + StudyUtils.updateStudyToolWindow(project); createTestResultPopUp(failedMessage, JBColor.RED, project); } } @@ -183,7 +231,7 @@ public class StudyCheckAction extends DumbAwareAction { VirtualFile openedFile) { try { - VirtualFile windowCopy = answerFile.copy(this, answerFile.getParent(), "window" + taskWindow.getIndex() + ".py"); + VirtualFile windowCopy = answerFile.copy(this, answerFile.getParent(), answerFile.getNameWithoutExtension() + "_window" + taskWindow.getIndex() + ".py"); final FileDocumentManager documentManager = FileDocumentManager.getInstance(); final Document windowDocument = documentManager.getDocument(windowCopy); if (windowDocument != null) { @@ -216,7 +264,7 @@ public class StudyCheckAction extends DumbAwareAction { VirtualFile fileWindows = StudyUtils.flushWindows(windowDocument, windowTaskFile, windowCopy); Process smartTestProcess = testRunner.launchTests(project, windowCopy.getPath()); boolean res = testRunner.getPassedTests(smartTestProcess).equals(StudyTestRunner.TEST_OK); - userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed); + userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed, StudyStatus.Unchecked); windowCopy.delete(this); fileWindows.delete(this); if (!resourceFile.delete()) { @@ -240,7 +288,7 @@ public class StudyCheckAction extends DumbAwareAction { VirtualFile copy = null; try { - copy = file.copy(this, taskDir, "answers.py"); + copy = file.copy(this, taskDir, file.getNameWithoutExtension() +"_answers.py"); final FileDocumentManager documentManager = FileDocumentManager.getInstance(); final Document document = documentManager.getDocument(copy); if (document != null) { diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java index b245b46f90ef..f8abb0b63365 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java @@ -81,13 +81,8 @@ public class StudyRefreshTaskAction extends DumbAwareAction { document.setText(patternText); StudyStatus oldStatus = currentTask.getStatus(); LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo(); - if (oldStatus == StudyStatus.Failed) { - lessonInfo.setTaskFailed(lessonInfo.getTaskFailed() - 1); - } - if (oldStatus == StudyStatus.Solved) { - lessonInfo.setTaskSolved(lessonInfo.getTaskSolved() - 1); - } - lessonInfo.setTaskUnchecked(lessonInfo.getTaskUnchecked() + 1); + lessonInfo.update(oldStatus, -1); + lessonInfo.update(StudyStatus.Unchecked, +1); StudyUtils.updateStudyToolWindow(project); for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) { taskWindow.reset(); diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java index 2ce200bfc641..b781e7da8849 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java @@ -67,12 +67,21 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction { if (taskDir == null) { return; } - for (String name : nextTaskFiles.keySet()) { + VirtualFile shouldBeActive = null; + for (Map.Entry entry : nextTaskFiles.entrySet()) { + String name = entry.getKey(); + TaskFile taskFile = entry.getValue(); VirtualFile vf = taskDir.findChild(name); if (vf != null) { FileEditorManager.getInstance(project).openFile(vf, true); + if (!taskFile.getTaskWindows().isEmpty()) { + shouldBeActive = vf; + } } } + if (shouldBeActive != null) { + FileEditorManager.getInstance(project).openFile(shouldBeActive, true); + } } protected abstract JButton getButton(StudyEditor selectedStudyEditor); diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Lesson.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Lesson.java index 84396ea404d4..3879d519957e 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Lesson.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Lesson.java @@ -33,9 +33,9 @@ public class Lesson implements Stateful{ } @Override - public void setStatus(StudyStatus status) { + public void setStatus(StudyStatus status, StudyStatus oldStatus) { for (Task task : taskList) { - task.setStatus(status); + task.setStatus(status, oldStatus); } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/LessonInfo.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/LessonInfo.java index 9431632c2edd..85e2eb8be1a9 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/LessonInfo.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/LessonInfo.java @@ -40,4 +40,21 @@ public class LessonInfo { public void setTaskUnchecked(int taskUnchecked) { myTaskUnchecked = taskUnchecked; } + + public void update(StudyStatus status, int delta) { + switch (status) { + case Solved: { + myTaskSolved += delta; + break; + } + case Failed: { + myTaskFailed += delta; + break; + } + case Unchecked: { + myTaskUnchecked += delta; + break; + } + } + } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Stateful.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Stateful.java index 10374bd94d9a..3a163622f56d 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Stateful.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Stateful.java @@ -2,5 +2,5 @@ package com.jetbrains.python.edu.course; public interface Stateful { StudyStatus getStatus(); - void setStatus(StudyStatus status); + void setStatus(StudyStatus status, StudyStatus oldStatus); } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Task.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Task.java index a493b0ebfe8e..2323412f4374 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/Task.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/Task.java @@ -55,21 +55,14 @@ public class Task implements Stateful{ this.name = name; } - public void setStatus(@NotNull final StudyStatus status) { + public void setStatus(@NotNull final StudyStatus status, @NotNull final StudyStatus oldStatus) { LessonInfo lessonInfo = myLesson.getLessonInfo(); - StudyStatus oldStatus = getStatus(); if (status != oldStatus) { - if (status == StudyStatus.Failed) { - lessonInfo.setTaskFailed(lessonInfo.getTaskFailed() + 1); - lessonInfo.setTaskUnchecked(lessonInfo.getTaskUnchecked() - 1); - } - if (status == StudyStatus.Solved) { - lessonInfo.setTaskSolved(lessonInfo.getTaskSolved() + 1); - lessonInfo.setTaskUnchecked(lessonInfo.getTaskUnchecked() - 1); - } - for (TaskFile taskFile : taskFiles.values()) { - taskFile.setStatus(status); - } + lessonInfo.update(oldStatus, -1); + lessonInfo.update(status, +1); + } + for (TaskFile taskFile : taskFiles.values()) { + taskFile.setStatus(status, oldStatus); } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java index 06d5736bd81a..4f17fc0d27f3 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java @@ -200,9 +200,9 @@ public class TaskFile implements Stateful{ this.taskWindows = taskWindows; } - public void setStatus(@NotNull final StudyStatus status) { + public void setStatus(@NotNull final StudyStatus status, @NotNull final StudyStatus oldStatus) { for (TaskWindow taskWindow : taskWindows) { - taskWindow.setStatus(status); + taskWindow.setStatus(status, oldStatus); } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java index 04e65c3abb59..4fb112cc1f9b 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java @@ -28,13 +28,13 @@ public class TaskWindow implements Comparable, Stateful { public int myInitialLine = -1; public int myInitialStart = -1; public int myInitialLength = -1; - private StudyStatus myStatus = StudyStatus.Unchecked; + public StudyStatus myStatus = StudyStatus.Unchecked; public StudyStatus getStatus() { return myStatus; } - public void setStatus(StudyStatus status) { + public void setStatus(StudyStatus status, StudyStatus oldStatus) { myStatus = status; } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java index b318386a8aff..69c5acc5f127 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java @@ -104,7 +104,7 @@ public class StudyEditor implements TextEditor { } } - private static void initializeTaskText(JPanel studyPanel, String taskText) { + private static void initializeTaskText(JPanel studyPanel, @Nullable String taskText) { JTextPane taskTextPane = new JTextPane(); taskTextPane.setContentType("text/html"); taskTextPane.setEditable(false); diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGenerator.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGenerator.java index c1732a306120..b0165652173e 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGenerator.java +++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/XmlZenCodingGenerator.java @@ -100,12 +100,16 @@ public abstract class XmlZenCodingGenerator extends ZenCodingGenerator { PsiElement prevVisibleLeaf = callback.getContext(); while (prevVisibleLeaf != null) { TextRange textRange = prevVisibleLeaf.getTextRange(); - if (textRange.getEndOffset() <= startOffset) { + final int endOffset = textRange.getEndOffset(); + if (endOffset > currentOffset) { + continue; + } + if (endOffset <= startOffset) { break; } IElementType prevType = prevVisibleLeaf.getNode().getElementType(); if (prevType == XmlTokenType.XML_TAG_END || prevType == XmlTokenType.XML_EMPTY_ELEMENT_END) { - startOffset = textRange.getEndOffset(); + startOffset = endOffset; break; } prevVisibleLeaf = PsiTreeUtil.prevVisibleLeaf(prevVisibleLeaf); diff --git a/xml/relaxng/src/resources/html5-schema/html5-svg-mathml.rnc b/xml/relaxng/src/resources/html5-schema/html5-svg-mathml.rnc index fadc971d2bb4..fe154b8f57bf 100644 --- a/xml/relaxng/src/resources/html5-schema/html5-svg-mathml.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5-svg-mathml.rnc @@ -10,8 +10,6 @@ common.elem.phrasing |= math SVG.foreignObject.content |= ( math - | html.elem - | body.elem | common.inner.flow ) diff --git a/xml/relaxng/src/resources/html5-schema/html5/applications.rnc b/xml/relaxng/src/resources/html5-schema/html5/applications.rnc index ac07294db29c..2a592fbbd781 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/applications.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/applications.rnc @@ -377,6 +377,7 @@ datatypes w = "http://whattf.org/datatype-draft" | common.attrs.aria.role.combobox | common.attrs.aria.role.dialog | common.attrs.aria.role.directory + | common.attrs.aria.role.group | common.attrs.aria.role.heading | common.attrs.aria.role.img | common.attrs.aria.role.link @@ -425,7 +426,16 @@ datatypes w = "http://whattf.org/datatype-draft" ( common.attrs & ( common.attrs.aria.role.presentation | common.attrs.aria.role.menuitem + | common.attrs.aria.role.button )? ) summary.inner = - ( common.inner.phrasing ) + ( common.inner.phrasing + | h1.elem + | h2.elem + | h3.elem + | h4.elem + | h5.elem + | h6.elem + | hgroup.elem + ) diff --git a/xml/relaxng/src/resources/html5-schema/html5/aria.rnc b/xml/relaxng/src/resources/html5-schema/html5/aria.rnc index b1035a0fa2a6..cf412e5f4ace 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/aria.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/aria.rnc @@ -133,15 +133,7 @@ common.attrs.aria.implicit.toolbar |= & aria.prop.activedescendant? ) -common.attrs.aria.implicit.columnheader |= - ( aria.prop.sort? - & aria.prop.readonly? - & aria.prop.required? - & aria.state.selected? - & aria.state.expanded? - ) - -common.attrs.aria.implicit.rowheader |= +common.attrs.aria.implicit.column-or-row-header |= ( aria.prop.sort? & aria.prop.readonly? & aria.prop.required? diff --git a/xml/relaxng/src/resources/html5-schema/html5/block.rnc b/xml/relaxng/src/resources/html5-schema/html5/block.rnc index 2fd2d233c71b..de3b5a54ee2d 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/block.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/block.rnc @@ -112,6 +112,7 @@ datatypes w = "http://whattf.org/datatype-draft" & ol.attrs.reversed? & ol.attrs.type? & ( ( common.attrs.aria.role.directory + | common.attrs.aria.role.group | common.attrs.aria.role.list | common.attrs.aria.role.listbox | common.attrs.aria.role.menu diff --git a/xml/relaxng/src/resources/html5-schema/html5/common.rnc b/xml/relaxng/src/resources/html5-schema/html5/common.rnc index 9f1e65673bd2..09afb1454233 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/common.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/common.rnc @@ -113,9 +113,7 @@ common.attrs = ) common.attrs.basic = - ( ( common.attrs.id - | common.attrs.xml-id - )? # REVISIT assuming only either one is allowed + ( common.attrs.id? & common.attrs.class? & common.attrs.title? & common.attrs.base? @@ -125,10 +123,6 @@ common.attrs.basic = attribute id { common.data.id } - common.attrs.xml-id = - attribute xml:id { - xsd:NCName - } & XMLonly common.attrs.class = attribute class { common.data.tokens @@ -387,6 +381,10 @@ common.attrs.other = common.data.keylabellist = w:keylabellist +## List of Source Sizes + common.data.source.size.list = + w:source-size-list + ## Microdata Properties common.data.microdata-properties = list { w:microdata-property+ } @@ -466,7 +464,7 @@ common.attrs.aria.implicit.article = ( notAllowed ) common.attrs.aria.implicit.banner = ( notAllowed ) common.attrs.aria.implicit.button = ( notAllowed ) common.attrs.aria.implicit.checkbox = ( notAllowed ) -common.attrs.aria.implicit.columnheader = ( notAllowed ) +common.attrs.aria.implicit.column-or-row-header = ( notAllowed ) common.attrs.aria.implicit.combobox = ( notAllowed ) common.attrs.aria.implicit.complementary = ( notAllowed ) common.attrs.aria.implicit.contentinfo = ( notAllowed ) @@ -486,7 +484,6 @@ common.attrs.aria.implicit.option = ( notAllowed ) common.attrs.aria.implicit.progressbar = ( notAllowed ) common.attrs.aria.implicit.radio = ( notAllowed ) common.attrs.aria.implicit.region = ( notAllowed ) -common.attrs.aria.implicit.rowheader = ( notAllowed ) common.attrs.aria.implicit.section = ( notAllowed ) common.attrs.aria.implicit.select = ( notAllowed ) common.attrs.aria.implicit.slider = ( notAllowed ) diff --git a/xml/relaxng/src/resources/html5-schema/html5/core-scripting.rnc b/xml/relaxng/src/resources/html5-schema/html5/core-scripting.rnc index bef6bf107423..eb08c2ed5bb7 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/core-scripting.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/core-scripting.rnc @@ -15,6 +15,7 @@ datatypes w = "http://whattf.org/datatype-draft" ( common.attrs & script.attrs.type? & script.attrs.language? # restricted in Schematron + & embedded.content.attrs.crossorigin? & ( common.attrs.aria.role.presentation | common.attrs.aria.role.menuitem )? @@ -29,6 +30,7 @@ datatypes w = "http://whattf.org/datatype-draft" & script.attrs.type? & script.attrs.charset? & script.attrs.language? # restricted in Schematron + & embedded.content.attrs.crossorigin? & ( common.attrs.aria.role.presentation | common.attrs.aria.role.menuitem )? diff --git a/xml/relaxng/src/resources/html5-schema/html5/embed.rnc b/xml/relaxng/src/resources/html5-schema/html5/embed.rnc index 51a2f0acb7a6..be4204914fa8 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/embed.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/embed.rnc @@ -15,12 +15,15 @@ namespace local = "" img.attrs = ( common.attrs & img.attrs.src + & img.attrs.srcset? + & img.attrs.sizes? & img.attrs.alt? # ARIA: if alt empty, only allowed role value is "presentation"; check in assertions & img.attrs.height? & img.attrs.width? & img.attrs.usemap? & img.attrs.ismap? & img.attrs.border? # obsolete + & embedded.content.attrs.crossorigin? & ( common.attrs.aria.implicit.img | common.attrs.aria )? @@ -29,6 +32,14 @@ namespace local = "" attribute src { common.data.uri.non-empty } + img.attrs.srcset = + attribute srcset { + string + } & v5only + img.attrs.sizes = + attribute sizes { + common.data.source.size.list + } & v5only img.attrs.alt = attribute alt { text @@ -58,6 +69,54 @@ namespace local = "" common.elem.phrasing |= img.elem +## Image with multiple sources: + + picture.elem = + element picture { picture.inner & picture.attrs } + & v5only + picture.attrs = + ( common.attrs ) + picture.inner = + ( ( source.picture.elem* + & common.elem.script-supporting* + ), + ( img.elem + & common.elem.script-supporting* + ) + ) + + common.elem.phrasing |= picture.elem + +## Picture source: + + source.picture.elem = + element source { source.picture.inner & source.picture.attrs } + source.picture.attrs = + ( common.attrs + & source.picture.attrs.media? + & source.picture.attrs.srcset + & source.picture.attrs.sizes? + & source.picture.attrs.type? + ) + source.picture.attrs.media = + attribute media { + common.data.mediaquery + } + source.picture.attrs.srcset = + attribute srcset { + string + } + source.picture.attrs.sizes = + attribute sizes { + common.data.source.size.list + } + source.picture.attrs.type = + attribute type { + common.data.mimetype + } + source.picture.inner = + ( empty ) + ## Plug-ins: embed.elem = @@ -72,6 +131,7 @@ namespace local = "" & ( common.attrs.aria.landmark.application | common.attrs.aria.landmark.document | common.attrs.aria.role.img + | common.attrs.aria.role.presentation )? ) embed.attrs.src = @@ -292,6 +352,7 @@ namespace local = "" & ( common.attrs.aria.landmark.application | common.attrs.aria.landmark.document | common.attrs.aria.role.img + | common.attrs.aria.role.presentation )? ) object.attrs.data = @@ -370,6 +431,7 @@ namespace local = "" & ( common.attrs.aria.landmark.application | common.attrs.aria.landmark.document | common.attrs.aria.role.img + | common.attrs.aria.role.presentation )? ) iframe.attrs.src = @@ -548,7 +610,7 @@ namespace local = "" w:string "allowfullscreen" | w:string "" } & v5only iframe.inner = - ( text ) + ( ( text & HTMLonly ) | empty ) common.elem.phrasing |= iframe.elem @@ -655,3 +717,10 @@ namespace local = "" ( empty ) common.elem.phrasing |= area.elem + +## Attributes Common to Embedded Content + + embedded.content.attrs.crossorigin = + attribute crossorigin { + w:string "anonymous" | w:string "use-credentials" | w:string "" + } & v5only diff --git a/xml/relaxng/src/resources/html5-schema/html5/legacy.rnc b/xml/relaxng/src/resources/html5-schema/html5/legacy.rnc index e64f77068455..3d6f1c1930ef 100644 --- a/xml/relaxng/src/resources/html5-schema/html5/legacy.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/legacy.rnc @@ -812,6 +812,12 @@ datatypes w = "http://whattf.org/datatype-draft" } object.attrs &= object.attrs.border? + table.attrs.border = + attribute border { + string + } + table.attrs &= table.attrs.border? + ## cellpadding attribute table.attrs.cellpadding = diff --git a/xml/relaxng/src/resources/html5-schema/html5/media.rnc b/xml/relaxng/src/resources/html5-schema/html5/media.rnc index 04072ecc1036..28cbb630bb39 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/media.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/media.rnc @@ -14,6 +14,7 @@ datatypes w = "http://whattf.org/datatype-draft" & media.attrs.loop? & media.attrs.mediagroup? & media.attrs.muted? + & embedded.content.attrs.crossorigin? ) media.attrs.autoplay = attribute autoplay { @@ -48,7 +49,6 @@ datatypes w = "http://whattf.org/datatype-draft" ( common.attrs & source.attrs.src & source.attrs.type? - & source.attrs.media? & ( common.attrs.aria.role.presentation | common.attrs.aria.role.menuitem )? @@ -61,10 +61,6 @@ datatypes w = "http://whattf.org/datatype-draft" attribute type { common.data.mimetype } - source.attrs.media = - attribute media { - common.data.mediaquery - } source.inner = ( empty ) diff --git a/xml/relaxng/src/resources/html5-schema/html5/meta.rnc b/xml/relaxng/src/resources/html5-schema/html5/meta.rnc index b5e810a0d46f..898a130ec6ed 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/meta.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/meta.rnc @@ -164,6 +164,7 @@ datatypes w = "http://whattf.org/datatype-draft" & shared-hyperlink.attrs.type? & link.attrs.sizes? # link.attrs.title included in common.attrs + & embedded.content.attrs.crossorigin? & ( common.attrs.aria.role.link | common.attrs.aria.role.presentation | common.attrs.aria.role.menuitem diff --git a/xml/relaxng/src/resources/html5-schema/html5/microdata.rnc b/xml/relaxng/src/resources/html5-schema/html5/microdata.rnc index 076a4ded1dad..91616c846147 100644 --- a/xml/relaxng/src/resources/html5-schema/html5/microdata.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/microdata.rnc @@ -25,7 +25,7 @@ common.attrs.microdata = } common.attrs.microdata.itemtype = attribute itemtype { - common.data.uri.absolute + list { common.data.uri.absolute+ } } common.attrs.microdata.itemid = attribute itemid { @@ -58,6 +58,7 @@ base.attrs &= common.attrs.microdata & shared-hyperlink.attrs.type? & link.attrs.sizes? # link.attrs.title included in common.attrs + & embedded.content.attrs.crossorigin? & ( common.attrs.aria.role.link | common.attrs.aria.role.presentation | common.attrs.aria.role.menuitem diff --git a/xml/relaxng/src/resources/html5-schema/html5/rdfa.rnc b/xml/relaxng/src/resources/html5-schema/html5/rdfa.rnc index b8513d716093..3c849d98a954 100644 --- a/xml/relaxng/src/resources/html5-schema/html5/rdfa.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/rdfa.rnc @@ -1,5 +1,5 @@ nonRDFaLite = empty -# ##################################################################### +# ##################################################################### ## RELAX NG Schema for HTML 5: RDFa 1.1 and RDFa Lite 1.1 # # ##################################################################### @@ -9,12 +9,12 @@ nonRDFaLite = empty common.data.rdfa.safecurie = xsd:string { - pattern = "\[(([\i-[:]][\c-[:]]*)?:)?[^\s]+\]" - minLength = "3" + pattern = "\[(([\i-[:]][\c-[:]]*)?:?)[^\s]*\]" + minLength = "2" } common.data.rdfa.curie = xsd:string { - pattern = "(([\i-[:]][\c-[:]]*)?:)?[^\s]+" + pattern = "(([\i-[:]][\c-[:]]*)?:)[^\s]*" minLength = "1" } common.data.rdfa.term = @@ -182,15 +182,11 @@ link.rdfa.attrs.metadata = & common.attrs.present & common.attrs.other & ( ( common.attrs.rdfa.property - & ( link.attrs.rel - | common.attrs.rdfa.rel - )? + & link.attrs.rel? ) | ( common.attrs.rdfa.property? - & ( link.attrs.rel - | common.attrs.rdfa.rel - ) + & link.attrs.rel ) ) & link.attrs.href @@ -208,6 +204,11 @@ link.rdfa.attrs.metadata = & shared-hyperlink.attrs.type? & link.attrs.sizes? # link.attrs.title included in common.attrs + & embedded.content.attrs.crossorigin? + & ( common.attrs.aria.role.link + | common.attrs.aria.role.presentation + | common.attrs.aria.role.menuitem + )? ) link.rdfa.attrs.phrasing = ( common.attrs.basic @@ -215,9 +216,7 @@ link.rdfa.attrs.phrasing = & common.attrs.present & common.attrs.other & common.attrs.rdfa.property - & ( link.attrs.rel - | common.attrs.rdfa.rel - )? + & link.attrs.rel? & ( ( common.attrs.rdfa.resource & link.attrs.href? ) @@ -239,6 +238,11 @@ link.rdfa.attrs.phrasing = & shared-hyperlink.attrs.type? & link.attrs.sizes? # link.attrs.title included in common.attrs + & embedded.content.attrs.crossorigin? + & ( common.attrs.aria.role.link + | common.attrs.aria.role.presentation + | common.attrs.aria.role.menuitem + )? ) common.elem.metadata |= link.rdfa.elem.metadata common.elem.phrasing |= link.rdfa.elem.phrasing diff --git a/xml/relaxng/src/resources/html5-schema/html5/tables.rnc b/xml/relaxng/src/resources/html5-schema/html5/tables.rnc index 2aecbcfc7a4e..b6021dac22dd 100755 --- a/xml/relaxng/src/resources/html5-schema/html5/tables.rnc +++ b/xml/relaxng/src/resources/html5-schema/html5/tables.rnc @@ -50,13 +50,8 @@ datatypes w = "http://whattf.org/datatype-draft" element table { table.inner & table.attrs } table.attrs = ( common.attrs - & table.attrs.border? & common.attrs.aria? ) - table.attrs.border = - attribute border { - string - } table.inner = ( caption.elem? , common.elem.script-supporting* @@ -84,9 +79,7 @@ datatypes w = "http://whattf.org/datatype-draft" element caption { caption.inner & caption.attrs } caption.attrs = ( common.attrs - & ( common.attrs.aria.role.presentation - | common.attrs.aria.role.menuitem - )? + & common.attrs.aria? ) caption.inner = ( common.inner.flow ) @@ -100,9 +93,7 @@ datatypes w = "http://whattf.org/datatype-draft" element colgroup { colgroup.inner & colgroup.attrs } colgroup.attrs = ( common.attrs - & ( common.attrs.aria.role.presentation - | common.attrs.aria.role.menuitem - )? + & common.attrs.aria? ) colgroup.attrs.span = attribute span { @@ -122,9 +113,7 @@ datatypes w = "http://whattf.org/datatype-draft" col.attrs = ( common.attrs & col.attrs.span? - & ( common.attrs.aria.role.presentation - | common.attrs.aria.role.menuitem - )? + & common.attrs.aria? ) col.attrs.span = attribute span { @@ -139,9 +128,7 @@ datatypes w = "http://whattf.org/datatype-draft" element thead { thead.inner & thead.attrs } thead.attrs = ( common.attrs - & ( common.attrs.aria.role.presentation - | common.attrs.aria.role.menuitem - )? + & common.attrs.aria? ) thead.inner = ( tr.elem* @@ -154,9 +141,7 @@ datatypes w = "http://whattf.org/datatype-draft" element tfoot { tfoot.inner & tfoot.attrs } tfoot.attrs = ( common.attrs - & ( common.attrs.aria.role.presentation - | common.attrs.aria.role.menuitem - )? + & common.attrs.aria? ) tfoot.inner = ( tr.elem* @@ -169,9 +154,7 @@ datatypes w = "http://whattf.org/datatype-draft" element tbody { tbody.inner & tbody.attrs } tbody.attrs = ( common.attrs - & ( common.attrs.aria.role.presentation - | common.attrs.aria.role.menuitem - )? + & common.attrs.aria? ) tbody.inner = ( tr.elem* @@ -253,7 +236,9 @@ datatypes w = "http://whattf.org/datatype-draft" & tables.attrs.scope? & tables.attrs.headers? # & tables.attrs.alignment - & common.attrs.aria? + & ( common.attrs.aria? + | common.attrs.aria.implicit.column-or-row-header + ) ) th.inner = ( common.inner.flow ) diff --git a/xml/relaxng/src/resources/html5-schema/legacy/legacy.rnc b/xml/relaxng/src/resources/html5-schema/legacy/legacy.rnc index e64f77068455..3d6f1c1930ef 100644 --- a/xml/relaxng/src/resources/html5-schema/legacy/legacy.rnc +++ b/xml/relaxng/src/resources/html5-schema/legacy/legacy.rnc @@ -812,6 +812,12 @@ datatypes w = "http://whattf.org/datatype-draft" } object.attrs &= object.attrs.border? + table.attrs.border = + attribute border { + string + } + table.attrs &= table.attrs.border? + ## cellpadding attribute table.attrs.cellpadding = diff --git a/xml/relaxng/src/resources/html5-schema/svg11/svg-basic-font.rnc b/xml/relaxng/src/resources/html5-schema/svg11/svg-basic-font.rnc index 32eb1460f564..94f5b7fa3121 100644 --- a/xml/relaxng/src/resources/html5-schema/svg11/svg-basic-font.rnc +++ b/xml/relaxng/src/resources/html5-schema/svg11/svg-basic-font.rnc @@ -97,7 +97,7 @@ grammar { attribute overline-thickness { Number.datatype }? a:documentation [ "\x{a}" ~ " glyph: Glyph Element\x{a}" ~ " " ] SVG.glyph.class = notAllowed - SVG.glyph.content = SVG.Description.class*, SVG.glyph.class* + SVG.glyph.content = SVG.Description.class* | SVG.glyph.class* glyph = element glyph { attlist.glyph, SVG.glyph.content } attlist.glyph &= SVG.Core.attrib, @@ -120,7 +120,7 @@ grammar { ] SVG.missing-glyph.class = notAllowed SVG.missing-glyph.content = - SVG.Description.class*, SVG.missing-glyph.class* + SVG.Description.class* | SVG.missing-glyph.class* missing-glyph = element missing-glyph { attlist.missing-glyph, SVG.missing-glyph.content diff --git a/xml/relaxng/src/resources/html5-schema/svg11/svg-conditional.rnc b/xml/relaxng/src/resources/html5-schema/svg11/svg-conditional.rnc index 22f3cc23b1d9..ecb3e154aded 100644 --- a/xml/relaxng/src/resources/html5-schema/svg11/svg-conditional.rnc +++ b/xml/relaxng/src/resources/html5-schema/svg11/svg-conditional.rnc @@ -62,8 +62,7 @@ grammar { | SVG.Conditional.class | SVG.Image.class | SVG.Shape.class - | SVG.Hyperlink.class - | SVG.Extensibility.class)* + | SVG.Hyperlink.class)* switch = element switch { attlist.switch, SVG.switch.content } attlist.switch &= SVG.Core.attrib, diff --git a/xml/relaxng/src/resources/html5-schema/svg11/svg-extensibility.rnc b/xml/relaxng/src/resources/html5-schema/svg11/svg-extensibility.rnc index 2f075b4c2cec..ba3316ad160b 100644 --- a/xml/relaxng/src/resources/html5-schema/svg11/svg-extensibility.rnc +++ b/xml/relaxng/src/resources/html5-schema/svg11/svg-extensibility.rnc @@ -57,3 +57,14 @@ foreignElement = | text | foreignElement)* } +SVG.a.content &= SVG.Extensibility.class* +SVG.defs.content &= SVG.Extensibility.class* +SVG.glyph.content &= SVG.Extensibility.class* +SVG.g.content &= SVG.Extensibility.class* +SVG.marker.content &= SVG.Extensibility.class* +SVG.mask.content &= SVG.Extensibility.class* +SVG.missing-glyph.content &= SVG.Extensibility.class* +SVG.pattern.content &= SVG.Extensibility.class* +SVG.svg.content &= SVG.Extensibility.class* +SVG.switch.content &= SVG.Extensibility.class* +SVG.symbol.content &= SVG.Extensibility.class* diff --git a/xml/relaxng/src/resources/patches/0004_ping.patch b/xml/relaxng/src/resources/patches/0004_ping.patch index 6f825ac4a2e0..4e47a934c7d6 100644 --- a/xml/relaxng/src/resources/patches/0004_ping.patch +++ b/xml/relaxng/src/resources/patches/0004_ping.patch @@ -7,7 +7,7 @@ - shared-hyperlink.attrs.ping = - attribute ping { - common.data.uris -- } & v5only & nonW3C +- } & v5only ## Emphatic Stress: diff --git a/xml/relaxng/src/resources/patches/patch_build.patch b/xml/relaxng/src/resources/patches/patch_build.patch index ba4c30769bd0..88e4148c2420 100644 --- a/xml/relaxng/src/resources/patches/patch_build.patch +++ b/xml/relaxng/src/resources/patches/patch_build.patch @@ -1,7 +1,8 @@ -diff -r dd84d714a0da build.py ---- a/build.py Fri Oct 25 13:00:39 2013 +0900 -+++ b/build.py Fri Jan 24 13:53:06 2014 +0400 -@@ -147,8 +147,8 @@ +diff --git a/build.py b/build.py +index ac7ecb3..db7de9c 100755 +--- a/build.py ++++ b/build.py +@@ -153,13 +153,13 @@ dependencyJars = runDependencyJars + buildOnlyDependencyJars moduleNames = [ "syntax", @@ -10,9 +11,18 @@ diff -r dd84d714a0da build.py + # "util", + # "xmlparser", "validator", +- "jing-trang", +- "htmlparser", +- "nu-validator-site", +- "tests", ++ # "jing-trang", ++ # "htmlparser", ++ # "nu-validator-site", ++ # "tests", ] -@@ -875,19 +875,19 @@ + javaSafeNamePat = re.compile(r'[^a-zA-Z0-9]') +@@ -926,19 +926,19 @@ def downloadDependencies(): downloadDependency(url, md5sum) def buildAll(): @@ -42,36 +52,5 @@ diff -r dd84d714a0da build.py + # buildXmlParser() + # buildValidator() - def hgCloneOrUpdate(mod, baseUrl): + def gitCloneOrUpdate(mod, baseUrl): if os.path.exists(mod): -@@ -925,18 +925,18 @@ - # XXX root dir - for mod in moduleNames: - hgCloneOrUpdate(mod, hgRoot) -- gitCloneOrUpdate("nu-validator-site", gitRoot) -- runCmd('"%s" co http://jing-trang.googlecode.com/svn/branches/validator-nu jing-trang' % (svnCmd)) -- hgCloneOrUpdate("htmlparser", parserHgRoot) -- testsRemote = "https://github.com/validator/tests.git" -- testsBranch = "master" -- testsDir = "tests" -- if os.path.exists(testsDir): -- os.chdir(testsDir) -- runCmd('"%s" pull %s %s' % (gitCmd, testsRemote, testsBranch)) -- os.chdir("..") -- else: -- runCmd('"%s" clone %s %s' % (gitCmd, testsRemote, testsDir)) -+ # gitCloneOrUpdate("nu-validator-site", gitRoot) -+ # runCmd('"%s" co http://jing-trang.googlecode.com/svn/branches/validator-nu jing-trang' % (svnCmd)) -+ # hgCloneOrUpdate("htmlparser", parserHgRoot) -+ # testsRemote = "https://github.com/validator/tests.git" -+ # testsBranch = "master" -+ # testsDir = "tests" -+ # if os.path.exists(testsDir): -+ # os.chdir(testsDir) -+ # runCmd('"%s" pull %s %s' % (gitCmd, testsRemote, testsBranch)) -+ # os.chdir("..") -+ # else: -+ # runCmd('"%s" clone %s %s' % (gitCmd, testsRemote, testsDir)) - - def selfUpdate(): - hgCloneOrUpdate("build", hgRoot) diff --git a/xml/relaxng/src/resources/update_html5_schema.sh b/xml/relaxng/src/resources/update_html5_schema.sh index 299129868253..20d28e53762e 100755 --- a/xml/relaxng/src/resources/update_html5_schema.sh +++ b/xml/relaxng/src/resources/update_html5_schema.sh @@ -11,9 +11,9 @@ mkdir temp cd temp echo ">>>>> Preparing validator build" -hg clone https://bitbucket.org/validator/build build +git clone https://github.com/validator/build build cd build -hg import "$PATCHES/patch_build.patch" --no-commit +git apply "$PATCHES/patch_build.patch" cd .. echo