Merge remote-tracking branch 'origin/master'

This commit is contained in:
Konstantin Bulenkov
2014-09-10 14:25:40 +02:00
26 changed files with 590 additions and 402 deletions
@@ -885,13 +885,13 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel,
getTools(toolId, project).removeScope(scopeIdx);
}
public void removeScope(@NotNull String toolId, @NotNull NamedScope scope, Project project) {
getTools(toolId, project).removeScope(scope);
public void removeScope(@NotNull String toolId, @NotNull String scopeName, Project project) {
getTools(toolId, project).removeScope(scopeName);
}
public void removeScopes(@NotNull List<String> toolIds, @NotNull NamedScope scope, Project project) {
public void removeScopes(@NotNull List<String> toolIds, @NotNull String scopeName, Project project) {
for (final String toolId : toolIds) {
removeScope(toolId, scope, project);
removeScope(toolId, scopeName, project);
}
}
@@ -267,25 +267,26 @@ public class ToolsImpl implements Tools {
public void removeScope(int scopeIdx) {
if (myTools != null && scopeIdx >= 0 && myTools.size() > scopeIdx) {
myTools.remove(scopeIdx);
if (myTools.isEmpty()) {
myTools = null;
setEnabled(myDefaultState.isEnabled());
}
checkToolsIsEmpty();
}
}
public void removeScope(final NamedScope scope) {
public void removeScope(final @NotNull String scopeName) {
if (myTools != null) {
for (final ScopeToolState tool : myTools) {
if (Comparing.equal(tool.getScopeName(), scope.getName())) {
for (ScopeToolState tool : myTools) {
if (scopeName.equals(tool.getScopeName())) {
myTools.remove(tool);
break;
}
}
if (myTools.isEmpty()) {
myTools = null;
setEnabled(myDefaultState.isEnabled());
}
checkToolsIsEmpty();
}
}
private void checkToolsIsEmpty() {
if (myTools.isEmpty()) {
myTools = null;
setEnabled(myDefaultState.isEnabled());
}
}
@@ -0,0 +1,133 @@
/*
* 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.lang.customFolding;
import com.intellij.ide.IdeBundle;
import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.psi.PsiElement;
import com.intellij.ui.components.JBList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
/**
* @author Rustam Vishnyakov
*/
public class CustomFoldingRegionsPopup {
private final @NotNull JBList myRegionsList;
private final @NotNull JBPopup myPopup;
private final @NotNull Editor myEditor;
CustomFoldingRegionsPopup(@NotNull Collection<FoldingDescriptor> descriptors,
@NotNull final Editor editor,
@NotNull final Project project) {
myEditor = editor;
myRegionsList = new JBList();
//noinspection unchecked
myRegionsList.setModel(new MyListModel(orderByPosition(descriptors)));
myRegionsList.setSelectedIndex(0);
final PopupChooserBuilder popupBuilder = JBPopupFactory.getInstance().createListPopupBuilder(myRegionsList);
myPopup = popupBuilder
.setTitle(IdeBundle.message("goto.custom.region.command"))
.setResizable(false)
.setMovable(false)
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
PsiElement navigationElement = getNavigationElement();
if (navigationElement != null) {
navigateTo(editor, navigationElement);
IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation();
}
}
}).createPopup();
}
void show() {
myPopup.showInBestPositionFor(myEditor);
}
private static class MyListModel extends DefaultListModel {
private MyListModel(Collection<FoldingDescriptor> descriptors) {
for (FoldingDescriptor descriptor : descriptors) {
//noinspection unchecked
super.addElement(new MyFoldingDescriptorWrapper(descriptor));
}
}
}
private static class MyFoldingDescriptorWrapper {
private final @NotNull FoldingDescriptor myDescriptor;
private MyFoldingDescriptorWrapper(@NotNull FoldingDescriptor descriptor) {
myDescriptor = descriptor;
}
@NotNull
public FoldingDescriptor getDescriptor() {
return myDescriptor;
}
@Nullable
@Override
public String toString() {
return myDescriptor.getPlaceholderText();
}
}
@Nullable
public PsiElement getNavigationElement() {
Object selection = myRegionsList.getSelectedValue();
if (selection instanceof MyFoldingDescriptorWrapper) {
return ((MyFoldingDescriptorWrapper)selection).getDescriptor().getElement().getPsi();
}
return null;
}
private static Collection<FoldingDescriptor> orderByPosition(Collection<FoldingDescriptor> descriptors) {
List<FoldingDescriptor> sorted = new ArrayList<FoldingDescriptor>(descriptors.size());
sorted.addAll(descriptors);
Collections.sort(sorted, new Comparator<FoldingDescriptor>() {
@Override
public int compare(FoldingDescriptor descriptor1, FoldingDescriptor descriptor2) {
int pos1 = descriptor1.getElement().getTextRange().getStartOffset();
int pos2 = descriptor2.getElement().getTextRange().getStartOffset();
return pos1 - pos2;
}
});
return sorted;
}
private static void navigateTo(@NotNull Editor editor, @NotNull PsiElement element) {
int offset = element.getTextRange().getStartOffset();
if (offset >= 0 && offset < editor.getDocument().getTextLength()) {
editor.getCaretModel().removeSecondaryCarets();
editor.getCaretModel().moveToOffset(offset);
editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
editor.getSelectionModel().removeSelection();
}
}
}
@@ -16,26 +16,35 @@
package com.intellij.lang.customFolding;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.lang.Language;
import com.intellij.lang.folding.*;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Disposer;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Set;
/**
* @author Rustam Vishnyakov
*/
public class GotoCustomRegionAction extends AnAction implements DumbAware {
public class GotoCustomRegionAction extends AnAction implements DumbAware, PopupAction {
@Override
public void actionPerformed(AnActionEvent e) {
public void actionPerformed(final AnActionEvent e) {
final Project project = e.getProject();
final Editor editor = e.getData(CommonDataKeys.EDITOR);
if (Boolean.TRUE.equals(e.getData(PlatformDataKeys.IS_MODAL_CONTEXT))) {
@@ -52,14 +61,13 @@ public class GotoCustomRegionAction extends AnAction implements DumbAware {
new Runnable() {
@Override
public void run() {
GotoCustomRegionDialog dialog = new GotoCustomRegionDialog(project, editor);
dialog.show();
if (dialog.isOK()) {
PsiElement navigationElement = dialog.getNavigationElement();
if (navigationElement != null) {
navigateTo(editor, navigationElement);
IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation();
}
Collection<FoldingDescriptor> foldingDescriptors = getCustomFoldingDescriptors(editor, project);
if (foldingDescriptors.size() > 0) {
CustomFoldingRegionsPopup regionsPopup = new CustomFoldingRegionsPopup(foldingDescriptors, editor, project);
regionsPopup.show();
}
else {
notifyCustomRegionsUnavailable(editor, project);
}
}
},
@@ -71,7 +79,7 @@ public class GotoCustomRegionAction extends AnAction implements DumbAware {
@Override
public void update(AnActionEvent e) {
Presentation presentation = e.getPresentation();
presentation.setText("Custom Region...");
presentation.setText(IdeBundle.message("goto.custom.region.menu.item"));
final Editor editor = e.getData(CommonDataKeys.EDITOR);
final Project project = e.getProject();
boolean isAvailable = editor != null && project != null;
@@ -79,13 +87,49 @@ public class GotoCustomRegionAction extends AnAction implements DumbAware {
presentation.setVisible(isAvailable);
}
private static void navigateTo(Editor editor, PsiElement element) {
int offset = element.getTextRange().getStartOffset();
if (offset >= 0 && offset < editor.getDocument().getTextLength()) {
editor.getCaretModel().removeSecondaryCarets();
editor.getCaretModel().moveToOffset(offset);
editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
editor.getSelectionModel().removeSelection();
@NotNull
private static Collection<FoldingDescriptor> getCustomFoldingDescriptors(@NotNull Editor editor, @NotNull Project project) {
Set<FoldingDescriptor> foldingDescriptors = new HashSet<FoldingDescriptor>();
final Document document = editor.getDocument();
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
PsiFile file = documentManager != null ? documentManager.getPsiFile(document) : null;
if (file != null) {
final FileViewProvider viewProvider = file.getViewProvider();
for (final Language language : viewProvider.getLanguages()) {
final PsiFile psi = viewProvider.getPsi(language);
final FoldingBuilder foldingBuilder = LanguageFolding.INSTANCE.forLanguage(language);
if (psi != null) {
for (FoldingDescriptor descriptor : LanguageFolding.buildFoldingDescriptors(foldingBuilder, psi, document, false)) {
CustomFoldingBuilder customFoldingBuilder = getCustomFoldingBuilder(foldingBuilder, descriptor);
if (customFoldingBuilder != null) {
if (customFoldingBuilder.isCustomRegionStart(descriptor.getElement())) {
foldingDescriptors.add(descriptor);
}
}
}
}
}
}
return foldingDescriptors;
}
@Nullable
private static CustomFoldingBuilder getCustomFoldingBuilder(FoldingBuilder builder, FoldingDescriptor descriptor) {
if (builder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)builder;
FoldingBuilder originalBuilder = descriptor.getElement().getUserData(CompositeFoldingBuilder.FOLDING_BUILDER);
if (originalBuilder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)originalBuilder;
return null;
}
private static void notifyCustomRegionsUnavailable(@NotNull Editor editor, @NotNull Project project) {
final JBPopupFactory popupFactory = JBPopupFactory.getInstance();
Balloon balloon = popupFactory
.createHtmlTextBalloonBuilder(IdeBundle.message("goto.custom.region.message.unavailable"), MessageType.INFO, null)
.setFadeoutTime(2000)
.setHideOnClickOutside(true)
.setHideOnKeyOutside(true)
.createBalloon();
Disposer.register(project, balloon);
balloon.show(popupFactory.guessBestPopupLocation(editor), Balloon.Position.above);
}
}
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.lang.customFolding.GotoCustomRegionDialog">
<grid id="27dc6" binding="myContentPane" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<scrollpane id="7557d" class="com.intellij.ui.components.JBScrollPane" binding="myScrollPane" custom-create="true">
<constraints border-constraint="Center"/>
<properties/>
<border type="none"/>
<children>
<component id="2f5d5" class="com.intellij.ui.components.JBList" binding="myRegionsList" custom-create="true">
<constraints/>
<properties>
<selectionMode value="0"/>
</properties>
</component>
</children>
</scrollpane>
</children>
</grid>
</form>
@@ -1,169 +0,0 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.lang.customFolding;
import com.intellij.ide.IdeBundle;
import com.intellij.lang.Language;
import com.intellij.lang.folding.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* @author Rustam Vishnyakov
*/
public class GotoCustomRegionDialog extends DialogWrapper {
private JBList myRegionsList;
private JPanel myContentPane;
private JBScrollPane myScrollPane;
private final Editor myEditor;
private final Project myProject;
protected GotoCustomRegionDialog(@Nullable Project project, @NotNull Editor editor) {
super(project);
myEditor = editor;
myProject = project;
Collection<FoldingDescriptor> descriptors = getCustomFoldingDescriptors();
init();
if (descriptors.size() == 0) {
myScrollPane.setVisible(false);
myContentPane.add(new JLabel(IdeBundle.message("goto.custom.region.message.unavailable")), BorderLayout.NORTH);
setOKActionEnabled(false);
}
else {
myRegionsList.setModel(new MyListModel(orderByPosition(descriptors)));
myRegionsList.setSelectedIndex(0);
}
setTitle(IdeBundle.message("goto.custom.region.command"));
}
@Override
public JComponent getPreferredFocusedComponent() {
if (!myRegionsList.isEmpty()) {
return myRegionsList;
}
return super.getPreferredFocusedComponent();
}
@Override
protected JComponent createCenterPanel() {
return myContentPane;
}
private Collection<FoldingDescriptor> getCustomFoldingDescriptors() {
Set<FoldingDescriptor> foldingDescriptors = new HashSet<FoldingDescriptor>();
final Document document = myEditor.getDocument();
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
PsiFile file = documentManager != null ? documentManager.getPsiFile(document) : null;
if (file != null) {
final FileViewProvider viewProvider = file.getViewProvider();
for (final Language language : viewProvider.getLanguages()) {
final PsiFile psi = viewProvider.getPsi(language);
final FoldingBuilder foldingBuilder = LanguageFolding.INSTANCE.forLanguage(language);
if (psi != null) {
for (FoldingDescriptor descriptor : LanguageFolding.buildFoldingDescriptors(foldingBuilder, psi, document, false)) {
CustomFoldingBuilder customFoldingBuilder = getCustomFoldingBuilder(foldingBuilder, descriptor);
if (customFoldingBuilder != null) {
if (customFoldingBuilder.isCustomRegionStart(descriptor.getElement())) {
foldingDescriptors.add(descriptor);
}
}
}
}
}
}
return foldingDescriptors;
}
private static Collection<FoldingDescriptor> orderByPosition(Collection<FoldingDescriptor> descriptors) {
List<FoldingDescriptor> sorted = new ArrayList<FoldingDescriptor>(descriptors.size());
sorted.addAll(descriptors);
Collections.sort(sorted, new Comparator<FoldingDescriptor>() {
@Override
public int compare(FoldingDescriptor descriptor1, FoldingDescriptor descriptor2) {
int pos1 = descriptor1.getElement().getTextRange().getStartOffset();
int pos2 = descriptor2.getElement().getTextRange().getStartOffset();
return pos1 - pos2;
}
});
return sorted;
}
private void createUIComponents() {
myRegionsList = new JBList();
myScrollPane = new JBScrollPane(myRegionsList);
}
@Nullable
private static CustomFoldingBuilder getCustomFoldingBuilder(FoldingBuilder builder, FoldingDescriptor descriptor) {
if (builder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)builder;
FoldingBuilder originalBuilder = descriptor.getElement().getUserData(CompositeFoldingBuilder.FOLDING_BUILDER);
if (originalBuilder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)originalBuilder;
return null;
}
private static class MyListModel extends DefaultListModel {
private MyListModel(Collection<FoldingDescriptor> descriptors) {
for (FoldingDescriptor descriptor : descriptors) {
super.addElement(new MyFoldingDescriptorWrapper(descriptor));
}
}
}
private static class MyFoldingDescriptorWrapper {
private final @NotNull FoldingDescriptor myDescriptor;
private MyFoldingDescriptorWrapper(@NotNull FoldingDescriptor descriptor) {
myDescriptor = descriptor;
}
@NotNull
public FoldingDescriptor getDescriptor() {
return myDescriptor;
}
@Nullable
@Override
public String toString() {
return myDescriptor.getPlaceholderText();
}
}
@Nullable
public PsiElement getNavigationElement() {
Object selection = myRegionsList.getSelectedValue();
if (selection instanceof MyFoldingDescriptorWrapper) {
return ((MyFoldingDescriptorWrapper)selection).getDescriptor().getElement().getPsi();
}
return null;
}
}
@@ -357,6 +357,9 @@ public class ScopesAndSeveritiesTable extends JBTable {
}
else if (columnIndex == SCOPE_ENABLED_COLUMN) {
final NamedScope scope = getScope(rowIndex);
if (scope == null) {
return;
}
if ((Boolean)value) {
if (rowIndex == lastRowIndex()) {
myInspectionProfile.enableToolsByDefault(myKeyNames, myProject);
@@ -381,7 +384,7 @@ public class ScopesAndSeveritiesTable extends JBTable {
@Override
public void removeRow(final int idx) {
if (idx != lastRowIndex()) {
myInspectionProfile.removeScopes(myKeyNames, getScope(idx), myProject);
myInspectionProfile.removeScopes(myKeyNames, getScopeName(idx), myProject);
refreshAggregatedScopes();
myTableSettings.onScopeRemoved(getRowCount());
}
@@ -1142,8 +1142,9 @@ whatsnew.action.custom.text=What''s _New in {0}
whatsnew.action.custom.description=Find out about the new features in this version of {0}
diff.dialog.title=Diff Between ''{0}'' and ''{1}''
goto.custom.region.command=Go to Custom Region
goto.custom.region.message.dumb.mode=Go to Custom Region action is not available until indices are built.
goto.custom.region.menu.item=Custom Folding Region...
goto.custom.region.command=Go to Custom Folding Region
goto.custom.region.message.dumb.mode=Go to Custom Folding Region action is not available until indices are built.
goto.custom.region.message.unavailable=There are no custom folding regions in the current file.
alphabetical.mode.is.on.warning=Alphabetical order for tabs is ON. Switch it OFF?
@@ -32,6 +32,7 @@ public class MockVirtualFileSystem extends DeprecatedVirtualFileSystem {
public static final String PROTOCOL = "mock";
@Override
@NotNull
public VirtualFile findFileByPath(@NotNull String path) {
path = path.replace(File.separatorChar, '/');
path = path.replace('/', ':');
@@ -106,6 +107,7 @@ public class MockVirtualFileSystem extends DeprecatedVirtualFileSystem {
return MockVirtualFileSystem.this;
}
@NotNull
public MyVirtualFile getOrCreate(String name) {
MyVirtualFile file = myChildren.get(name);
if (file == null) {
@@ -37,6 +37,7 @@ public abstract class IdeaTestFixtureFactory {
}
}
@NotNull
public static IdeaTestFixtureFactory getFixtureFactory() {
return ourInstance;
}
@@ -59,6 +60,7 @@ public abstract class IdeaTestFixtureFactory {
public abstract TestFixtureBuilder<IdeaProjectTestFixture> createFixtureBuilder(@NotNull String name);
@NotNull
public abstract TestFixtureBuilder<IdeaProjectTestFixture> createLightFixtureBuilder();
public abstract TestFixtureBuilder<IdeaProjectTestFixture> createLightFixtureBuilder(@Nullable LightProjectDescriptor projectDescriptor);
@@ -17,11 +17,13 @@
package com.intellij.testFramework.fixtures;
import com.intellij.testFramework.builders.ModuleFixtureBuilder;
import org.jetbrains.annotations.NotNull;
/**
* @author mike
*/
public interface TestFixtureBuilder<T extends IdeaTestFixture> {
@NotNull
T getFixture();
<M extends ModuleFixtureBuilder> M addModule(Class<M> builderClass);
@@ -22,6 +22,7 @@ import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
import com.intellij.testFramework.fixtures.TestFixtureBuilder;
import com.intellij.util.pico.ConstructorInjectionComponentAdapter;
import com.intellij.util.pico.IdeaPicoContainer;
import org.jetbrains.annotations.NotNull;
import org.picocontainer.MutablePicoContainer;
import java.lang.reflect.Field;
@@ -50,6 +51,7 @@ class HeavyTestFixtureBuilderImpl implements TestFixtureBuilder<IdeaProjectTestF
return (M)adapter.getComponentInstance(myContainer);
}
@NotNull
@Override
public HeavyIdeaTestFixture getFixture() {
return myFixture;
@@ -62,6 +62,7 @@ public class IdeaTestFixtureFactoryImpl extends IdeaTestFixtureFactory {
return new HeavyTestFixtureBuilderImpl(new HeavyIdeaTestFixtureImpl(name), myFixtureBuilderProviders);
}
@NotNull
@Override
public TestFixtureBuilder<IdeaProjectTestFixture> createLightFixtureBuilder() {
return new LightTestFixtureBuilderImpl<IdeaProjectTestFixture>(new LightIdeaTestFixtureImpl(
@@ -19,6 +19,7 @@ package com.intellij.testFramework.fixtures.impl;
import com.intellij.testFramework.builders.ModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
import com.intellij.testFramework.fixtures.TestFixtureBuilder;
import org.jetbrains.annotations.NotNull;
/**
* @author mike
@@ -31,6 +32,7 @@ class LightTestFixtureBuilderImpl<F extends IdeaProjectTestFixture> implements T
myFixture = fixture;
}
@NotNull
@Override
public F getFixture() {
return myFixture;
@@ -39,7 +39,7 @@ public class WatchInplaceEditor extends XDebuggerTreeInplaceEditor {
@Nullable private final WatchNode myOldNode;
public WatchInplaceEditor(@NotNull WatchesRootNode rootNode,
@NotNull XDebugSession session, XWatchesView watchesView, final WatchNode node,
@Nullable XDebugSession session, XWatchesView watchesView, final WatchNode node,
@NonNls final String historyId,
final @Nullable WatchNode oldNode) {
super((XDebuggerTreeNode)node, historyId);
@@ -47,7 +47,9 @@ public class WatchInplaceEditor extends XDebuggerTreeInplaceEditor {
myWatchesView = watchesView;
myOldNode = oldNode;
myExpressionEditor.setExpression(oldNode != null ? oldNode.getExpression() : null);
new WatchEditorSessionListener(session).install();
if (session != null) {
new WatchEditorSessionListener(session).install();
}
}
@Override
@@ -19,6 +19,7 @@ import com.intellij.execution.ui.layout.ViewContext;
import com.intellij.ide.DataManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.ui.content.ContentManager;
import com.intellij.util.SingleAlarm;
import com.intellij.xdebugger.XDebugSession;
@@ -66,15 +67,20 @@ public abstract class XDebugView implements Disposable {
@Nullable
public static XDebugSession getSession(@NotNull Component component) {
return getData(XDebugSession.DATA_KEY, component);
}
@Nullable
public static <T> T getData(DataKey<T> key, @NotNull Component component) {
DataContext dataContext = DataManager.getInstance().getDataContext(component);
ViewContext viewContext = ViewContext.CONTEXT_KEY.getData(dataContext);
ContentManager contentManager = viewContext == null ? null : viewContext.getContentManager();
if (contentManager != null) {
XDebugSession session = XDebugSession.DATA_KEY.getData(DataManager.getInstance().getDataContext(contentManager.getComponent()));
if (session != null) {
return session;
T data = key.getData(DataManager.getInstance().getDataContext(contentManager.getComponent()));
if (data != null) {
return data;
}
}
return XDebugSession.DATA_KEY.getData(dataContext);
return key.getData(dataContext);
}
}
@@ -40,6 +40,7 @@ import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.impl.XDebugSessionImpl;
import com.intellij.xdebugger.impl.actions.XDebuggerActions;
import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl;
import com.intellij.xdebugger.impl.ui.XDebugSessionData;
import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreePanel;
@@ -220,13 +221,9 @@ public class XWatchesViewImpl extends XDebugView implements DnDNativeTarget, XWa
@Override
public void addWatchExpression(@NotNull XExpression expression, int index, final boolean navigateToWatchNode) {
XDebugSession session = getSession(getTree());
if (session == null) {
return;
}
myRootNode.addWatchExpression(session.getDebugProcess().getEvaluator(), expression, index, navigateToWatchNode);
myRootNode.addWatchExpression(session != null ? session.getDebugProcess().getEvaluator() : null, expression, index, navigateToWatchNode);
updateSessionData();
if (navigateToWatchNode) {
if (navigateToWatchNode && session != null) {
showWatchesTab((XDebugSessionImpl)session);
}
}
@@ -342,8 +339,15 @@ public class XWatchesViewImpl extends XDebugView implements DnDNativeTarget, XWa
}
XDebugSession session = getSession(getTree());
XExpression[] expressions = watchExpressions.toArray(new XExpression[watchExpressions.size()]);
if (session != null) {
((XDebugSessionImpl)session).setWatchExpressions(watchExpressions.toArray(new XExpression[watchExpressions.size()]));
((XDebugSessionImpl)session).setWatchExpressions(expressions);
}
else {
XDebugSessionData data = getData(XDebugSessionData.DATA_KEY, getTree());
if (data != null) {
data.setWatchExpressions(expressions);
}
}
}
@@ -188,9 +188,7 @@ public class WatchesRootNode extends XDebuggerTreeNode {
fireNodeStructureChanged(messageNode);
}
XDebugSession session = XDebugView.getSession(myTree);
if (session != null) {
new WatchInplaceEditor(this, session, myWatchesView, messageNode, "watch", node).show();
}
new WatchInplaceEditor(this, session, myWatchesView, messageNode, "watch", node).show();
}
private class MyEvaluationCallback extends XEvaluationCallbackBase {
@@ -480,49 +480,44 @@ public abstract class TestObject implements JavaCommandLine {
return StringUtil.compare(o1.getName(), o2.getName(), true);
}
}) : null;
final PrintWriter writer = new PrintWriter(myTempFile, CharsetToolkit.UTF8);
try {
writer.println(packageName);
final JUnitConfiguration.Data data = myConfiguration.getPersistentData();
final String category = data.TEST_OBJECT == JUnitConfiguration.TEST_CATEGORY ? data.getCategory() : "";
writer.println(category);
final List<String> testNames = new ArrayList<String>();
for (final T element : elements) {
final String name = nameFunction.fun(element);
if (name == null) {
LOG.error("invalid element " + element);
return;
}
if (perModule != null && element instanceof PsiElement) {
final Module module = ModuleUtilCore.findModuleForPsiElement((PsiElement)element);
if (module != null) {
List<String> list = perModule.get(module);
if (list == null) {
list = new ArrayList<String>();
perModule.put(module, list);
}
list.add(name);
final List<String> testNames = new ArrayList<String>();
for (final T element : elements) {
final String name = nameFunction.fun(element);
if (name == null) {
LOG.error("invalid element " + element);
return;
}
if (perModule != null && element instanceof PsiElement) {
final Module module = ModuleUtilCore.findModuleForPsiElement((PsiElement)element);
if (module != null) {
List<String> list = perModule.get(module);
if (list == null) {
list = new ArrayList<String>();
perModule.put(module, list);
}
} else {
testNames.add(name);
list.add(name);
}
}
if (perModule != null) {
for (List<String> perModuleClasses : perModule.values()) {
Collections.sort(perModuleClasses);
testNames.addAll(perModuleClasses);
}
} else {
Collections.sort(testNames); //sort tests in FQN order
}
for (String testName : testNames) {
writer.println(testName);
else {
testNames.add(name);
}
}
finally {
writer.close();
if (perModule != null) {
for (List<String> perModuleClasses : perModule.values()) {
Collections.sort(perModuleClasses);
testNames.addAll(perModuleClasses);
}
}
else {
Collections.sort(testNames); //sort tests in FQN order
}
final JUnitConfiguration.Data data = myConfiguration.getPersistentData();
final String category = data.TEST_OBJECT == JUnitConfiguration.TEST_CATEGORY ? data.getCategory() : "";
JUnitStarter.printClassesList(testNames, packageName, category, myTempFile);
if (perModule != null && perModule.size() > 1) {
final PrintWriter wWriter = new PrintWriter(myWorkingDirsFile, CharsetToolkit.UTF8);
@@ -106,37 +106,27 @@ public class JUnitForkedStarter {
final String packageName = perDirReader.readLine();
String workingDir;
while ((workingDir = perDirReader.readLine()) != null) {
final String classpath = perDirReader.readLine();
try {
File tempFile = File.createTempFile("idea_junit", ".tmp");
tempFile.deleteOnExit();
final FileOutputStream writer = new FileOutputStream(tempFile);
final String classpath = perDirReader.readLine();
List classNames = new ArrayList();
try {
final int classNamesSize = Integer.parseInt(perDirReader.readLine());
writer.write((packageName + ", working directory: \'" + workingDir + "\'\n").getBytes("UTF-8")); //instead of package name
writer.write("\n".getBytes("UTF-8")); //category
for (int i = 0; i < classNamesSize; i++) {
String className = perDirReader.readLine();
if (className == null) {
System.err.println("Class name is expected. Working dir: " + workingDir);
return -1;
}
classNames.add(className);
writer.write((className + "\n").getBytes("UTF-8"));
final int classNamesSize = Integer.parseInt(perDirReader.readLine());
for (int i = 0; i < classNamesSize; i++) {
String className = perDirReader.readLine();
if (className == null) {
System.err.println("Class name is expected. Working dir: " + workingDir);
return -1;
}
}
finally {
writer.close();
classNames.add(className);
}
final Object rootDescriptor = findByClassName(testRunner, (String)classNames.get(0), description);
final int childResult;
final File dir = new File(workingDir);
if (forkMode.equals("none")) {
File tempFile = File.createTempFile("idea_junit", ".tmp");
tempFile.deleteOnExit();
JUnitStarter.printClassesList(classNames, packageName + ", working directory: \'" + workingDir + "\'", "", tempFile);
childResult =
runChild(isJUnit4, listeners, out, err, parameters, "@" + tempFile.getAbsolutePath(), dir,
String.valueOf(testRunner.getRegistry().getKnownObject(rootDescriptor) - 1), classpath);
@@ -226,4 +226,19 @@ public class JUnitStarter {
: Class.forName("com.intellij.junit3.JUnit3IdeaTestRunner");
}
public static void printClassesList(List classNames, String packageName, String category, File tempFile) throws IOException {
final PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(tempFile), "UTF-8"));
try {
writer.println(packageName); //package name
writer.println(category); //category
for (int i = 0; i < classNames.size(); i++) {
writer.println(classNames.get(i));
}
}
finally {
writer.close();
}
}
}
@@ -96,7 +96,9 @@ public class CCProjectComponent implements ProjectComponent {
}
public void projectClosed() {
VirtualFileManager.getInstance().removeVirtualFileListener(myListener);
if (myListener != null) {
VirtualFileManager.getInstance().removeVirtualFileListener(myListener);
}
}
private class FileDeletedListener extends VirtualFileAdapter {
@@ -67,6 +67,7 @@
<action id="WelcomeScreen.LearnPython" class="com.jetbrains.python.edu.actions.StudyNewProject" icon="StudyIcons.EducationalProjectType">
<add-to-group group-id="WelcomeScreen.QuickStart" relative-to-action="WelcomeScreen.PythonIntro" anchor="after"/>
</action>
<action id="ReloadCourseAction" class="com.jetbrains.python.edu.actions.StudyReloadCourseAction"/>
</actions>
@@ -22,108 +22,139 @@ import com.jetbrains.python.edu.StudyTaskManager;
import com.jetbrains.python.edu.StudyUtils;
import com.jetbrains.python.edu.course.*;
import com.jetbrains.python.edu.editor.StudyEditor;
import org.jetbrains.annotations.NotNull;
import java.io.*;
public class StudyRefreshTaskFileAction extends DumbAwareAction {
private static final Logger LOG = Logger.getInstance(StudyRefreshTaskFileAction.class.getName());
public void refresh(final Project project) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public static void refresh(final Project project) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
@Override
public void run() {
final Editor editor = StudyEditor.getSelectedEditor(project);
assert editor != null;
final Document document = editor.getDocument();
StudyDocumentListener listener = StudyEditor.getListener(document);
if (listener != null) {
document.removeDocumentListener(listener);
}
final int lineCount = document.getLineCount();
if (lineCount != 0) {
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@Override
public void run() {
document.deleteString(0, document.getLineEndOffset(lineCount - 1));
}
});
}
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
Course course = taskManager.getCourse();
assert course != null;
File resourceFile = new File(course.getResourcePath());
File resourceRoot = resourceFile.getParentFile();
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
VirtualFile openedFile = fileDocumentManager.getFile(document);
assert openedFile != null;
final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
assert selectedTaskFile != null;
Task currentTask = selectedTaskFile.getTask();
String lessonDir = Lesson.LESSON_DIR + String.valueOf(currentTask.getLesson().getIndex() + 1);
String taskDir = Task.TASK_DIR + String.valueOf(currentTask.getIndex() + 1);
File pattern = new File(new File(new File(resourceRoot, lessonDir), taskDir), openedFile.getName());
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(pattern)));
String line;
StringBuilder patternText = new StringBuilder();
while ((line = reader.readLine()) != null) {
patternText.append(line);
patternText.append("\n");
}
int patternLength = patternText.length();
if (patternText.charAt(patternLength - 1) == '\n') {
patternText.delete(patternLength - 1, patternLength);
}
document.setText(patternText);
StudyStatus oldStatus = currentTask.getStatus();
LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo();
lessonInfo.update(oldStatus, -1);
lessonInfo.update(StudyStatus.Unchecked, +1);
StudyUtils.updateStudyToolWindow(project);
for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) {
taskWindow.reset();
}
ProjectView.getInstance(project).refresh();
if (listener != null) {
document.addDocumentListener(listener);
}
selectedTaskFile.drawAllWindows(editor);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
}
});
selectedTaskFile.navigateToFirstTaskWindow(editor);
BalloonBuilder balloonBuilder =
JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can now start again", MessageType.INFO, null);
final Balloon balloon = balloonBuilder.createBalloon();
StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
assert selectedStudyEditor != null;
balloon.showInCenterOf(selectedStudyEditor.getRefreshButton());
Disposer.register(project, balloon);
}
catch (FileNotFoundException e1) {
LOG.error(e1);
}
catch (IOException e1) {
LOG.error(e1);
}
finally {
StudyUtils.closeSilently(reader);
}
}
});
final Editor editor = StudyEditor.getSelectedEditor(project);
assert editor != null;
final Document document = editor.getDocument();
refreshFile(editor, document, project);
}
});
}
});
}
public void actionPerformed(AnActionEvent e) {
public static void refreshFile(@NotNull final Editor editor, @NotNull final Document document, @NotNull final Project project) {
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
Course course = taskManager.getCourse();
assert course != null;
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
VirtualFile openedFile = fileDocumentManager.getFile(document);
assert openedFile != null;
final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
assert selectedTaskFile != null;
String openedFileName = openedFile.getName();
Task currentTask = selectedTaskFile.getTask();
resetTaskFile(document, project, course, selectedTaskFile, openedFileName, currentTask);
selectedTaskFile.drawAllWindows(editor);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
}
});
selectedTaskFile.navigateToFirstTaskWindow(editor);
showBaloon(project);
}
public static void resetTaskFile(Document document, Project project, Course course, TaskFile taskFile, String name, Task task) {
resetDocument(document, course, name, task);
updateLessonInfo(task);
StudyUtils.updateStudyToolWindow(project);
resetTaskWindows(taskFile);
ProjectView.getInstance(project).refresh();
}
private static void showBaloon(Project project) {
BalloonBuilder balloonBuilder =
JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can now start again", MessageType.INFO, null);
final Balloon balloon = balloonBuilder.createBalloon();
StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
assert selectedStudyEditor != null;
balloon.showInCenterOf(selectedStudyEditor.getRefreshButton());
Disposer.register(project, balloon);
}
private static void resetTaskWindows(TaskFile selectedTaskFile) {
for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) {
taskWindow.reset();
}
}
private static void updateLessonInfo(Task currentTask) {
StudyStatus oldStatus = currentTask.getStatus();
LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo();
lessonInfo.update(oldStatus, -1);
lessonInfo.update(StudyStatus.Unchecked, +1);
}
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
private static void resetDocument(Document document, Course course, String fileName, Task task) {
BufferedReader reader = null;
StudyDocumentListener listener = StudyEditor.getListener(document);
if (listener != null) {
document.removeDocumentListener(listener);
}
clearDocument(document);
try {
String lessonDir = Lesson.LESSON_DIR + String.valueOf(task.getLesson().getIndex() + 1);
String taskDir = Task.TASK_DIR + String.valueOf(task.getIndex() + 1);
File resourceFile = new File(course.getResourcePath());
File resourceRoot = resourceFile.getParentFile();
File pattern = new File(new File(new File(resourceRoot, lessonDir), taskDir), fileName);
reader = new BufferedReader(new InputStreamReader(new FileInputStream(pattern)));
String line;
StringBuilder patternText = new StringBuilder();
while ((line = reader.readLine()) != null) {
patternText.append(line);
patternText.append("\n");
}
int patternLength = patternText.length();
if (patternText.charAt(patternLength - 1) == '\n') {
patternText.delete(patternLength - 1, patternLength);
}
document.setText(patternText);
}
catch (FileNotFoundException e) {
LOG.error(e);
}
catch (IOException e) {
LOG.error(e);
}
finally {
StudyUtils.closeSilently(reader);
}
if (listener != null) {
document.addDocumentListener(listener);
}
}
private static void clearDocument(final Document document) {
final int lineCount = document.getLineCount();
if (lineCount != 0) {
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@Override
public void run() {
document.deleteString(0, document.getLineEndOffset(lineCount - 1));
}
});
}
}
public void actionPerformed(@NotNull AnActionEvent e) {
refresh(e.getProject());
}
}
@@ -0,0 +1,134 @@
/*
* 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.jetbrains.python.edu.actions;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ui.tree.TreeUtil;
import com.jetbrains.python.edu.StudyTaskManager;
import com.jetbrains.python.edu.StudyUtils;
import com.jetbrains.python.edu.course.Course;
import com.jetbrains.python.edu.course.Lesson;
import com.jetbrains.python.edu.course.Task;
import com.jetbrains.python.edu.course.TaskFile;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.TreePath;
import java.util.List;
import java.util.Map;
public class StudyReloadCourseAction extends DumbAwareAction {
public StudyReloadCourseAction() {
super("Reload Course", "Reload Course", null);
}
@Override
public void update(@NotNull AnActionEvent e) {
Presentation presentation = e.getPresentation();
Project project = e.getProject();
if (project != null) {
Course course = StudyTaskManager.getInstance(project).getCourse();
if (course != null) {
presentation.setVisible(true);
presentation.setEnabled(true);
}
}
presentation.setVisible(false);
presentation.setEnabled(false);
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (project == null) {
return;
}
reloadCourse(project);
}
public static void reloadCourse(@NotNull final Project project) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
Course course = StudyTaskManager.getInstance(project).getCourse();
if (course == null) {
return;
}
for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) {
FileEditorManager.getInstance(project).closeFile(file);
}
JTree tree = ProjectView.getInstance(project).getCurrentProjectViewPane().getTree();
TreePath path = TreeUtil.getFirstNodePath(tree);
tree.collapsePath(path);
List<Lesson> lessons = course.getLessons();
for (Lesson lesson : lessons) {
List<Task> tasks = lesson.getTaskList();
VirtualFile lessonDir = project.getBaseDir().findChild(Lesson.LESSON_DIR + (lesson.getIndex() + 1));
if (lessonDir == null) {
continue;
}
for (Task task : tasks) {
VirtualFile taskDir = lessonDir.findChild(Task.TASK_DIR + (task.getIndex() + 1));
if (taskDir == null) {
continue;
}
Map<String, TaskFile> taskFiles = task.getTaskFiles();
for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
String name = entry.getKey();
TaskFile taskFile = entry.getValue();
VirtualFile file = taskDir.findChild(name);
if (file == null) {
continue;
}
Document document = FileDocumentManager.getInstance().getDocument(file);
if (document == null) {
continue;
}
StudyRefreshTaskFileAction.resetTaskFile(document, project, course, taskFile, name, task);
}
}
}
Lesson firstLesson = StudyUtils.getFirst(lessons);
if (firstLesson == null) {
return;
}
Task firstTask = StudyUtils.getFirst(firstLesson.getTaskList());
VirtualFile lessonDir = project.getBaseDir().findChild(Lesson.LESSON_DIR + (firstLesson.getIndex() + 1));
if (lessonDir != null) {
VirtualFile taskDir = lessonDir.findChild(Task.TASK_DIR + (firstTask.getIndex() + 1));
if (taskDir != null) {
ProjectView.getInstance(project).select(taskDir, taskDir, true);
}
}
}
});
}
});
}
}
@@ -9,6 +9,7 @@ import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.edu.StudyTaskManager;
import com.jetbrains.python.edu.actions.StudyReloadCourseAction;
import com.jetbrains.python.edu.course.Course;
import com.jetbrains.python.edu.course.Lesson;
import com.jetbrains.python.edu.course.LessonInfo;
@@ -17,6 +18,8 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
@@ -41,7 +44,15 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
contentPanel.add(new JLabel(authorLabel));
contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
contentPanel.add(new JLabel(description));
contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
JButton reloadCourseButton = new JButton("reload course");
reloadCourseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
StudyReloadCourseAction.reloadCourse(project);
}
});
contentPanel.add(reloadCourseButton);
int taskNum = 0;
int taskSolved = 0;
int lessonsCompleted = 0;