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
\
-
-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: