diff --git a/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java b/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java index a83e63439760..d4f8bdf090e3 100644 --- a/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java +++ b/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java @@ -30,7 +30,7 @@ import org.jetbrains.annotations.NotNull; /** * @author peter */ -public class DefaultLightProjectDescriptor implements LightProjectDescriptor { +public class DefaultLightProjectDescriptor extends LightProjectDescriptor { @NotNull @Override public ModuleType getModuleType() { diff --git a/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java index f4368bc68414..9eb99d8c72d8 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java @@ -53,7 +53,6 @@ import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl; import com.intellij.openapi.module.EmptyModuleType; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.project.ModuleAdapter; import com.intellij.openapi.project.Project; @@ -61,18 +60,19 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.project.impl.ProjectManagerImpl; import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.ShutDownTracker; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.openapi.vfs.encoding.EncodingManagerImpl; -import com.intellij.openapi.vfs.ex.temp.TempFileSystem; import com.intellij.openapi.vfs.impl.VirtualFilePointerManagerImpl; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl; @@ -92,19 +92,17 @@ import com.intellij.psi.impl.PsiManagerImpl; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl; import com.intellij.psi.templateLanguages.TemplateDataLanguageMappings; import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; -import com.intellij.util.Consumer; import com.intellij.util.GCUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.LocalTimeCounter; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.indexing.FileBasedIndex; -import com.intellij.util.indexing.IndexableFileSet; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; import gnu.trove.THashMap; import junit.framework.TestCase; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; @@ -119,8 +117,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import static com.intellij.openapi.roots.ModuleRootModificationUtil.updateModel; - /** * @author yole */ @@ -228,11 +224,6 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da ((PersistentFSImpl)PersistentFS.getInstance()).cleanPersistedContents(); } - public static boolean isLight(@NotNull Project project) { - String creationPlace = project.getUserData(CREATION_PLACE); - return creationPlace != null && StringUtil.startsWith(creationPlace, LIGHT_PROJECT_MARK); - } - private static void initProject(@NotNull final LightProjectDescriptor descriptor) throws Exception { ourProjectDescriptor = descriptor; @@ -261,78 +252,10 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da registerShutdownHook(); } ourPsiManager = null; - ourModule = createMainModule(descriptor.getModuleType()); - if (descriptor instanceof LightProjectDescriptorEx) { - ((LightProjectDescriptorEx)descriptor).setupModule(ourModule); - - if(!((LightProjectDescriptorEx)descriptor).shouldConfigureModule()) return; - } - - VirtualFile dummyRoot = VirtualFileManager.getInstance().findFileByUrl("temp:///"); - assert dummyRoot != null; - dummyRoot.refresh(false, false); - - try { - ourSourceRoot = dummyRoot.createChildDirectory(this, "src"); - cleanSourceRoot(); - } - catch (IOException e) { - throw new RuntimeException(e); - } - - final IndexableFileSet indexableFileSet = new IndexableFileSet() { - @Override - public boolean isInSet(@NotNull final VirtualFile file) { - return ourSourceRoot != null && - file.getFileSystem() == ourSourceRoot.getFileSystem() && - ourProject != null && - ourProject.isOpen(); - } - - @Override - public void iterateIndexableFilesIn(@NotNull final VirtualFile file, @NotNull final ContentIterator iterator) { - VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() { - @Override - public boolean visitFile(@NotNull VirtualFile file) { - iterator.processFile(file); - return true; - } - }); - } - }; - FileBasedIndex.getInstance().registerIndexableSet(indexableFileSet, null); - Disposer.register(ourProject, new Disposable() { - @Override - public void dispose() { - FileBasedIndex.getInstance().removeIndexableSet(indexableFileSet); - } - }); - - updateModel(ourModule, new Consumer() { - @Override - public void consume(ModifiableRootModel model) { - final Sdk sdk = descriptor.getSdk(); - if (sdk != null) { - model.setSdk(sdk); - } - - ContentEntry contentEntry = model.addContentEntry(ourSourceRoot); - contentEntry.addSourceFolder(ourSourceRoot, false); - - descriptor.configureModule(ourModule, model, contentEntry); - } - }); - } - - private void cleanSourceRoot() throws IOException { - TempFileSystem tempFs = (TempFileSystem)ourSourceRoot.getFileSystem(); - for (VirtualFile child : ourSourceRoot.getChildren()) { - if (!tempFs.exists(child)) { - tempFs.createChildFile(this, ourSourceRoot, child.getName()); - } - child.delete(this); - } + LightProjectDescriptor.ProjectInfo projectInfo = ourProjectDescriptor.setUpProject(ourProject); + ourModule = projectInfo.module; + ourSourceRoot = projectInfo.moduleSourcesRoot; } }.execute().throwException(); @@ -340,15 +263,6 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).storePointers(); } - @NotNull - protected static Module createMainModule(@NotNull final ModuleType moduleType) { - return ApplicationManager.getApplication().runWriteAction(new Computable() { - @Override - public Module compute() { - return ModuleManager.getInstance(ourProject).newModule("light_idea_test_case.iml", moduleType.getId()); - } - }); - } /** * @return The only source root @@ -838,11 +752,11 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da }); } - private static class SimpleLightProjectDescriptor implements LightProjectDescriptor { + private static class SimpleLightProjectDescriptor extends LightProjectDescriptor { @NotNull private final ModuleType myModuleType; - private final Sdk mySdk; + @Nullable private final Sdk mySdk; - SimpleLightProjectDescriptor(@NotNull ModuleType moduleType, Sdk sdk) { + SimpleLightProjectDescriptor(@NotNull ModuleType moduleType, @Nullable Sdk sdk) { myModuleType = moduleType; mySdk = sdk; } @@ -853,15 +767,12 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da return myModuleType; } + @Nullable @Override public Sdk getSdk() { return mySdk; } - @Override - public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java b/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java index 915fef8195a2..58aca8ac93d3 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java @@ -15,39 +15,144 @@ */ package com.intellij.testFramework; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.EmptyModuleType; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ContentIterator; import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.VirtualFileVisitor; +import com.intellij.openapi.vfs.ex.temp.TempFileSystem; +import com.intellij.util.Consumer; +import com.intellij.util.indexing.FileBasedIndex; +import com.intellij.util.indexing.IndexableFileSet; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +import static com.intellij.openapi.roots.ModuleRootModificationUtil.updateModel; + + +public class LightProjectDescriptor { + public static final LightProjectDescriptor EMPTY_PROJECT_DESCRIPTOR = new LightProjectDescriptor(); -/** - * @author yole - */ -public interface LightProjectDescriptor { @NotNull - ModuleType getModuleType(); - Sdk getSdk(); - void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry); + public ProjectInfo setUpProject(@NotNull Project project) throws Exception { + Module module = createMainModule(project); + VirtualFile contentRoot = createSourcesRoot(module); + return new ProjectInfo(module, contentRoot); + } - class Empty implements LightProjectDescriptor { - @NotNull - @Override - public ModuleType getModuleType() { - return EmptyModuleType.getInstance(); + @NotNull + public Module createMainModule(@NotNull final Project project) { + return ApplicationManager.getApplication().runWriteAction(new Computable() { + @Override + public Module compute() { + return ModuleManager.getInstance(project).newModule("light_idea_test_case.iml", getModuleType().getId()); + } + }); + } + + @NotNull + public ModuleType getModuleType() { + return EmptyModuleType.getInstance(); + } + + @Nullable + public VirtualFile createSourcesRoot(@NotNull final Module module) { + VirtualFile dummyRoot = VirtualFileManager.getInstance().findFileByUrl("temp:///"); + assert dummyRoot != null; + dummyRoot.refresh(false, false); + + final VirtualFile srcRoot; + try { + srcRoot = dummyRoot.createChildDirectory(this, "src"); + cleanSourceRoot(srcRoot); + } + catch (IOException e) { + throw new RuntimeException(e); } - @Override - public Sdk getSdk() { - return null; - } + final IndexableFileSet indexableFileSet = new IndexableFileSet() { + @Override + public boolean isInSet(@NotNull final VirtualFile file) { + return file.getFileSystem() == srcRoot.getFileSystem() && + module.getProject().isOpen(); + } - @Override - public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { + @Override + public void iterateIndexableFilesIn(@NotNull final VirtualFile file, @NotNull final ContentIterator iterator) { + VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() { + @Override + public boolean visitFile(@NotNull VirtualFile file) { + iterator.processFile(file); + return true; + } + }); + } + }; + FileBasedIndex.getInstance().registerIndexableSet(indexableFileSet, null); + Disposer.register(module.getProject(), new Disposable() { + @Override + public void dispose() { + FileBasedIndex.getInstance().removeIndexableSet(indexableFileSet); + } + }); + + updateModel(module, new Consumer() { + @Override + public void consume(ModifiableRootModel model) { + Sdk sdk = getSdk(); + if (sdk != null) { + model.setSdk(sdk); + } + + ContentEntry contentEntry = model.addContentEntry(srcRoot); + contentEntry.addSourceFolder(srcRoot, false); + + configureModule(module, model, contentEntry); + } + }); + + return srcRoot; + } + + @Nullable + public Sdk getSdk() { + return null; + } + + private void cleanSourceRoot(@NotNull VirtualFile contentRoot) throws IOException { + TempFileSystem tempFs = (TempFileSystem)contentRoot.getFileSystem(); + for (VirtualFile child : contentRoot.getChildren()) { + if (!tempFs.exists(child)) { + tempFs.createChildFile(this, contentRoot, child.getName()); + } + child.delete(this); } } - LightProjectDescriptor EMPTY_PROJECT_DESCRIPTOR = new Empty(); + protected void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { + } + + public static class ProjectInfo { + @NotNull public final Module module; + @Nullable public final VirtualFile moduleSourcesRoot; + + public ProjectInfo(@NotNull Module module, @Nullable VirtualFile moduleSourcesRoot) { + this.module = module; + this.moduleSourcesRoot = moduleSourcesRoot; + } + } } diff --git a/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptorEx.java b/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptorEx.java deleted file mode 100644 index 26c68c476028..000000000000 --- a/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptorEx.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2000-2015 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.testFramework; - -import com.intellij.openapi.module.Module; - -public interface LightProjectDescriptorEx extends LightProjectDescriptor { - boolean shouldConfigureModule(); - - void setupModule(Module module); -} diff --git a/python/educational/interactive-learning/resources/META-INF/plugin.xml b/python/educational/interactive-learning/resources/META-INF/plugin.xml index 0c112e2e4bea..57adbc2196e7 100644 --- a/python/educational/interactive-learning/resources/META-INF/plugin.xml +++ b/python/educational/interactive-learning/resources/META-INF/plugin.xml @@ -50,9 +50,9 @@ description="Navigate to the next answer holder"> + description="Navigate to the previous task"/> + description="Navigate to the next task"/> - + blocks = documentImpl.getGuardedBlocks(); + for (final RangeMarker block : blocks) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + document.removeGuardedBlock(block); + } + }); + } + }); + } + } + } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java index 3b8e5f84e5ae..f92c883c9c92 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java @@ -6,6 +6,7 @@ import com.intellij.execution.process.ProcessOutput; import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; @@ -14,6 +15,7 @@ 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.keymap.KeymapUtil; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.TaskInfo; @@ -43,9 +45,11 @@ import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import com.jetbrains.edu.learning.run.StudySmartChecker; import com.jetbrains.edu.learning.run.StudyTestRunner; +import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.awt.*; import java.io.IOException; import java.util.List; @@ -58,6 +62,11 @@ public class StudyCheckAction extends DumbAwareAction { public static final String ACTION_ID = "CheckAction"; public static final String SHORTCUT = "ctrl alt pressed ENTER"; + boolean checkInProgress = false; + + public StudyCheckAction() { + super("Check Task (" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")", "Check current task", InteractiveLearningIcons.Resolve); + } private static void flushWindows(@NotNull final Task task, @NotNull final VirtualFile taskDir) { for (Map.Entry entry : task.getTaskFiles().entrySet()) { @@ -115,7 +124,7 @@ public class StudyCheckAction extends DumbAwareAction { CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { @Override public void run() { - final StudyEditor selectedEditor = StudyEditor.getSelectedStudyEditor(project); + final StudyEditor selectedEditor = StudyUtils.getSelectedStudyEditor(project); if (selectedEditor == null) return; final StudyState studyState = new StudyState(selectedEditor); if (!studyState.isValid()) { @@ -158,7 +167,7 @@ public class StudyCheckAction extends DumbAwareAction { if (testProcess == null) { return; } - selectedEditor.getCheckButton().setEnabled(false); + checkInProgress = true; ProgressManager.getInstance().run(getCheckTask(studyState, testRunner, testProcess, project, selectedEditor)); } }); @@ -202,14 +211,14 @@ public class StudyCheckAction extends DumbAwareAction { drawAllPlaceholders(project, task, taskDir); ProjectView.getInstance(project).refresh(); deleteWindowDescriptions(task, taskDir); - selectedEditor.getCheckButton().setEnabled(true); + checkInProgress = false; } @Override public void onCancel() { taskManager.setStatus(task, statusBeforeCheck); deleteWindowDescriptions(task, taskDir); - selectedEditor.getCheckButton().setEnabled(true); + checkInProgress = false; } @Override @@ -382,7 +391,6 @@ public class StudyCheckAction extends DumbAwareAction { StudyUtils.showCheckPopUp(project, balloon); } - @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); @@ -390,4 +398,9 @@ public class StudyCheckAction extends DumbAwareAction { check(project); } } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(!checkInProgress); + } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyEditInputAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyEditInputAction.java index 24d6e31f3fb6..8b11fb42ffd5 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyEditInputAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyEditInputAction.java @@ -29,6 +29,7 @@ import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.UserTest; import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.ui.StudyTestContentPanel; +import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -46,8 +47,12 @@ public class StudyEditInputAction extends DumbAwareAction { private JBEditorTabs tabbedPane; private Map myEditableTabs = new HashMap(); + public StudyEditInputAction() { + super("Watch Test Input", "Watch test input", InteractiveLearningIcons.WatchInput); + } + public void showInput(final Project project) { - final Editor selectedEditor = StudyEditor.getSelectedEditor(project); + final Editor selectedEditor = StudyUtils.getSelectedEditor(project); if (selectedEditor != null) { FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); final VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument()); @@ -100,7 +105,7 @@ public class StudyEditInputAction extends DumbAwareAction { .setMovable(true) .setRequestFocus(true) .createPopup(); - StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project); + StudyEditor selectedStudyEditor = StudyUtils.getSelectedStudyEditor(project); assert selectedStudyEditor != null; hint.showInCenterOf(selectedStudyEditor.getComponent()); hint.addListener(new HintClosedListener(currentTask, studyTaskManager)); @@ -219,4 +224,19 @@ public class StudyEditInputAction extends DumbAwareAction { } } } + @Override + public void update(final AnActionEvent e) { + EduUtils.enableAction(e, false); + + final Project project = e.getProject(); + if (project != null) { + StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); + if (studyEditor != null) { + final List userTests = StudyTaskManager.getInstance(project).getUserTests(studyEditor.getTaskFile().getTask()); + if (!userTests.isEmpty()) { + EduUtils.enableAction(e, true); + } + } + } + } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyNextStudyTaskAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyNextStudyTaskAction.java index 458039259122..d76308bac774 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyNextStudyTaskAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyNextStudyTaskAction.java @@ -1,7 +1,9 @@ package com.jetbrains.edu.learning.actions; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.keymap.KeymapUtil; import com.jetbrains.edu.courseFormat.Task; -import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import org.jetbrains.annotations.NotNull; @@ -12,9 +14,8 @@ public class StudyNextStudyTaskAction extends StudyTaskNavigationAction { public static final String ACTION_ID = "NextTaskAction"; public static final String SHORTCUT = "ctrl pressed PERIOD"; - @Override - protected JButton getButton(@NotNull final StudyEditor selectedStudyEditor) { - return selectedStudyEditor.getNextTaskButton(); + public StudyNextStudyTaskAction() { + super("Next Task (" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")", "Navigate to the next task", AllIcons.Actions.Forward); } @Override diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyPreviousStudyTaskAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyPreviousStudyTaskAction.java index 35e8048f49ca..0c3d5b4088ce 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyPreviousStudyTaskAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyPreviousStudyTaskAction.java @@ -1,21 +1,22 @@ package com.jetbrains.edu.learning.actions; +import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.keymap.KeymapUtil; import com.jetbrains.edu.courseFormat.Task; -import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; +import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; public class StudyPreviousStudyTaskAction extends StudyTaskNavigationAction { + public StudyPreviousStudyTaskAction() { + super("Previous Task (" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")", "Navigate to the previous task", InteractiveLearningIcons.Prev); + } public static final String ACTION_ID = "PreviousTaskAction"; public static final String SHORTCUT = "ctrl pressed COMMA"; - @Override - protected JButton getButton(@NotNull final StudyEditor selectedStudyEditor) { - return selectedStudyEditor.getPrevTaskButton(); - } @Override protected String getNavigationFinishedMessage() { diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java index 9725e19043b4..151aec37dd85 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java @@ -1,13 +1,16 @@ package com.jetbrains.edu.learning.actions; +import com.intellij.icons.AllIcons; import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; 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.keymap.KeymapUtil; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -35,6 +38,7 @@ import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import org.jetbrains.annotations.NotNull; +import javax.swing.*; import java.io.File; public class StudyRefreshTaskFileAction extends DumbAwareAction { @@ -42,6 +46,10 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { public static final String SHORTCUT = "ctrl shift pressed X"; private static final Logger LOG = Logger.getInstance(StudyRefreshTaskFileAction.class.getName()); + public StudyRefreshTaskFileAction() { + super("Reset Task File (" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")", "Refresh current task", AllIcons.Actions.Refresh); + } + public static void refresh(@NotNull final Project project) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override @@ -50,7 +58,7 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") @Override public void run() { - StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project); + StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); StudyState studyState = new StudyState(studyEditor); if (studyEditor == null || !studyState.isValid()) { LOG.info("RefreshTaskFileAction was invoked outside of Study Editor"); @@ -101,9 +109,9 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, messageType, null); final Balloon balloon = balloonBuilder.createBalloon(); - StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project); + StudyEditor selectedStudyEditor = StudyUtils.getSelectedStudyEditor(project); assert selectedStudyEditor != null; - balloon.showInCenterOf(selectedStudyEditor.getRefreshButton()); + balloon.show(StudyUtils.computeLocation(selectedStudyEditor.getEditor()), Balloon.Position.above); Disposer.register(project, balloon); } @@ -120,7 +128,7 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { @NotNull final Document document, @NotNull final TaskFile taskFile, String fileName) { - StudyEditor.deleteGuardedBlocks(document); + StudyUtils.deleteGuardedBlocks(document); taskFile.setTrackChanges(false); clearDocument(document); Task task = taskFile.getTask(); @@ -167,14 +175,14 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { @Override public void update(AnActionEvent event) { + EduUtils.enableAction(event, false); final Project project = event.getProject(); if (project != null) { - StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project); + StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); StudyState studyState = new StudyState(studyEditor); if (studyState.isValid()) { EduUtils.enableAction(event, true); } } - EduUtils.enableAction(event, false); } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRunAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRunAction.java index 8bf70f564a7c..79b07c1453ed 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRunAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRunAction.java @@ -5,6 +5,7 @@ import com.intellij.execution.RunContentExecutor; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessHandler; +import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; @@ -14,20 +15,29 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.EduUtils; import com.jetbrains.edu.courseFormat.Task; import com.jetbrains.edu.courseFormat.TaskFile; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.UserTest; import com.jetbrains.edu.learning.editor.StudyEditor; import org.jetbrains.annotations.NotNull; +import java.util.List; + public class StudyRunAction extends DumbAwareAction { private static final Logger LOG = Logger.getInstance(StudyRunAction.class.getName()); public static final String ACTION_ID = "StudyRunAction"; private ProcessHandler myHandler; + public StudyRunAction() { + super("Run File With Tests", "Run your code with tests", AllIcons.General.Run); + } + public void run(@NotNull final Project project) { if (myHandler != null && !myHandler.isProcessTerminated()) return; - Editor selectedEditor = StudyEditor.getSelectedEditor(project); + Editor selectedEditor = StudyUtils.getSelectedEditor(project); FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); assert selectedEditor != null; VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument()); @@ -79,4 +89,20 @@ public class StudyRunAction extends DumbAwareAction { run(project); } } + + @Override + public void update(AnActionEvent e) { + EduUtils.enableAction(e, false); + + final Project project = e.getProject(); + if (project != null) { + StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); + if (studyEditor != null) { + final List userTests = StudyTaskManager.getInstance(project).getUserTests(studyEditor.getTaskFile().getTask()); + if (!userTests.isEmpty()) { + EduUtils.enableAction(e, true); + } + } + } + } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java index ab3a25e10eef..a742ff0ae447 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java @@ -3,8 +3,10 @@ package com.jetbrains.edu.learning.actions; import com.intellij.codeInsight.documentation.DocumentationComponent; import com.intellij.codeInsight.documentation.DocumentationManager; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; @@ -18,10 +20,11 @@ import com.jetbrains.edu.courseFormat.Course; import com.jetbrains.edu.learning.StudyState; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; -import com.jetbrains.edu.learning.editor.StudyEditor; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; +import javax.swing.*; + public class StudyShowHintAction extends DumbAwareAction { public static final String ACTION_ID = "ShowHintAction"; public static final String SHORTCUT = "ctrl pressed 7"; @@ -29,7 +32,7 @@ public class StudyShowHintAction extends DumbAwareAction { public static final String HINT_NOT_AVAILABLE = "There is no hint for this answer placeholder"; public StudyShowHintAction() { - super("Show hint", "Show hint", InteractiveLearningIcons.ShowHint); + super("Show hint (" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")", "Show hint", InteractiveLearningIcons.ShowHint); } public void actionPerformed(@NotNull AnActionEvent e) { @@ -45,7 +48,7 @@ public class StudyShowHintAction extends DumbAwareAction { if (course == null) { return; } - StudyState studyState = new StudyState(StudyEditor.getSelectedStudyEditor(project)); + StudyState studyState = new StudyState(StudyUtils.getSelectedStudyEditor(project)); if (!studyState.isValid()) { return; } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java index 015a18ff227d..eaa828be292a 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java @@ -18,8 +18,10 @@ import com.jetbrains.edu.EduNames; import com.jetbrains.edu.courseFormat.Task; import com.jetbrains.edu.courseFormat.TaskFile; import com.jetbrains.edu.learning.StudyState; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.editor.StudyEditor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.TreePath; @@ -27,8 +29,12 @@ import java.util.Map; abstract public class StudyTaskNavigationAction extends DumbAwareAction { + public StudyTaskNavigationAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) { + super(text, description, icon); + } + public void navigateTask(@NotNull final Project project) { - StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project); + StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); StudyState studyState = new StudyState(studyEditor); if (!studyState.isValid()) { return; @@ -39,7 +45,7 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction { JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(getNavigationFinishedMessage(), MessageType.INFO, null); Balloon balloon = balloonBuilder.createBalloon(); assert studyEditor != null; - balloon.showInCenterOf(getButton(studyEditor)); + balloon.show(StudyUtils.computeLocation(studyEditor.getEditor()), Balloon.Position.above); return; } for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) { @@ -90,8 +96,6 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction { } } - protected abstract JButton getButton(@NotNull final StudyEditor selectedStudyEditor); - @Override public void actionPerformed(AnActionEvent e) { Project project = e.getProject(); diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java index 6a00f0e22122..add5910357f0 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java @@ -9,7 +9,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.edu.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.courseFormat.TaskFile; import com.jetbrains.edu.learning.StudyUtils; -import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,7 +22,7 @@ abstract public class StudyWindowNavigationAction extends DumbAwareAction { } private void navigateToPlaceholder(@NotNull final Project project) { - final Editor selectedEditor = StudyEditor.getSelectedEditor(project); + final Editor selectedEditor = StudyUtils.getSelectedEditor(project); if (selectedEditor != null) { final FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); final VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument()); diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java index 90b9afd9b60a..ae5366a50970 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditor.java @@ -1,405 +1,38 @@ package com.jetbrains.edu.learning.editor; -import com.intellij.codeHighlighting.BackgroundEditorHighlighter; -import com.intellij.icons.AllIcons; -import com.intellij.ide.structureView.StructureViewBuilder; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.KeyboardShortcut; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.colors.EditorColorsScheme; -import com.intellij.openapi.editor.impl.DocumentImpl; -import com.intellij.openapi.fileEditor.*; -import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; -import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.openapi.wm.ToolWindowId; -import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.pom.Navigatable; -import com.intellij.ui.BrowserHyperlinkListener; -import com.intellij.ui.HideableTitledPanel; -import com.intellij.ui.JBColor; -import com.intellij.util.ui.EmptyClipboardOwner; -import com.intellij.util.ui.UIUtil; import com.jetbrains.edu.EduDocumentListener; -import com.jetbrains.edu.courseFormat.Task; import com.jetbrains.edu.courseFormat.TaskFile; -import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; -import com.jetbrains.edu.learning.actions.*; -import com.jetbrains.edu.learning.courseFormat.UserTest; -import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.text.MutableAttributeSet; -import javax.swing.text.StyleConstants; -import javax.swing.text.StyledDocument; -import java.awt.*; -import java.awt.datatransfer.StringSelection; -import java.awt.event.*; -import java.beans.PropertyChangeListener; import java.util.HashMap; -import java.util.List; import java.util.Map; /** * Implementation of StudyEditor which has panel with special buttons and task text * also @see {@link StudyFileEditorProvider} */ -public class StudyEditor implements TextEditor { - private static final String TASK_TEXT_HEADER = "Task Text"; - private final FileEditor myDefaultEditor; - private final JComponent myComponent; +public class StudyEditor extends PsiAwareTextEditorImpl { private final TaskFile myTaskFile; - private JButton myCheckButton; - private JButton myNextTaskButton; - private JButton myPrevTaskButton; - private JButton myRefreshButton; private static final Map myDocumentListeners = new HashMap(); - private final Project myProject; - - public JButton getCheckButton() { - return myCheckButton; - } - - public JButton getPrevTaskButton() { - return myPrevTaskButton; - } public TaskFile getTaskFile() { return myTaskFile; } - private static JButton addButton(@NotNull final JComponent parentComponent, @NotNull final String actionID, - @NotNull final Icon icon, @Nullable String defaultShortcutString) { - final AnAction action = ActionManager.getInstance().getAction(actionID); - String toolTipText = KeymapUtil.createTooltipText(action.getTemplatePresentation().getText(), action); - if (!toolTipText.contains("(") && defaultShortcutString != null) { - KeyboardShortcut shortcut = new KeyboardShortcut(KeyStroke.getKeyStroke(defaultShortcutString), null); - toolTipText += " (" + KeymapUtil.getShortcutText(shortcut) + ")"; - } - final JButton newButton = new JButton(); - newButton.setToolTipText(toolTipText); - newButton.setIcon(icon); - newButton.setSize(new Dimension(icon.getIconWidth(), icon.getIconHeight())); - parentComponent.add(newButton); - return newButton; - } - public static void addDocumentListener(@NotNull final Document document, @NotNull final EduDocumentListener listener) { document.addDocumentListener(listener); myDocumentListeners.put(document, listener); } public StudyEditor(@NotNull final Project project, @NotNull final VirtualFile file) { - myProject = project; - myDefaultEditor = TextEditorProvider.getInstance().createEditor(myProject, file); - myComponent = myDefaultEditor.getComponent(); - final JPanel studyPanel = new JPanel(); - studyPanel.setLayout(new BoxLayout(studyPanel, BoxLayout.Y_AXIS)); + super(project, file, TextEditorProvider.getInstance()); myTaskFile = StudyUtils.getTaskFile(project, file); - if (myTaskFile != null) { - final Task currentTask = myTaskFile.getTask(); - final String taskText = currentTask.getText(); - //initializeTaskText(studyPanel, taskText); - final JPanel studyButtonPanel = new JPanel(new GridLayout(1, 2)); - final JPanel taskActionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - studyButtonPanel.add(taskActionsPanel); - studyButtonPanel.add(new JPanel()); - initializeButtons(taskActionsPanel, myTaskFile); - studyPanel.add(studyButtonPanel); - myComponent.add(studyPanel, BorderLayout.NORTH); - } - } - - class CopyListener extends MouseAdapter { - final JTextPane myTextPane; - - public CopyListener(@NotNull final JTextPane textPane) { - myTextPane = textPane; - } - - @Override - public void mouseReleased(MouseEvent e) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - final ToolWindow projectView = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW); - if (projectView == null) { - return; - } - final Component focusComponent = projectView.getComponent(); - IdeFocusManager.getInstance(myProject).requestFocus(focusComponent, true); - final String text = myTextPane.getSelectedText(); - if (text == null) { - return; - } - KeyAdapter keyAdapter = new KeyAdapter() { - @Override - public void keyPressed(KeyEvent ev) { - if (ev.getKeyCode() == KeyEvent.VK_C - && ev.getModifiers() == InputEvent.CTRL_MASK) { - final StringSelection selection = new StringSelection(text); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, EmptyClipboardOwner.INSTANCE); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - IdeFocusManager.getInstance(myProject).requestFocus(myDefaultEditor.getComponent(), true); - } - }); - } - } - }; - focusComponent.addKeyListener(keyAdapter); - } - }); - } - } - - private void initializeTaskText(@NotNull final JPanel studyPanel, @Nullable final String taskText) { - final JTextPane taskTextPane = new JTextPane(); - taskTextPane.addMouseListener(new CopyListener(taskTextPane)); - taskTextPane.setContentType("text/html"); - taskTextPane.setEditable(false); - taskTextPane.setText(taskText); - taskTextPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); - final EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getGlobalScheme(); - int fontSize = editorColorsScheme.getEditorFontSize(); - final String fontName = editorColorsScheme.getEditorFontName(); - setJTextPaneFont(taskTextPane, new Font(fontName, Font.PLAIN, fontSize)); - taskTextPane.setBackground(UIUtil.getPanelBackground()); - taskTextPane.setBorder(new EmptyBorder(15, 20, 0, 100)); - final HideableTitledPanel taskTextPanel = new HideableTitledPanel(TASK_TEXT_HEADER, taskTextPane, true); - taskTextPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); - studyPanel.add(taskTextPanel); - } - - private static void setJTextPaneFont(@NotNull final JTextPane textPane, @NotNull final Font font) { - final MutableAttributeSet attrs = textPane.getInputAttributes(); - StyleConstants.setFontFamily(attrs, font.getFamily()); - StyleConstants.setFontSize(attrs, font.getSize()); - StyleConstants.setItalic(attrs, (font.getStyle() & Font.ITALIC) != 0); - StyleConstants.setBold(attrs, (font.getStyle() & Font.BOLD) != 0); - StyleConstants.setForeground(attrs, JBColor.BLACK); - StyledDocument doc = textPane.getStyledDocument(); - doc.setCharacterAttributes(0, doc.getLength() + 1, attrs, false); - } - - private void initializeButtons(@NotNull final JPanel taskActionsPanel, @NotNull final TaskFile taskFile) { - myCheckButton = addButton(taskActionsPanel, StudyCheckAction.ACTION_ID, InteractiveLearningIcons.Resolve, StudyCheckAction.SHORTCUT); - myPrevTaskButton = addButton(taskActionsPanel, StudyPreviousStudyTaskAction.ACTION_ID, InteractiveLearningIcons.Prev, StudyPreviousStudyTaskAction.SHORTCUT); - myNextTaskButton = addButton(taskActionsPanel, StudyNextStudyTaskAction.ACTION_ID, AllIcons.Actions.Forward, StudyNextStudyTaskAction.SHORTCUT); - myRefreshButton = addButton(taskActionsPanel, StudyRefreshTaskFileAction.ACTION_ID, AllIcons.Actions.Refresh, StudyRefreshTaskFileAction.SHORTCUT); - final JButton myShowHintButton = addButton(taskActionsPanel, StudyShowHintAction.ACTION_ID, InteractiveLearningIcons.ShowHint, StudyShowHintAction.SHORTCUT); - final List userTests = StudyTaskManager.getInstance(myProject).getUserTests(taskFile.getTask()); - if (!userTests.isEmpty()) { - final JButton runButton = addButton(taskActionsPanel, StudyRunAction.ACTION_ID, AllIcons.General.Run, null); - runButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - StudyRunAction studyRunAction = (StudyRunAction)ActionManager.getInstance().getAction("StudyRunAction"); - studyRunAction.run(myProject); - } - }); - final JButton watchInputButton = addButton(taskActionsPanel, "WatchInputAction", InteractiveLearningIcons.WatchInput, null); - watchInputButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - StudyEditInputAction studyEditInputAction = - (StudyEditInputAction)ActionManager.getInstance().getAction("WatchInputAction"); - studyEditInputAction.showInput(myProject); - } - }); - } - initializeButtonActions(myShowHintButton); - } - - protected void initializeButtonActions(JButton myShowHintButton) { - myCheckButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - StudyCheckAction studyCheckAction = (StudyCheckAction)ActionManager.getInstance().getAction("CheckAction"); - studyCheckAction.check(myProject); - } - }); - - myNextTaskButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - StudyNextStudyTaskAction studyNextTaskAction = - (StudyNextStudyTaskAction)ActionManager.getInstance().getAction("NextTaskAction"); - studyNextTaskAction.navigateTask(myProject); - } - }); - myPrevTaskButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - StudyPreviousStudyTaskAction - prevTaskAction = (StudyPreviousStudyTaskAction)ActionManager.getInstance().getAction("PreviousTaskAction"); - prevTaskAction.navigateTask(myProject); - } - }); - myRefreshButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - StudyRefreshTaskFileAction.refresh(myProject); - } - }); - - myShowHintButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - StudyShowHintAction.showHint(myProject); - } - }); - } - - public JButton getNextTaskButton() { - return myNextTaskButton; - } - - public JButton getRefreshButton() { - return myRefreshButton; - } - - private FileEditor getDefaultEditor() { - return myDefaultEditor; - } - - @NotNull - @Override - public JComponent getComponent() { - return myComponent; - } - - @Nullable - @Override - public JComponent getPreferredFocusedComponent() { - return myDefaultEditor.getPreferredFocusedComponent(); - } - - @NotNull - @Override - public String getName() { - return "Study Editor"; - } - - @NotNull - @Override - public FileEditorState getState(@NotNull FileEditorStateLevel level) { - return myDefaultEditor.getState(level); - } - - @Override - public void setState(@NotNull FileEditorState state) { - myDefaultEditor.setState(state); - } - - @Override - public boolean isModified() { - return myDefaultEditor.isModified(); - } - - @Override - public boolean isValid() { - return myDefaultEditor.isValid(); - } - - @Override - public void selectNotify() { - myDefaultEditor.selectNotify(); - } - - @Override - public void deselectNotify() { - myDefaultEditor.deselectNotify(); - } - - @Override - public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) { - myDefaultEditor.addPropertyChangeListener(listener); - } - - @Override - public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) { - myDefaultEditor.removePropertyChangeListener(listener); - } - - @Nullable - @Override - public BackgroundEditorHighlighter getBackgroundHighlighter() { - return myDefaultEditor.getBackgroundHighlighter(); - } - - @Nullable - @Override - public FileEditorLocation getCurrentLocation() { - return myDefaultEditor.getCurrentLocation(); - } - - @Nullable - @Override - public StructureViewBuilder getStructureViewBuilder() { - return myDefaultEditor.getStructureViewBuilder(); - } - - @Override - public void dispose() { - Disposer.dispose(myDefaultEditor); - } - - @Nullable - @Override - public T getUserData(@NotNull Key key) { - return myDefaultEditor.getUserData(key); - } - - @Override - public void putUserData(@NotNull Key key, @Nullable T value) { - myDefaultEditor.putUserData(key, value); - } - - - @Nullable - public static StudyEditor getSelectedStudyEditor(@NotNull final Project project) { - try { - final FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow(). - getSelectedEditor().getSelectedEditorWithProvider().getFirst(); - if (fileEditor instanceof StudyEditor) { - return (StudyEditor)fileEditor; - } - } - catch (Exception e) { - return null; - } - return null; - } - - @Nullable - public static Editor getSelectedEditor(@NotNull final Project project) { - final StudyEditor studyEditor = getSelectedStudyEditor(project); - if (studyEditor != null) { - FileEditor defaultEditor = studyEditor.getDefaultEditor(); - if (defaultEditor instanceof PsiAwareTextEditorImpl) { - return ((PsiAwareTextEditorImpl)defaultEditor).getEditor(); - } - } - return null; } public static void removeListener(Document document) { @@ -409,48 +42,4 @@ public class StudyEditor implements TextEditor { } myDocumentListeners.remove(document); } - - @NotNull - @Override - public Editor getEditor() { - if (myDefaultEditor instanceof TextEditor) { - return ((TextEditor)myDefaultEditor).getEditor(); - } - return EditorFactory.getInstance().createViewer(new DocumentImpl(""), myProject); - } - - @Override - public boolean canNavigateTo(@NotNull Navigatable navigatable) { - if (myDefaultEditor instanceof TextEditor) { - ((TextEditor)myDefaultEditor).canNavigateTo(navigatable); - } - return false; - } - - @Override - public void navigateTo(@NotNull Navigatable navigatable) { - if (myDefaultEditor instanceof TextEditor) { - ((TextEditor)myDefaultEditor).navigateTo(navigatable); - } - } - - public static void deleteGuardedBlocks(@NotNull final Document document) { - if (document instanceof DocumentImpl) { - final DocumentImpl documentImpl = (DocumentImpl)document; - List blocks = documentImpl.getGuardedBlocks(); - for (final RangeMarker block : blocks) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - document.removeGuardedBlock(block); - } - }); - } - }); - } - } - } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java index 1a04083a612e..66b06933e321 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java @@ -9,11 +9,14 @@ import com.intellij.openapi.editor.event.EditorFactoryListener; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.problems.WolfTheProblemSolver; import com.jetbrains.edu.EduDocumentListener; import com.jetbrains.edu.courseFormat.TaskFile; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.navigation.StudyNavigator; +import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import org.jetbrains.annotations.NotNull; @@ -43,6 +46,13 @@ public class StudyEditorFactoryListener implements EditorFactoryListener { StudyEditor.addDocumentListener(document, new EduDocumentListener(taskFile)); WolfTheProblemSolver.getInstance(project).clearProblems(openedFile); StudyUtils.drawAllWindows(editor, taskFile); + + final ToolWindow studyToolWindow = ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); + if (studyToolWindow != null) { + StudyUtils.updateStudyToolWindow(project); + studyToolWindow.show(null); + } + } } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyCondition.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyCondition.java index 46ac007e4dc7..4895bc04faf7 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyCondition.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyCondition.java @@ -1,26 +1,15 @@ package com.jetbrains.edu.learning.ui; import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; -import com.jetbrains.edu.learning.StudyTaskManager; /** * author: liana * data: 7/29/14. */ public class StudyCondition implements Condition, DumbAware { - public static boolean VALUE = false; @Override public boolean value(Object o) { - if (o instanceof Project) { - Project project = (Project) o; - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - if (taskManager.getCourse() != null) { - //noinspection AssignmentToStaticFieldFromInstanceMethod - VALUE = true; - } - } return false; } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java new file mode 100644 index 000000000000..833223296db2 --- /dev/null +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java @@ -0,0 +1,136 @@ +/* + * Copyright 2000-2015 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.edu.learning.ui; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionToolbar; +import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.FileEditorManagerEvent; +import com.intellij.openapi.fileEditor.FileEditorManagerListener; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.SimpleToolWindowPanel; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.BrowserHyperlinkListener; +import com.intellij.util.ui.JBUI; +import com.jetbrains.edu.courseFormat.Task; +import com.jetbrains.edu.courseFormat.TaskFile; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.actions.*; +import com.jetbrains.edu.learning.editor.StudyEditor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; + +public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable { + + public StudyToolWindow(final Project project) { + super(true, true); + JPanel toolbarPanel = createToolbarPanel(); + setToolbar(toolbarPanel); + + final StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); + if (studyEditor == null) return; + Task task = studyEditor.getTaskFile().getTask(); + + if (task != null) { + final String taskText = task.getText(); + + final JTextPane taskTextPane = new JTextPane(); + taskTextPane.setContentType("text/html"); + taskTextPane.setEditable(false); + taskTextPane.setText(taskText); + taskTextPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); + taskTextPane.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground()); + taskTextPane.setBorder(new EmptyBorder(15, 20, 0, 100)); + setContent(taskTextPane); + + final FileEditorManagerListener listener = new StudyFileEditorManagerListener(project, taskTextPane); + project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener); + } + + } + + public void dispose() { + } + + private static JPanel createToolbarPanel() { + final DefaultActionGroup group = new DefaultActionGroup(); + group.add(new StudyCheckAction()); + group.add(new StudyPreviousStudyTaskAction()); + group.add(new StudyNextStudyTaskAction()); + group.add(new StudyRefreshTaskFileAction()); + group.add(new StudyShowHintAction()); + + group.add(new StudyRunAction()); + group.add(new StudyEditInputAction()); + + final ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar("Study", group, true); + return JBUI.Panels.simplePanel(actionToolBar.getComponent()); + } + + static class StudyFileEditorManagerListener implements FileEditorManagerListener { + private Project myProject; + private JTextPane myTaskTextPane; + + StudyFileEditorManagerListener(@NotNull final Project project, JTextPane taskTextPane){ + myProject = project; + myTaskTextPane = taskTextPane; + } + @Override + public void fileOpened (@NotNull FileEditorManager source, @NotNull VirtualFile file){ + Task task = getTask(file); + setTaskText(task); + } + + @Override + public void fileClosed (@NotNull FileEditorManager source, @NotNull VirtualFile file){ + } + + @Override + public void selectionChanged (@NotNull FileEditorManagerEvent event){ + VirtualFile file = event.getNewFile(); + if (file != null) { + Task task = getTask(file); + setTaskText(task); + } + } + + @Nullable + private Task getTask (@NotNull VirtualFile file){ + TaskFile taskFile = StudyUtils.getTaskFile(myProject, file); + if (taskFile != null) { + return taskFile.getTask(); + } + else { + return null; + } + } + + private void setTaskText(@Nullable final Task task) { + if (task == null) { + return; + } + String text = task.getText(); + myTaskTextPane.setText(text); + } + } +} diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java index 0539c68a9965..4522064be221 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java @@ -2,90 +2,31 @@ package com.jetbrains.edu.learning.ui; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.intellij.ui.content.Content; -import com.intellij.ui.content.ContentFactory; -import com.intellij.util.ui.UIUtil; +import com.intellij.ui.content.ContentManager; import com.jetbrains.edu.courseFormat.Course; -import com.jetbrains.edu.courseFormat.Lesson; -import com.jetbrains.edu.courseFormat.Task; import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.courseFormat.StudyStatus; import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import java.awt.*; -import java.util.List; - public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware { - public static final String STUDY_TOOL_WINDOW = "Course Description"; + public static final String STUDY_TOOL_WINDOW = "Task Description"; @Override public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { - JPanel contentPanel = new JPanel(); StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - if (taskManager.getCourse() != null) { - contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS)); - contentPanel.add(Box.createRigidArea(new Dimension(10, 0))); + final Course course = taskManager.getCourse(); + if (course != null) { + final StudyToolWindow studyToolWindow = new StudyToolWindow(project); + final ContentManager contentManager = toolWindow.getContentManager(); + final Content content = contentManager.getFactory().createContent(studyToolWindow, null, false); + contentManager.addContent(content); + Disposer.register(project, studyToolWindow); - Course course = taskManager.getCourse(); - if (course == null) { - return; - } - String courseName = UIUtil.toHtml("

" + course.getName() + "

", 10); - String description = UIUtil.toHtml(course.getDescription(), 5); - String authorLabel = UIUtil.toHtml("Author: " + Course.getAuthorsString(course.getAuthors()), 5); - contentPanel.add(new JLabel(courseName)); - 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))); - int taskNum = 0; - int taskSolved = 0; - int lessonsCompleted = 0; - List lessons = course.getLessons(); - for (Lesson lesson : lessons) { - StudyStatus status = taskManager.getStatus(lesson); - if (status == StudyStatus.Solved) { - lessonsCompleted++; - } - taskNum += lesson.getTaskList().size(); - taskSolved += getSolvedTasks(lesson, taskManager); - } - String completedLessons = String.format("%d of %d lessons completed", lessonsCompleted, course.getLessons().size()); - String completedTasks = String.format("%d of %d tasks completed", taskSolved, taskNum); - String tasksLeft = String.format("%d of %d tasks left", taskNum - taskSolved, taskNum); - contentPanel.add(Box.createVerticalStrut(10)); - addStatistics(completedLessons, contentPanel); - addStatistics(completedTasks, contentPanel); - - double percent = (taskSolved * 100.0) / taskNum; - contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); - StudyProgressBar studyProgressBar = new StudyProgressBar(percent / 100, 40, 10); - contentPanel.add(studyProgressBar); - addStatistics(tasksLeft, contentPanel); - ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); - Content content = contentFactory.createContent(contentPanel, "", true); - toolWindow.getContentManager().addContent(content); } } - private static int getSolvedTasks(@NotNull final Lesson lesson, StudyTaskManager taskManager) { - int solved = 0; - for (Task task : lesson.getTaskList()) { - if (taskManager.getStatus(task) == StudyStatus.Solved) { - solved += 1; - } - } - return solved; - } - - private static void addStatistics(String statistics, JPanel contentPanel) { - String labelText = UIUtil.toHtml(statistics, 5); - contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); - JLabel statisticLabel = new JLabel(labelText); - contentPanel.add(statisticLabel); - } } diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyLightProjectDescriptor.java b/python/testSrc/com/jetbrains/python/fixtures/PyLightProjectDescriptor.java index ed60251e0932..f528f229ddaa 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyLightProjectDescriptor.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyLightProjectDescriptor.java @@ -16,10 +16,8 @@ package com.jetbrains.python.fixtures; import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.Library; @@ -34,7 +32,7 @@ import org.jetbrains.annotations.NotNull; * Project descriptor (extracted from {@link com.jetbrains.python.fixtures.PyTestCase}) and should be used with it. * @author Ilya.Kazakevich */ -public class PyLightProjectDescriptor implements LightProjectDescriptor { +public class PyLightProjectDescriptor extends LightProjectDescriptor { private final String myPythonVersion; public PyLightProjectDescriptor(String pythonVersion) { @@ -52,10 +50,6 @@ public class PyLightProjectDescriptor implements LightProjectDescriptor { return PythonMockSdk.findOrCreate(myPythonVersion); } - @Override - public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { - } - protected void createLibrary(ModifiableRootModel model, final String name, final String path) { final Library.ModifiableModel modifiableModel = model.getModuleLibraryTable().createLibrary(name).getModifiableModel(); final VirtualFile home =