From 1c6c95174fa2bd92d484a3e9eab8920165b9f926 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Sat, 16 Aug 2014 16:41:29 +0400 Subject: [PATCH 01/24] Initial commit --- CourseCreator.iml | 14 ++ META-INF/plugin.xml | 51 +++++++ .../fileTemplates/internal/task.html.ft | 4 + .../fileTemplates/internal/task.py.ft | 1 + .../fileTemplates/internal/test_helper.py.ft | 127 ++++++++++++++++++ .../fileTemplates/internal/tests.py.ft | 17 +++ .../coursecreator/CCNewProjectPanel.form | 65 +++++++++ .../coursecreator/CCNewProjectPanel.java | 39 ++++++ .../coursecreator/CCProjectGenerator.java | 85 ++++++++++++ .../coursecreator/CCProjectService.java | 66 +++++++++ .../coursecreator/actions/AddTaskWindow.java | 98 ++++++++++++++ .../coursecreator/actions/CreateLesson.java | 85 ++++++++++++ .../coursecreator/actions/CreateTask.java | 118 ++++++++++++++++ .../plugins/coursecreator/format/Course.java | 52 +++++++ .../plugins/coursecreator/format/Lesson.java | 34 +++++ .../plugins/coursecreator/format/Task.java | 25 ++++ .../coursecreator/format/TaskFile.java | 16 +++ .../coursecreator/format/TaskWindow.java | 26 ++++ 18 files changed, 923 insertions(+) create mode 100644 CourseCreator.iml create mode 100644 META-INF/plugin.xml create mode 100644 pluginResources/fileTemplates/internal/task.html.ft create mode 100644 pluginResources/fileTemplates/internal/task.py.ft create mode 100644 pluginResources/fileTemplates/internal/test_helper.py.ft create mode 100644 pluginResources/fileTemplates/internal/tests.py.ft create mode 100644 src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form create mode 100644 src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java create mode 100644 src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java create mode 100644 src/org/jetbrains/plugins/coursecreator/CCProjectService.java create mode 100644 src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java create mode 100644 src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java create mode 100644 src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java create mode 100644 src/org/jetbrains/plugins/coursecreator/format/Course.java create mode 100644 src/org/jetbrains/plugins/coursecreator/format/Lesson.java create mode 100644 src/org/jetbrains/plugins/coursecreator/format/Task.java create mode 100644 src/org/jetbrains/plugins/coursecreator/format/TaskFile.java create mode 100644 src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java diff --git a/CourseCreator.iml b/CourseCreator.iml new file mode 100644 index 000000000000..353f005c1d79 --- /dev/null +++ b/CourseCreator.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml new file mode 100644 index 000000000000..cc37d9330f98 --- /dev/null +++ b/META-INF/plugin.xml @@ -0,0 +1,51 @@ + + org.jetbrains.plugins.coursecreator + Cource Creator for PyCharm Education + 1.0 + + + + + + + + + + + + + com.intellij.modules.python + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pluginResources/fileTemplates/internal/task.html.ft b/pluginResources/fileTemplates/internal/task.html.ft new file mode 100644 index 000000000000..f6833496d1c1 --- /dev/null +++ b/pluginResources/fileTemplates/internal/task.html.ft @@ -0,0 +1,4 @@ + +Write your task text here. +
+ \ No newline at end of file diff --git a/pluginResources/fileTemplates/internal/task.py.ft b/pluginResources/fileTemplates/internal/task.py.ft new file mode 100644 index 000000000000..0256e108786c --- /dev/null +++ b/pluginResources/fileTemplates/internal/task.py.ft @@ -0,0 +1 @@ +# TODO: type solution here \ No newline at end of file diff --git a/pluginResources/fileTemplates/internal/test_helper.py.ft b/pluginResources/fileTemplates/internal/test_helper.py.ft new file mode 100644 index 000000000000..1182b7815c06 --- /dev/null +++ b/pluginResources/fileTemplates/internal/test_helper.py.ft @@ -0,0 +1,127 @@ +import sys + +def get_file_text(path): + """ get file text by path""" + file_io = open(path, "r") + text = file_io.read() + file_io.close() + return text + +def get_file_output(path): + # TODO: get file output by path + return "" + +def test_file_importable(): + """ tests there is no obvious syntax errors""" + path = sys.argv[-1] + try: + import_file(path) + except ImportError: + failed("File contains syntax errors") + return + except SyntaxError: + failed("File contains syntax errors") + return + except NameError: + failed("File contains syntax errors") + return + + passed() + +def import_file(path): + """ returns imported file """ + import imp + tmp = imp.load_source('tmp', path) + return tmp + +def import_task_file(): + """ returns imported file """ + path = sys.argv[-1] + return import_file(path) + +def test_is_not_empty(): + path = sys.argv[-1] + file_text = get_file_text(path) + + if len(file_text) > 0: + passed() + else: + failed("The file is empty. Please, reload the task and try again.") + +def test_is_initial_text(error_text="You should modify the file"): + path = sys.argv[-1] + text = get_initial_text(path) + file_text = get_file_text(path) + + if file_text.strip() == text: + failed(error_text) + else: + passed() + +def get_initial_text(path): + course_lib = sys.argv[-2] + + import os + # path format is "project_root/lessonX/taskY/file.py" + task_index = path.rfind(os.sep, 0, path.rfind(os.sep)) + index = path.rfind(os.sep, 0, task_index) + relative_path = path[index+1:] + initial_file_path = os.path.join(course_lib, relative_path) + return get_file_text(initial_file_path) + + +def test_text_equals(text, error_text): + path = sys.argv[-1] + file_text = get_file_text(path) + + if file_text.strip() == text: + passed() + else: + failed(error_text) + +def test_window_text_deleted(error_text="Don't just delete task text"): + windows = get_task_windows() + + for window in windows: + if len(window) == 0: + failed(error_text) + return + passed() + + +def failed(message="Please, reload the task and try again."): + print("#study_plugin FAILED + " + message) + +def passed(): + print("#study_plugin test OK") + +def get_task_windows(): + prefix = "#study_plugin_window = " + path = sys.argv[-1] + import os + windows_path = os.path.splitext(path)[0] + "_windows" + windows = [] + f = open(windows_path, "r") + window_text = "" + first = True + for line in f.readlines(): + if line.startswith(prefix): + if not first: + windows.append(window_text.strip()) + else: + first = False + window_text = line[len(prefix):] + else: + window_text += line + + if window_text: + windows.append(window_text.strip()) + + f.close() + return windows + +def run_common_tests(error_text="Please, reload file and try again"): + test_file_importable() + test_is_not_empty() + test_is_initial_text(error_text) + test_window_text_deleted(error_text) \ No newline at end of file diff --git a/pluginResources/fileTemplates/internal/tests.py.ft b/pluginResources/fileTemplates/internal/tests.py.ft new file mode 100644 index 000000000000..2e6fd4c78eed --- /dev/null +++ b/pluginResources/fileTemplates/internal/tests.py.ft @@ -0,0 +1,17 @@ +from test_helper import run_common_tests, failed, passed, get_task_windows + + +def test_task_windows(): + windows = get_task_windows() + window = windows[0] + if window != "": # TODO: your condition here + passed() + else: + failed() + + +if __name__ == '__main__': + run_common_tests() + test_task_windows() + + diff --git a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form b/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form new file mode 100644 index 000000000000..f01368ad25e5 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form @@ -0,0 +1,65 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java b/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java new file mode 100644 index 000000000000..709f61082817 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java @@ -0,0 +1,39 @@ +package org.jetbrains.plugins.coursecreator; + +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class CCNewProjectPanel { + private JPanel myPanel; + private JTextArea myDescription; + private JTextField myName; + private JTextField myAuthorField; + + + public CCNewProjectPanel() { + final String userName = System.getProperty("user.name"); + if (userName != null) + myAuthorField.setText(userName); + } + + public JPanel getMainPanel() { + return myPanel; + } + + @NotNull + public String getName() { + return StringUtil.notNullize(myName.getText()); + } + + @NotNull + public String getDescription() { + return StringUtil.notNullize(myDescription.getText()); + } + + @NotNull + public String getAuthor() { + return StringUtil.notNullize(myAuthorField.getText()); + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java b/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java new file mode 100644 index 000000000000..9cae302680d1 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java @@ -0,0 +1,85 @@ +package org.jetbrains.plugins.coursecreator; + +import com.intellij.facet.ui.ValidationResult; +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.ide.util.DirectoryUtil; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.DirectoryProjectGenerator; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiManager; +import com.jetbrains.python.newProject.PythonProjectGenerator; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.format.Course; + +import javax.swing.*; + + +public class CCProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator { + private CCNewProjectPanel mySettingsPanel; + + @Nls + @NotNull + @Override + public String getName() { + return "Course creation"; + } + + @Nullable + @Override + public Object showGenerationSettings(VirtualFile baseDir) throws ProcessCanceledException { + return null; + } + + @Nullable + @Override + public Icon getLogo() { + return null; + } + + + @Override + public void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir, + @Nullable Object settings, @NotNull Module module) { + + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = new Course(mySettingsPanel.getName(), mySettingsPanel.getAuthor(), mySettingsPanel.getDescription()); + service.setCourse(course); + + final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(baseDir); + if (projectDir == null) return; + new WriteCommandAction.Simple(project) { + @Override + protected void run() throws Throwable { + final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate("test_helper"); + try { + FileTemplateUtil.createFromTemplate(template, "test_helper.py", null, projectDir); + } + catch (Exception ignored) { + } + DirectoryUtil.createSubdirectories("hints", projectDir, "\\/"); + } + }.execute(); + + } + + @NotNull + @Override + public ValidationResult validate(@NotNull String s) { + return ValidationResult.OK; + } + + @Nullable + @Override + public JPanel extendBasePanel() throws ProcessCanceledException { + mySettingsPanel = new CCNewProjectPanel(); + return mySettingsPanel.getMainPanel(); + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectService.java b/src/org/jetbrains/plugins/coursecreator/CCProjectService.java new file mode 100644 index 000000000000..59226d1901b6 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectService.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.coursecreator; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; +import com.intellij.util.xmlb.XmlSerializer; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.coursecreator.format.Course; + +@State(name = "CCProjectService", + storages = { + @Storage(file = "$PROJECT_CONFIG_DIR$/course_service.xml") + } +) +public class CCProjectService implements PersistentStateComponent { + + public Course myCourse; + public static final String COURSE_ELEMENT = "course"; + + public void setCourse(@NotNull final Course course) { + myCourse = course; + } + + public Course getCourse() { + return myCourse; + } + + @Override + public Element getState() { + final Element el = new Element("CCProjectService"); + if (myCourse != null) { + Element courseElement = new Element(COURSE_ELEMENT); + XmlSerializer.serializeInto(myCourse, courseElement); + el.addContent(courseElement); + } + return el; + } + + @Override + public void loadState(Element el) { + myCourse = XmlSerializer.deserialize(el.getChild(COURSE_ELEMENT), Course.class); + } + + public static CCProjectService getInstance(@NotNull Project project) { + return ServiceManager.getService(project, CCProjectService.class); + } + +} diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java new file mode 100644 index 000000000000..5e0b0d9fd72a --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -0,0 +1,98 @@ +package org.jetbrains.plugins.coursecreator.actions; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.SelectionModel; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.*; + +public class AddTaskWindow extends AnAction implements DumbAware { + public AddTaskWindow() { + super("Add task window","Add task window", null); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final Project project = e.getData(CommonDataKeys.PROJECT); + if (project == null) { + return; + } + final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); + if (file == null) return; + final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); + if (editor == null) return; + + final SelectionModel model = editor.getSelectionModel(); + final Document document = PsiDocumentManager.getInstance(project).getDocument(file); + if (document == null) return; + final int start = model.getSelectionStart(); + final int end = model.getSelectionEnd(); + final int lineNumber = document.getLineNumber(start); + final int length = end - start; + int realStart = start - document.getLineStartOffset(lineNumber); + + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final PsiDirectory taskDir = file.getContainingDirectory(); + final PsiDirectory lessonDir = taskDir.getParent(); + if (lessonDir == null) return; + + final Lesson lesson = course.getLesson(lessonDir.getName()); + final Task task = lesson.getTask(taskDir.getName()); + final TaskFile taskFile = task.getTaskFile(file.getName()); + + final String taskText = Messages.showMultilineInputDialog(project, "Add window task text", "Task Window Text", "", null, null); + + taskFile.addTaskWindow(new TaskWindow(lineNumber, realStart, length)); + } + + @Override + public void update(AnActionEvent event) { + final Presentation presentation = event.getPresentation(); + final Project project = event.getData(CommonDataKeys.PROJECT); + if (project == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + final Editor editor = CommonDataKeys.EDITOR.getData(event.getDataContext()); + final PsiFile file = CommonDataKeys.PSI_FILE.getData(event.getDataContext()); + if (editor == null || file == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + if (!editor.getSelectionModel().hasSelection()) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final PsiDirectory taskDir = file.getContainingDirectory(); + final PsiDirectory lessonDir = taskDir.getParent(); + if (lessonDir == null) return; + + final Lesson lesson = course.getLesson(lessonDir.getName()); + final Task task = lesson.getTask(taskDir.getName()); + if (task == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + presentation.setVisible(true); + presentation.setEnabled(true); + + } +} \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java new file mode 100644 index 000000000000..25438e7618a1 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java @@ -0,0 +1,85 @@ +package org.jetbrains.plugins.coursecreator.actions; + +import com.intellij.ide.IdeView; +import com.intellij.ide.util.DirectoryChooserUtil; +import com.intellij.ide.util.DirectoryUtil; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.psi.PsiDirectory; +import com.intellij.util.PlatformIcons; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.Course; +import org.jetbrains.plugins.coursecreator.format.Lesson; + +public class CreateLesson extends AnAction implements DumbAware { + public CreateLesson() { + super("Lesson", "Create new Lesson", PlatformIcons.DIRECTORY_CLOSED_ICON); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final IdeView view = e.getData(LangDataKeys.IDE_VIEW); + final Project project = e.getData(CommonDataKeys.PROJECT); + + if (view == null || project == null) { + return; + } + final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); + if (directory == null) return; + + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final int size = course.getLessons().size(); + final String lessonName = Messages.showInputDialog("Name:", "Lesson Name", null, "lesson" + (size+1), null); + if (lessonName == null) return; + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + final PsiDirectory lessonDirectory = DirectoryUtil.createSubdirectories("lesson" + (size+1), directory, "\\/"); + if (lessonDirectory != null) { + view.selectElement(lessonDirectory); + final Lesson lesson = new Lesson(lessonName); + course.addLesson(lesson, lessonDirectory); + } + } + }); + } + + @Override + public void update(AnActionEvent event) { + final Presentation presentation = event.getPresentation(); + final Project project = event.getData(CommonDataKeys.PROJECT); + if (project == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + + final IdeView view = event.getData(LangDataKeys.IDE_VIEW); + if (view == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + + final PsiDirectory[] directories = view.getDirectories(); + if (directories.length == 0) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); + if (directory != null && !project.getBaseDir().equals(directory.getVirtualFile())) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + presentation.setVisible(true); + presentation.setEnabled(true); + + } +} \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java new file mode 100644 index 000000000000..69a5e5d19489 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java @@ -0,0 +1,118 @@ +package org.jetbrains.plugins.coursecreator.actions; + +import com.intellij.ide.IdeView; +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.ide.util.DirectoryChooserUtil; +import com.intellij.ide.util.DirectoryUtil; +import com.intellij.ide.util.EditorHelper; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.util.PlatformIcons; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.Course; +import org.jetbrains.plugins.coursecreator.format.Lesson; +import org.jetbrains.plugins.coursecreator.format.Task; + +public class CreateTask extends AnAction implements DumbAware { + public CreateTask() { + super("Task", "Create new Task", PlatformIcons.DIRECTORY_CLOSED_ICON); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final IdeView view = e.getData(LangDataKeys.IDE_VIEW); + final Project project = e.getData(CommonDataKeys.PROJECT); + + if (view == null || project == null) { + return; + } + final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); + + if (directory == null) return; + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final Lesson lesson = course.getLesson(directory.getName()); + final int size = lesson.getTasklist().size(); + + final String taskName = Messages.showInputDialog("Name:", "Task Name", null, "task" + (size + 1), null); + if (taskName == null) return; + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + final PsiDirectory taskDirectory = DirectoryUtil.createSubdirectories("task" + (size + 1), directory, "\\/"); + if (taskDirectory != null) { + final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate("task.html"); + final FileTemplate testsTemplate = FileTemplateManager.getInstance().getInternalTemplate("tests"); + final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.py"); + try { + final PsiElement taskFile = FileTemplateUtil.createFromTemplate(template, "task.html", null, taskDirectory); + final PsiElement testsFile = FileTemplateUtil.createFromTemplate(testsTemplate, "tests.py", null, taskDirectory); + final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskName + ".py", null, taskDirectory); + + final Task task = new Task(taskName); + task.addTaskFile(taskPyFile.getContainingFile().getName()); + lesson.addTask(task, taskDirectory); + + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + EditorHelper.openInEditor(testsFile, false); + EditorHelper.openInEditor(taskPyFile, false); + view.selectElement(taskFile); + } + }); + } + catch (Exception ignored) { + } + + + } + } + }); + } + + @Override + public void update(AnActionEvent event) { + final Presentation presentation = event.getPresentation(); + final Project project = event.getData(CommonDataKeys.PROJECT); + if (project == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + + final IdeView view = event.getData(LangDataKeys.IDE_VIEW); + if (view == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + + final PsiDirectory[] directories = view.getDirectories(); + if (directories.length == 0) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + if (course != null && directory != null && course.getLesson(directory.getName()) == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + + presentation.setVisible(true); + presentation.setEnabled(true); + + } +} \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/Course.java b/src/org/jetbrains/plugins/coursecreator/format/Course.java new file mode 100644 index 000000000000..b5858dbc34d3 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/format/Course.java @@ -0,0 +1,52 @@ +package org.jetbrains.plugins.coursecreator.format; + +import com.intellij.psi.PsiDirectory; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Course { + public List lessons = new ArrayList(); + public String description; + public String name; + public String author; + public Map myLessonsMap = new HashMap(); + + public Map getLessonsMap() { + return myLessonsMap; + } + + public Lesson getLesson(@NotNull final String name) { + return myLessonsMap.get(name); + } + + + public Course() { + } + + public Course(@NotNull final String name, @NotNull final String author, @NotNull final String description) { + this.description = description; + this.name = name; + this.author = author; + } + + public List getLessons() { + return lessons; + } + + public void addLesson(@NotNull final Lesson lesson, @NotNull final PsiDirectory directory) { + lessons.add(lesson); + myLessonsMap.put(directory.getName(), lesson); + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/format/Lesson.java b/src/org/jetbrains/plugins/coursecreator/format/Lesson.java new file mode 100644 index 000000000000..0e7d031d35c2 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/format/Lesson.java @@ -0,0 +1,34 @@ +package org.jetbrains.plugins.coursecreator.format; + +import com.intellij.psi.PsiDirectory; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Lesson { + public String name; + public List task_list = new ArrayList(); + public Map myTasksMap = new HashMap(); + + public Lesson() {} + + public Lesson(@NotNull final String name) { + this.name = name; + } + + public void addTask(@NotNull final Task task, PsiDirectory taskDirectory) { + myTasksMap.put(taskDirectory.getName(), task); + task_list.add(task); + } + + public Task getTask(@NotNull final String name) { + return myTasksMap.get(name); + } + + public List getTasklist() { + return task_list; + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/format/Task.java b/src/org/jetbrains/plugins/coursecreator/format/Task.java new file mode 100644 index 000000000000..09a89ac466e4 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/format/Task.java @@ -0,0 +1,25 @@ +package org.jetbrains.plugins.coursecreator.format; + +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public class Task { + public String name; + public Map taskFiles = new HashMap(); + + public Task() {} + + public Task(@NotNull final String name) { + this.name = name; + } + + public void addTaskFile(@NotNull final String name) { + taskFiles.put(name, new TaskFile()); + } + + public TaskFile getTaskFile(@NotNull final String name) { + return taskFiles.get(name); + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java new file mode 100644 index 000000000000..022f28bfe1f3 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java @@ -0,0 +1,16 @@ +package org.jetbrains.plugins.coursecreator.format; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class TaskFile { + public List task_windows = new ArrayList(); + + public TaskFile() {} + + public void addTaskWindow(@NotNull final TaskWindow taskWindow) { + task_windows.add(taskWindow); + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java new file mode 100644 index 000000000000..8338767cd449 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -0,0 +1,26 @@ +package org.jetbrains.plugins.coursecreator.format; + +import org.jetbrains.annotations.NotNull; + +public class TaskWindow { + + public int line = 0; + public int start = 0; + public String hint = ""; + public String possible_answer = ""; + public int length = 0; + + public TaskWindow() {} + + public TaskWindow(int line, int start, int length) { + + } + + public TaskWindow(int line, int start, @NotNull final String hint, @NotNull final String possible_answer, int length) { + this.line = line; + this.start = start; + this.hint = hint; + this.possible_answer = possible_answer; + this.length = length; + } +} \ No newline at end of file From 84da732d4e3d5d55cf4663a02eca4f32bf7b81d4 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 19 Aug 2014 12:38:23 +0400 Subject: [PATCH 02/24] added json generation action --- META-INF/plugin.xml | 2 + .../coursecreator/actions/GenerateJson.java | 67 +++++++++++++++++++ .../plugins/coursecreator/format/Course.java | 11 +-- .../plugins/coursecreator/format/Lesson.java | 8 ++- .../plugins/coursecreator/format/Task.java | 5 +- .../coursecreator/format/TaskFile.java | 3 +- .../coursecreator/format/TaskWindow.java | 11 +-- 7 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index cc37d9330f98..34cc73c47145 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -46,6 +46,8 @@ + + \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java b/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java new file mode 100644 index 000000000000..13eb904efcc1 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java @@ -0,0 +1,67 @@ +package org.jetbrains.plugins.coursecreator.actions; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.Course; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +public class GenerateJson extends AnAction implements DumbAware { + private static final Logger LOG = Logger.getInstance(GenerateJson.class.getName()); + public GenerateJson() { + super("Generate course Json","Generate course Json", null); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final Project project = e.getData(CommonDataKeys.PROJECT); + if (project == null) { + return; + } + + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); + final String json = gson.toJson(course); + final File courseJson = new File(project.getBasePath(), "course.json"); + FileWriter writer = null; + try { + writer = new FileWriter(courseJson); + writer.write(json); + } catch (IOException e1) { + LOG.error(e); + } + finally { + try { + if (writer != null) + writer.close(); + } catch (IOException e1) { + LOG.error(e1); + } + } + } + + @Override + public void update(AnActionEvent event) { + final Presentation presentation = event.getPresentation(); + final Project project = event.getData(CommonDataKeys.PROJECT); + if (project == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + presentation.setVisible(true); + presentation.setEnabled(true); + + } +} \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/Course.java b/src/org/jetbrains/plugins/coursecreator/format/Course.java index b5858dbc34d3..eb62d59cd9b1 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/Course.java +++ b/src/org/jetbrains/plugins/coursecreator/format/Course.java @@ -1,5 +1,6 @@ package org.jetbrains.plugins.coursecreator.format; +import com.google.gson.annotations.Expose; import com.intellij.psi.PsiDirectory; import org.jetbrains.annotations.NotNull; @@ -9,10 +10,12 @@ import java.util.List; import java.util.Map; public class Course { - public List lessons = new ArrayList(); - public String description; - public String name; - public String author; + @Expose public List lessons = new ArrayList(); + @Expose public String description; + + @Expose public String name; + @Expose public String author; + public Map myLessonsMap = new HashMap(); public Map getLessonsMap() { diff --git a/src/org/jetbrains/plugins/coursecreator/format/Lesson.java b/src/org/jetbrains/plugins/coursecreator/format/Lesson.java index 0e7d031d35c2..703a10f71fb4 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/Lesson.java +++ b/src/org/jetbrains/plugins/coursecreator/format/Lesson.java @@ -1,5 +1,6 @@ package org.jetbrains.plugins.coursecreator.format; +import com.google.gson.annotations.Expose; import com.intellij.psi.PsiDirectory; import org.jetbrains.annotations.NotNull; @@ -9,9 +10,10 @@ import java.util.List; import java.util.Map; public class Lesson { - public String name; - public List task_list = new ArrayList(); - public Map myTasksMap = new HashMap(); + @Expose public String name; + @Expose public List task_list = new ArrayList(); + + private Map myTasksMap = new HashMap(); public Lesson() {} diff --git a/src/org/jetbrains/plugins/coursecreator/format/Task.java b/src/org/jetbrains/plugins/coursecreator/format/Task.java index 09a89ac466e4..6dcbc4b0814d 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/Task.java +++ b/src/org/jetbrains/plugins/coursecreator/format/Task.java @@ -1,13 +1,14 @@ package org.jetbrains.plugins.coursecreator.format; +import com.google.gson.annotations.Expose; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; public class Task { - public String name; - public Map taskFiles = new HashMap(); + @Expose public String name; + @Expose public Map taskFiles = new HashMap(); public Task() {} diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java index 022f28bfe1f3..b9f965cfa866 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java @@ -1,12 +1,13 @@ package org.jetbrains.plugins.coursecreator.format; +import com.google.gson.annotations.Expose; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; public class TaskFile { - public List task_windows = new ArrayList(); + @Expose public List task_windows = new ArrayList(); public TaskFile() {} diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 8338767cd449..9d687f4c9fd8 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -1,14 +1,15 @@ package org.jetbrains.plugins.coursecreator.format; +import com.google.gson.annotations.Expose; import org.jetbrains.annotations.NotNull; public class TaskWindow { - public int line = 0; - public int start = 0; - public String hint = ""; - public String possible_answer = ""; - public int length = 0; + @Expose public int line = 0; + @Expose public int start = 0; + @Expose public String hint = ""; + @Expose public String possible_answer = ""; + @Expose public int length = 0; public TaskWindow() {} From ceb278c9ce642625a31ded830bad3292af004c51 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 19 Aug 2014 13:21:50 +0400 Subject: [PATCH 03/24] added gutter for task text --- pluginResources/icons/gutter.png | Bin 0 -> 500 bytes .../coursecreator/actions/AddTaskWindow.java | 12 ++++- .../actions/ShowTaskWindowText.java | 32 ++++++++++++ .../plugins/coursecreator/format/Lesson.java | 2 +- .../coursecreator/format/TaskWindow.java | 9 ++++ .../highlighting/TaskTextGutter.java | 48 ++++++++++++++++++ 6 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 pluginResources/icons/gutter.png create mode 100644 src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java create mode 100644 src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java diff --git a/pluginResources/icons/gutter.png b/pluginResources/icons/gutter.png new file mode 100644 index 0000000000000000000000000000000000000000..244e6ca045c50a130086ac388b560a12761544b4 GIT binary patch literal 500 zcmV z!-sF!^gVb+8rtpyctW0#N6uWni0LCt_6PoOdbjll_d4>B|?abUmpo8>v>h}Zj|Ya;Eu#qwvU1IVc9khP8VrtAsT2=e83P~$#!xXbw)n}FlPSEe7Hq1uCb zR8w;xqmBrUgA^pnkB=O@-lq0DPz$ay0yh_~I_IDpzxRb(4=Iy9CT||k!08w)Pe>W4 zElmH8fF;68$GMwZ#7{4ozI(ySrR%I+xs4-G1q^UxnUV7rlf9>Rn&_6Wike0000 task_list = new ArrayList(); - private Map myTasksMap = new HashMap(); + public Map myTasksMap = new HashMap(); public Lesson() {} diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 9d687f4c9fd8..7f7c9cbe000a 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -10,6 +10,7 @@ public class TaskWindow { @Expose public String hint = ""; @Expose public String possible_answer = ""; @Expose public int length = 0; + public String myTaskText = ""; public TaskWindow() {} @@ -17,6 +18,14 @@ public class TaskWindow { } + public void setTaskText(@NotNull final String taskText) { + myTaskText = taskText; + } + + public String getTaskText() { + return myTaskText; + } + public TaskWindow(int line, int start, @NotNull final String hint, @NotNull final String possible_answer, int length) { this.line = line; this.start = start; diff --git a/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java b/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java new file mode 100644 index 000000000000..3c76266821fb --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java @@ -0,0 +1,48 @@ +package org.jetbrains.plugins.coursecreator.highlighting; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.editor.markup.GutterIconRenderer; +import com.intellij.openapi.util.IconLoader; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.actions.ShowTaskWindowText; +import org.jetbrains.plugins.coursecreator.format.TaskWindow; + +import javax.swing.*; + +public class TaskTextGutter extends GutterIconRenderer { + @NotNull + private final TaskWindow myTaskWindow; + + public TaskTextGutter(@NotNull final TaskWindow taskWindow) { + myTaskWindow = taskWindow; + } + + @NotNull + @Override + public Icon getIcon() { + return IconLoader.getIcon("/icons/gutter.png"); + } + + @Override + public boolean equals(Object o) { + return this == o || o instanceof TaskTextGutter + && myTaskWindow.getTaskText().equals(((TaskTextGutter) o).getTaskWindow().getTaskText()); + } + + @NotNull + public TaskWindow getTaskWindow() { + return myTaskWindow; + } + + @Override + public int hashCode() { + return myTaskWindow.hashCode(); + } + + @Nullable + @Override + public AnAction getClickAction() { + return new ShowTaskWindowText(myTaskWindow); + } +} From 3b687ba01d594791153d39b1dddc26f894955633 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 19 Aug 2014 14:21:18 +0400 Subject: [PATCH 04/24] add all created task windows --- META-INF/plugin.xml | 2 +- .../coursecreator/CCTaskWindowsUpdater.java | 62 +++++++++++++++++++ .../coursecreator/format/TaskFile.java | 4 ++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index 34cc73c47145..a6a814b0f4ac 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -25,7 +25,7 @@ - + diff --git a/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java b/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java new file mode 100644 index 000000000000..559ae602b9a9 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java @@ -0,0 +1,62 @@ +package org.jetbrains.plugins.coursecreator; + +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.markup.HighlighterLayer; +import com.intellij.openapi.editor.markup.RangeHighlighter; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupActivity; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.coursecreator.format.*; +import org.jetbrains.plugins.coursecreator.highlighting.TaskTextGutter; + +import java.util.Map; + +public class CCTaskWindowsUpdater implements StartupActivity { + private static final Logger LOG = Logger.getInstance(CCTaskWindowsUpdater.class.getName()); + + @Override + public void runActivity(@NotNull final Project project) { + final Application application = ApplicationManager.getApplication(); + if (application.isUnitTestMode()) { + return; + } + final Course course = CCProjectService.getInstance(project).getCourse(); + final VirtualFile baseDir = project.getBaseDir(); + if (course != null) { + final Map lessonsMap = course.getLessonsMap(); + for (Map.Entry nameLessonEntry : lessonsMap.entrySet()) { + final VirtualFile lessonDir = baseDir.findChild(nameLessonEntry.getKey()); + if (lessonDir == null) continue; + final Map tasksMap = nameLessonEntry.getValue().myTasksMap; + for (Map.Entry nameTaskEntry : tasksMap.entrySet()) { + final VirtualFile taskDir = lessonDir.findChild(nameTaskEntry.getKey()); + if (taskDir == null) continue; + final Map taskFiles = nameTaskEntry.getValue().taskFiles; + for (Map.Entry nameTaskFileEntry : taskFiles.entrySet()) { + final VirtualFile taskFile = taskDir.findChild(nameTaskFileEntry.getKey()); + if (taskFile == null) continue; + + final FileEditor fileEditor = FileEditorManager.getInstance(project).getEditors(taskFile)[0]; + if (fileEditor instanceof TextEditor) { + final Editor editor = ((TextEditor)fileEditor).getEditor(); + final TaskFile value = nameTaskFileEntry.getValue(); + for (TaskWindow taskWindow : value.getTaskWindows()) { + final RangeHighlighter rangeHighlighter = editor.getMarkupModel().addLineHighlighter(taskWindow.line, HighlighterLayer.FIRST, TextAttributes.ERASE_MARKER); + rangeHighlighter.setGutterIconRenderer(new TaskTextGutter(taskWindow)); + } + + } + } + } + } + } + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java index b9f965cfa866..16cc89d23129 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java @@ -14,4 +14,8 @@ public class TaskFile { public void addTaskWindow(@NotNull final TaskWindow taskWindow) { task_windows.add(taskWindow); } + + public List getTaskWindows() { + return task_windows; + } } From e8d44af10a56e70b9f43840ae14edb6ca0282cac Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 19 Aug 2014 15:37:39 +0400 Subject: [PATCH 05/24] use slow line markers --- META-INF/plugin.xml | 2 +- .../coursecreator/CCTaskWindowsUpdater.java | 98 +++++++++++-------- .../coursecreator/actions/AddTaskWindow.java | 6 -- .../actions/DeleteTaskWindowAction.java | 28 ++++++ .../coursecreator/format/TaskWindow.java | 23 ++--- .../highlighting/TaskTextGutter.java | 18 +++- 6 files changed, 108 insertions(+), 67 deletions(-) create mode 100644 src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index a6a814b0f4ac..a6fe4dccc0aa 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -25,7 +25,7 @@ - + diff --git a/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java b/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java index 559ae602b9a9..728c995ec667 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java +++ b/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java @@ -1,60 +1,72 @@ package org.jetbrains.plugins.coursecreator; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.codeHighlighting.Pass; +import com.intellij.codeInsight.daemon.LineMarkerInfo; +import com.intellij.codeInsight.daemon.LineMarkerProvider; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.markup.HighlighterLayer; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.project.Project; -import com.intellij.openapi.startup.StartupActivity; -import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.coursecreator.format.*; import org.jetbrains.plugins.coursecreator.highlighting.TaskTextGutter; -import java.util.Map; +import java.util.Collection; +import java.util.List; -public class CCTaskWindowsUpdater implements StartupActivity { +public class CCTaskWindowsUpdater implements LineMarkerProvider { private static final Logger LOG = Logger.getInstance(CCTaskWindowsUpdater.class.getName()); + @Nullable @Override - public void runActivity(@NotNull final Project project) { - final Application application = ApplicationManager.getApplication(); - if (application.isUnitTestMode()) { - return; - } - final Course course = CCProjectService.getInstance(project).getCourse(); - final VirtualFile baseDir = project.getBaseDir(); - if (course != null) { - final Map lessonsMap = course.getLessonsMap(); - for (Map.Entry nameLessonEntry : lessonsMap.entrySet()) { - final VirtualFile lessonDir = baseDir.findChild(nameLessonEntry.getKey()); + public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) { + return null; + } + + @Override + public void collectSlowLineMarkers(@NotNull List elements, @NotNull final Collection result) { + for (PsiElement element : elements) { + if (element instanceof PsiFile) { + final Project project = element.getProject(); + final Course course = CCProjectService.getInstance(project).getCourse(); + final String taskFileName = ((PsiFile) element).getName(); + final PsiDirectory taskDir = ((PsiFile) element).getParent(); + if (taskDir == null) continue; + final String taskDirName = taskDir.getName(); + final PsiDirectory lessonDir = taskDir.getParentDirectory(); if (lessonDir == null) continue; - final Map tasksMap = nameLessonEntry.getValue().myTasksMap; - for (Map.Entry nameTaskEntry : tasksMap.entrySet()) { - final VirtualFile taskDir = lessonDir.findChild(nameTaskEntry.getKey()); - if (taskDir == null) continue; - final Map taskFiles = nameTaskEntry.getValue().taskFiles; - for (Map.Entry nameTaskFileEntry : taskFiles.entrySet()) { - final VirtualFile taskFile = taskDir.findChild(nameTaskFileEntry.getKey()); - if (taskFile == null) continue; - - final FileEditor fileEditor = FileEditorManager.getInstance(project).getEditors(taskFile)[0]; - if (fileEditor instanceof TextEditor) { - final Editor editor = ((TextEditor)fileEditor).getEditor(); - final TaskFile value = nameTaskFileEntry.getValue(); - for (TaskWindow taskWindow : value.getTaskWindows()) { - final RangeHighlighter rangeHighlighter = editor.getMarkupModel().addLineHighlighter(taskWindow.line, HighlighterLayer.FIRST, TextAttributes.ERASE_MARKER); - rangeHighlighter.setGutterIconRenderer(new TaskTextGutter(taskWindow)); - } - + final String lessonDirName = lessonDir.getName(); + final Lesson lesson = course.getLesson(lessonDirName); + if (lesson == null) continue; + final Task task = lesson.getTask(taskDirName); + final TaskFile taskFile = task.getTaskFile(taskFileName); + if (taskFile == null) continue; + final Document document = PsiDocumentManager.getInstance(project).getDocument((PsiFile) element); + if (document == null) continue; + for (final TaskWindow taskWindow : taskFile.getTaskWindows()) { + if (taskWindow.line > document.getLineCount()) continue; + final int lineStartOffset = document.getLineStartOffset(taskWindow.line); + final int offset = lineStartOffset + taskWindow.start; + if (offset >= document.getTextLength()) continue; + final TextRange textRange = TextRange.create(offset, offset + taskWindow.length); + @SuppressWarnings("unchecked") + final LineMarkerInfo info = new LineMarkerInfo(element, textRange, + IconLoader.getIcon("/icons/gutter.png"), Pass.UPDATE_OVERRIDEN_MARKERS, + null, null, GutterIconRenderer.Alignment.CENTER) { + @Nullable + @Override + public GutterIconRenderer createGutterRenderer() { + return new TaskTextGutter(taskWindow, this); } - } + }; + result.add(info); } } } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java index 0895d1b25a33..8fba2f87d5a5 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -7,9 +7,6 @@ import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; -import com.intellij.openapi.editor.markup.HighlighterLayer; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -19,7 +16,6 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.*; -import org.jetbrains.plugins.coursecreator.highlighting.TaskTextGutter; public class AddTaskWindow extends AnAction implements DumbAware { public AddTaskWindow() { @@ -58,10 +54,8 @@ public class AddTaskWindow extends AnAction implements DumbAware { final String taskText = Messages.showMultilineInputDialog(project, "Add window task text", "Task Window Text", "", null, null); - final RangeHighlighter rangeHighlighter = editor.getMarkupModel().addLineHighlighter(lineNumber, HighlighterLayer.FIRST, TextAttributes.ERASE_MARKER); final TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length); taskWindow.setTaskText(StringUtil.notNullize(taskText)); - rangeHighlighter.setGutterIconRenderer(new TaskTextGutter(taskWindow)); taskFile.addTaskWindow(taskWindow); } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java new file mode 100644 index 000000000000..c745f4e485b2 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java @@ -0,0 +1,28 @@ +package org.jetbrains.plugins.coursecreator.actions; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.coursecreator.format.TaskWindow; + +@SuppressWarnings("ComponentNotRegistered") +public class DeleteTaskWindowAction extends AnAction implements DumbAware { + @NotNull + private final TaskWindow myTaskWindow; + + public DeleteTaskWindowAction(@NotNull final TaskWindow taskWindow) { + super("Delete task window","Delete task window", null); + myTaskWindow = taskWindow; + } + + @Override + public void actionPerformed(AnActionEvent e) { + final Project project = e.getData(PlatformDataKeys.PROJECT); + if (project == null) return; +// myTaskWindow.remove(); + } + +} \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 7f7c9cbe000a..1dedf7ff6374 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -5,17 +5,19 @@ import org.jetbrains.annotations.NotNull; public class TaskWindow { - @Expose public int line = 0; - @Expose public int start = 0; - @Expose public String hint = ""; - @Expose public String possible_answer = ""; - @Expose public int length = 0; - public String myTaskText = ""; + @Expose public int line; + @Expose public int start; + @Expose public String hint; + @Expose public String possible_answer; + @Expose public int length; + public String myTaskText; public TaskWindow() {} public TaskWindow(int line, int start, int length) { - + this.line = line; + this.start = start; + this.length = length; } public void setTaskText(@NotNull final String taskText) { @@ -26,11 +28,4 @@ public class TaskWindow { return myTaskText; } - public TaskWindow(int line, int start, @NotNull final String hint, @NotNull final String possible_answer, int length) { - this.line = line; - this.start = start; - this.hint = hint; - this.possible_answer = possible_answer; - this.length = length; - } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java b/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java index 3c76266821fb..009e9d67134c 100644 --- a/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java +++ b/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java @@ -1,20 +1,24 @@ package org.jetbrains.plugins.coursecreator.highlighting; +import com.intellij.codeInsight.daemon.LineMarkerInfo; +import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.editor.markup.GutterIconRenderer; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.util.IconLoader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.actions.DeleteTaskWindowAction; import org.jetbrains.plugins.coursecreator.actions.ShowTaskWindowText; import org.jetbrains.plugins.coursecreator.format.TaskWindow; import javax.swing.*; -public class TaskTextGutter extends GutterIconRenderer { +public class TaskTextGutter extends LineMarkerInfo.LineMarkerGutterIconRenderer { @NotNull private final TaskWindow myTaskWindow; - public TaskTextGutter(@NotNull final TaskWindow taskWindow) { + public TaskTextGutter(@NotNull final TaskWindow taskWindow, LineMarkerInfo lineMarkerInfo) { + super(lineMarkerInfo); myTaskWindow = taskWindow; } @@ -45,4 +49,12 @@ public class TaskTextGutter extends GutterIconRenderer { public AnAction getClickAction() { return new ShowTaskWindowText(myTaskWindow); } + + @Nullable + @Override + public ActionGroup getPopupMenuActions() { + DefaultActionGroup group = new DefaultActionGroup(); + group.add(new DeleteTaskWindowAction(myTaskWindow)); + return group; + } } From e106b9f3e994ffd9c3e2ed4814ebec707cd35732 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 19 Aug 2014 16:05:37 +0400 Subject: [PATCH 06/24] delete task window action, update line markers --- META-INF/plugin.xml | 2 +- ...ter.java => CCTaskLineMarkerProvider.java} | 6 ++-- .../coursecreator/actions/AddTaskWindow.java | 2 ++ .../actions/DeleteTaskWindowAction.java | 32 +++++++++++++++++-- 4 files changed, 36 insertions(+), 6 deletions(-) rename src/org/jetbrains/plugins/coursecreator/{CCTaskWindowsUpdater.java => CCTaskLineMarkerProvider.java} (93%) diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index a6fe4dccc0aa..623445c51a53 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -25,7 +25,7 @@ - + diff --git a/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java b/src/org/jetbrains/plugins/coursecreator/CCTaskLineMarkerProvider.java similarity index 93% rename from src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java rename to src/org/jetbrains/plugins/coursecreator/CCTaskLineMarkerProvider.java index 728c995ec667..67e3d57179f3 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCTaskWindowsUpdater.java +++ b/src/org/jetbrains/plugins/coursecreator/CCTaskLineMarkerProvider.java @@ -21,8 +21,8 @@ import org.jetbrains.plugins.coursecreator.highlighting.TaskTextGutter; import java.util.Collection; import java.util.List; -public class CCTaskWindowsUpdater implements LineMarkerProvider { - private static final Logger LOG = Logger.getInstance(CCTaskWindowsUpdater.class.getName()); +public class CCTaskLineMarkerProvider implements LineMarkerProvider { + private static final Logger LOG = Logger.getInstance(CCTaskLineMarkerProvider.class.getName()); @Nullable @Override @@ -54,7 +54,7 @@ public class CCTaskWindowsUpdater implements LineMarkerProvider { if (taskWindow.line > document.getLineCount()) continue; final int lineStartOffset = document.getLineStartOffset(taskWindow.line); final int offset = lineStartOffset + taskWindow.start; - if (offset >= document.getTextLength()) continue; + if (offset > document.getTextLength()) continue; final TextRange textRange = TextRange.create(offset, offset + taskWindow.length); @SuppressWarnings("unchecked") final LineMarkerInfo info = new LineMarkerInfo(element, textRange, diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java index 8fba2f87d5a5..d8e55f4bd3e8 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -1,5 +1,6 @@ package org.jetbrains.plugins.coursecreator.actions; +import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -58,6 +59,7 @@ public class AddTaskWindow extends AnAction implements DumbAware { taskWindow.setTaskText(StringUtil.notNullize(taskText)); taskFile.addTaskWindow(taskWindow); + DaemonCodeAnalyzerImpl.getInstance(project).restart(file); } @Override diff --git a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java index c745f4e485b2..49aeb81dc455 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java @@ -1,12 +1,21 @@ package org.jetbrains.plugins.coursecreator.actions; +import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; 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.editor.Document; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.coursecreator.format.TaskWindow; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.*; + +import java.util.List; @SuppressWarnings("ComponentNotRegistered") public class DeleteTaskWindowAction extends AnAction implements DumbAware { @@ -22,7 +31,26 @@ public class DeleteTaskWindowAction extends AnAction implements DumbAware { public void actionPerformed(AnActionEvent e) { final Project project = e.getData(PlatformDataKeys.PROJECT); if (project == null) return; -// myTaskWindow.remove(); + final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); + if (file == null) return; + final Document document = PsiDocumentManager.getInstance(project).getDocument(file); + if (document == null) return; + + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final PsiDirectory taskDir = file.getContainingDirectory(); + final PsiDirectory lessonDir = taskDir.getParent(); + if (lessonDir == null) return; + + final Lesson lesson = course.getLesson(lessonDir.getName()); + final Task task = lesson.getTask(taskDir.getName()); + final TaskFile taskFile = task.getTaskFile(file.getName()); + final List taskWindows = taskFile.getTaskWindows(); + if (taskWindows.contains(myTaskWindow)) { + taskWindows.remove(myTaskWindow); + DaemonCodeAnalyzerImpl.getInstance(project).restart(file); + } + } } \ No newline at end of file From becca60f5fca58fe4a33fc6d4a8780683d7becec Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 19 Aug 2014 16:09:06 +0400 Subject: [PATCH 07/24] moved linemarker provider to the proper place --- META-INF/plugin.xml | 2 +- .../{ => highlighting}/CCTaskLineMarkerProvider.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/org/jetbrains/plugins/coursecreator/{ => highlighting}/CCTaskLineMarkerProvider.java (96%) diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index 623445c51a53..96606bbcc56e 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -25,7 +25,7 @@ - + diff --git a/src/org/jetbrains/plugins/coursecreator/CCTaskLineMarkerProvider.java b/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java similarity index 96% rename from src/org/jetbrains/plugins/coursecreator/CCTaskLineMarkerProvider.java rename to src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java index 67e3d57179f3..feee464a2c93 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCTaskLineMarkerProvider.java +++ b/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java @@ -1,4 +1,4 @@ -package org.jetbrains.plugins.coursecreator; +package org.jetbrains.plugins.coursecreator.highlighting; import com.intellij.codeHighlighting.Pass; import com.intellij.codeInsight.daemon.LineMarkerInfo; @@ -15,8 +15,8 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.*; -import org.jetbrains.plugins.coursecreator.highlighting.TaskTextGutter; import java.util.Collection; import java.util.List; From 6229086cb4acee7ccdb8ca8f5eddacaae4dca354 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 19 Aug 2014 16:52:44 +0400 Subject: [PATCH 08/24] modified project view --- META-INF/plugin.xml | 1 + .../projectView/CCDirectoryNode.java | 63 +++++++++++++++++++ .../projectView/CCTreeStructureProvider.java | 55 ++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java create mode 100644 src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index 96606bbcc56e..477588c8e504 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -26,6 +26,7 @@ + diff --git a/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java b/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java new file mode 100644 index 000000000000..1a7304123f0a --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java @@ -0,0 +1,63 @@ +package org.jetbrains.plugins.coursecreator.projectView; + +import com.intellij.ide.projectView.PresentationData; +import com.intellij.ide.projectView.ViewSettings; +import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDirectory; +import com.intellij.ui.SimpleTextAttributes; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.Course; +import org.jetbrains.plugins.coursecreator.format.Lesson; +import org.jetbrains.plugins.coursecreator.format.Task; + +public class CCDirectoryNode extends PsiDirectoryNode { + private final PsiDirectory myValue; + private final Project myProject; + + public CCDirectoryNode(@NotNull final Project project, + PsiDirectory value, + ViewSettings viewSettings) { + super(project, value, viewSettings); + myValue = value; + myProject = project; + } + + @Override + protected void updateImpl(PresentationData data) { + String valueName = myValue.getName(); + final Course course = CCProjectService.getInstance(myProject).getCourse(); + if (course == null) return; + if (myProject.getBaseDir().equals(myValue.getVirtualFile())) { + data.clearText(); + data.addText(valueName, SimpleTextAttributes.REGULAR_ATTRIBUTES); + data.addText(" (" + course.getName() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); + return; + } + final Lesson lesson = course.getLesson(valueName); + if (lesson != null) { + data.clearText(); + data.addText(valueName, SimpleTextAttributes.REGULAR_ATTRIBUTES); + data.addText(" (" + lesson.name + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); + return; + } + else { + final PsiDirectory parentDir = myValue.getParentDirectory(); + if (parentDir != null) { + final Lesson parentLesson = course.getLesson(parentDir.getName()); + if (parentLesson != null) { + final Task task = parentLesson.getTask(valueName); + if (task != null) { + data.clearText(); + data.addText(valueName, SimpleTextAttributes.REGULAR_ATTRIBUTES); + data.addText(" (" + task.name + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); + return; + } + } + } + } + data.setPresentableText(valueName); + } + +} diff --git a/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java b/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java new file mode 100644 index 000000000000..69b78ec9a92b --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java @@ -0,0 +1,55 @@ +package org.jetbrains.plugins.coursecreator.projectView; + +import com.intellij.ide.projectView.TreeStructureProvider; +import com.intellij.ide.projectView.ViewSettings; +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDirectory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.CCProjectService; + +import java.util.ArrayList; +import java.util.Collection; + +public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware { + @NotNull + @Override + public Collection modify(@NotNull AbstractTreeNode parent, + @NotNull Collection children, + ViewSettings settings) { + if (!needModify(parent)) { + return children; + } + Collection nodes = new ArrayList(); + for (AbstractTreeNode node : children) { + Project project = node.getProject(); + if (project != null) { + if (node.getValue() instanceof PsiDirectory) { + PsiDirectory directory = (PsiDirectory) node.getValue(); + nodes.add(new CCDirectoryNode(project, directory, settings)); + } else { + nodes.add(node); + } + } + } + return nodes; + } + + private static boolean needModify(@NotNull final AbstractTreeNode parent) { + Project project = parent.getProject(); + if (project != null) { + if (CCProjectService.getInstance(project).getCourse() == null) { + return false; + } + } + return true; + } + + @Nullable + @Override + public Object getData(Collection selected, String dataName) { + return null; + } +} From 0171f26e3cb41e1769140c8e4555ebf08c09cf7a Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Wed, 20 Aug 2014 13:23:04 +0400 Subject: [PATCH 09/24] aded create course archive action --- META-INF/plugin.xml | 2 + .../coursecreator/actions/AddTaskWindow.java | 2 +- .../coursecreator/actions/PackCourse.java | 113 ++++++++++++++++++ .../plugins/coursecreator/format/Task.java | 6 +- .../coursecreator/format/TaskWindow.java | 10 +- .../CCTaskLineMarkerProvider.java | 2 +- 6 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index 477588c8e504..e0a85ea9b984 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -49,6 +49,8 @@ + + \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java index d8e55f4bd3e8..a25a41a42b90 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -55,7 +55,7 @@ public class AddTaskWindow extends AnAction implements DumbAware { final String taskText = Messages.showMultilineInputDialog(project, "Add window task text", "Task Window Text", "", null, null); - final TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length); + final TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length, model.getSelectedText()); taskWindow.setTaskText(StringUtil.notNullize(taskText)); taskFile.addTaskWindow(taskWindow); diff --git a/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java b/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java new file mode 100644 index 000000000000..c07ba5f99d00 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java @@ -0,0 +1,113 @@ +package org.jetbrains.plugins.coursecreator.actions; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.command.undo.UndoManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.io.ZipUtil; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.*; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipOutputStream; + +public class PackCourse extends AnAction implements DumbAware { + private static final Logger LOG = Logger.getInstance(PackCourse.class.getName()); + public PackCourse() { + super("Generate course archive","Generate course archive", null); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final Project project = e.getData(CommonDataKeys.PROJECT); + if (project == null) { + return; + } + + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + if (course == null) return; + final VirtualFile baseDir = project.getBaseDir(); + final Map lessons = course.getLessonsMap(); + + List editorList = new ArrayList(); + + for (Map.Entry lesson : lessons.entrySet()) { + final VirtualFile lessonDir = baseDir.findChild(lesson.getKey()); + if (lessonDir == null) continue; + for (Map.Entry task : lesson.getValue().myTasksMap.entrySet()) { + final VirtualFile taskDir = lessonDir.findChild(task.getKey()); + if (taskDir == null) continue; + for (Map.Entry entry : task.getValue().task_files.entrySet()) { + final VirtualFile file = taskDir.findChild(entry.getKey()); + if (file == null) continue; + final Document document = FileDocumentManager.getInstance().getDocument(file); + if (document == null) continue; + + final TaskFile taskFile = entry.getValue(); + for (final TaskWindow taskWindow : taskFile.getTaskWindows()) { + final String taskText = taskWindow.getTaskText(); + final int lineStartOffset = document.getLineStartOffset(taskWindow.line); + final int offset = lineStartOffset + taskWindow.start; + final FileEditor[] editors = FileEditorManager.getInstance(project).getEditors(file); + if (editors.length > 0) + editorList.add(editors[0]); + + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + document.replaceString(offset, offset + taskWindow.getReplacementLength(), taskText); + FileDocumentManager.getInstance().saveDocument(document); + } + }); + } + }, "x", "qwe"); + + + } + } + } + } + try { + File zipFile = new File(project.getBasePath(), course.getName() + ".zip"); + ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile))); + + for (Map.Entry entry : lessons.entrySet()) { + final VirtualFile lessonDir = baseDir.findChild(entry.getKey()); + if (lessonDir == null) continue; + + ZipUtil.addFileOrDirRecursively(zos, null, new File(lessonDir.getPath()), lessonDir.getName(), null, null); + } + ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "hints"), "hints", null, null); + ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "course.json"), "course.json", null, null); + ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "test_helper.py"), "test_helper.py", null, null); + zos.close(); + } catch (IOException e1) { + LOG.error(e1); + } + + for (FileEditor fileEditor : editorList) { + UndoManager.getInstance(project).undo(fileEditor); + } + } + +} \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/Task.java b/src/org/jetbrains/plugins/coursecreator/format/Task.java index 6dcbc4b0814d..eecf4de0c103 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/Task.java +++ b/src/org/jetbrains/plugins/coursecreator/format/Task.java @@ -8,7 +8,7 @@ import java.util.Map; public class Task { @Expose public String name; - @Expose public Map taskFiles = new HashMap(); + @Expose public Map task_files = new HashMap(); public Task() {} @@ -17,10 +17,10 @@ public class Task { } public void addTaskFile(@NotNull final String name) { - taskFiles.put(name, new TaskFile()); + task_files.put(name, new TaskFile()); } public TaskFile getTaskFile(@NotNull final String name) { - return taskFiles.get(name); + return task_files.get(name); } } diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 1dedf7ff6374..0cd993c7bed2 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -11,21 +11,27 @@ public class TaskWindow { @Expose public String possible_answer; @Expose public int length; public String myTaskText; + public int myReplacementLength; public TaskWindow() {} - public TaskWindow(int line, int start, int length) { + public TaskWindow(int line, int start, int length, String selectedText) { this.line = line; this.start = start; - this.length = length; + myReplacementLength = length; + this.possible_answer = selectedText; } public void setTaskText(@NotNull final String taskText) { myTaskText = taskText; + length = myTaskText.length(); } public String getTaskText() { return myTaskText; } + public int getReplacementLength() { + return myReplacementLength; + } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java b/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java index feee464a2c93..9ba74ad88bb0 100644 --- a/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java +++ b/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java @@ -55,7 +55,7 @@ public class CCTaskLineMarkerProvider implements LineMarkerProvider { final int lineStartOffset = document.getLineStartOffset(taskWindow.line); final int offset = lineStartOffset + taskWindow.start; if (offset > document.getTextLength()) continue; - final TextRange textRange = TextRange.create(offset, offset + taskWindow.length); + final TextRange textRange = TextRange.create(offset, offset + taskWindow.getReplacementLength()); @SuppressWarnings("unchecked") final LineMarkerInfo info = new LineMarkerInfo(element, textRange, IconLoader.getIcon("/icons/gutter.png"), Pass.UPDATE_OVERRIDEN_MARKERS, From 3e1f2f2425cdb0b266d4a34c405938d58fc8d610 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Fri, 22 Aug 2014 12:48:02 +0400 Subject: [PATCH 10/24] cleanup --- .../plugins/coursecreator/actions/AddTaskWindow.java | 5 ++--- .../plugins/coursecreator/actions/CreateLesson.java | 9 ++++++--- .../plugins/coursecreator/actions/CreateTask.java | 9 ++++++--- .../coursecreator/actions/DeleteTaskWindowAction.java | 5 ++--- .../plugins/coursecreator/actions/GenerateJson.java | 5 ++--- .../plugins/coursecreator/actions/PackCourse.java | 5 ++--- .../coursecreator/actions/ShowTaskWindowText.java | 5 ++--- 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java index a25a41a42b90..68239840dc12 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -1,14 +1,13 @@ package org.jetbrains.plugins.coursecreator.actions; import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; @@ -18,7 +17,7 @@ import com.intellij.psi.PsiFile; import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.*; -public class AddTaskWindow extends AnAction implements DumbAware { +public class AddTaskWindow extends DumbAwareAction { public AddTaskWindow() { super("Add task window","Add task window", null); } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java index 25438e7618a1..bbb9998579e9 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java @@ -3,9 +3,12 @@ package org.jetbrains.plugins.coursecreator.actions; import com.intellij.ide.IdeView; import com.intellij.ide.util.DirectoryChooserUtil; import com.intellij.ide.util.DirectoryUtil; -import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.psi.PsiDirectory; @@ -14,7 +17,7 @@ import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.Course; import org.jetbrains.plugins.coursecreator.format.Lesson; -public class CreateLesson extends AnAction implements DumbAware { +public class CreateLesson extends DumbAwareAction { public CreateLesson() { super("Lesson", "Create new Lesson", PlatformIcons.DIRECTORY_CLOSED_ICON); } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java index 69a5e5d19489..603ebd72ef53 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java @@ -7,9 +7,12 @@ import com.intellij.ide.fileTemplates.FileTemplateUtil; import com.intellij.ide.util.DirectoryChooserUtil; import com.intellij.ide.util.DirectoryUtil; import com.intellij.ide.util.EditorHelper; -import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.psi.PsiDirectory; @@ -20,7 +23,7 @@ import org.jetbrains.plugins.coursecreator.format.Course; import org.jetbrains.plugins.coursecreator.format.Lesson; import org.jetbrains.plugins.coursecreator.format.Task; -public class CreateTask extends AnAction implements DumbAware { +public class CreateTask extends DumbAwareAction { public CreateTask() { super("Task", "Create new Task", PlatformIcons.DIRECTORY_CLOSED_ICON); } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java index 49aeb81dc455..0ac61478dfa0 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java @@ -1,12 +1,11 @@ package org.jetbrains.plugins.coursecreator.actions; import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; -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.editor.Document; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; @@ -18,7 +17,7 @@ import org.jetbrains.plugins.coursecreator.format.*; import java.util.List; @SuppressWarnings("ComponentNotRegistered") -public class DeleteTaskWindowAction extends AnAction implements DumbAware { +public class DeleteTaskWindowAction extends DumbAwareAction { @NotNull private final TaskWindow myTaskWindow; diff --git a/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java b/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java index 13eb904efcc1..18b8f60b5668 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java @@ -2,12 +2,11 @@ package org.jetbrains.plugins.coursecreator.actions; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.Course; @@ -16,7 +15,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -public class GenerateJson extends AnAction implements DumbAware { +public class GenerateJson extends DumbAwareAction { private static final Logger LOG = Logger.getInstance(GenerateJson.class.getName()); public GenerateJson() { super("Generate course Json","Generate course Json", null); diff --git a/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java b/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java index c07ba5f99d00..d1ca6dac1465 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java @@ -1,6 +1,5 @@ package org.jetbrains.plugins.coursecreator.actions; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.ApplicationManager; @@ -11,7 +10,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.io.ZipUtil; @@ -27,7 +26,7 @@ import java.util.List; import java.util.Map; import java.util.zip.ZipOutputStream; -public class PackCourse extends AnAction implements DumbAware { +public class PackCourse extends DumbAwareAction { private static final Logger LOG = Logger.getInstance(PackCourse.class.getName()); public PackCourse() { super("Generate course archive","Generate course archive", null); diff --git a/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java b/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java index 44c4c1aea570..a93cd5088f0d 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java @@ -1,9 +1,8 @@ package org.jetbrains.plugins.coursecreator.actions; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; @@ -11,7 +10,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.coursecreator.format.TaskWindow; @SuppressWarnings("ComponentNotRegistered") -public class ShowTaskWindowText extends AnAction implements DumbAware { +public class ShowTaskWindowText extends DumbAwareAction { @NotNull private final TaskWindow myTaskWindow; From febb9b738e506a672de01fe30a1216991dcaf0cb Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Fri, 22 Aug 2014 13:57:59 +0400 Subject: [PATCH 11/24] added border to description field --- .../coursecreator/CCNewProjectPanel.form | 34 +++++++++++++------ .../coursecreator/CCNewProjectPanel.java | 5 +-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form b/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form index f01368ad25e5..dedce9ecd3fe 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form +++ b/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form @@ -1,6 +1,6 @@
- + @@ -20,20 +20,12 @@ - + - - - - - - - - @@ -54,12 +46,32 @@ - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java b/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java index 709f61082817..ed1a6116bf32 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java +++ b/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java @@ -14,8 +14,9 @@ public class CCNewProjectPanel { public CCNewProjectPanel() { final String userName = System.getProperty("user.name"); - if (userName != null) - myAuthorField.setText(userName); + if (userName != null) { + myAuthorField.setText(userName); + } } public JPanel getMainPanel() { From 12d28e757bbafbf4992e02bc6284417ded299c40 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 22 Aug 2014 14:45:43 +0400 Subject: [PATCH 12/24] log exception, not action event --- .../jetbrains/plugins/coursecreator/actions/GenerateJson.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java b/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java index 13eb904efcc1..2a2305546944 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java @@ -39,7 +39,7 @@ public class GenerateJson extends AnAction implements DumbAware { writer = new FileWriter(courseJson); writer.write(json); } catch (IOException e1) { - LOG.error(e); + LOG.error(e1); } finally { try { From ba99978a8caace6ef0417077070e12c3832578f3 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Fri, 22 Aug 2014 20:29:48 +0400 Subject: [PATCH 13/24] created hints with task windows --- .../coursecreator/CCProjectGenerator.java | 1 + .../coursecreator/actions/AddTaskWindow.java | 14 +-- .../actions/ShowTaskWindowText.java | 10 +- .../coursecreator/format/TaskWindow.java | 9 ++ .../{ => ui}/CCNewProjectPanel.form | 2 +- .../{ => ui}/CCNewProjectPanel.java | 2 +- .../ui/CreateTaskWindowDialog.java | 107 ++++++++++++++++++ .../ui/CreateTaskWindowPanel.form | 102 +++++++++++++++++ .../ui/CreateTaskWindowPanel.java | 66 +++++++++++ 9 files changed, 298 insertions(+), 15 deletions(-) rename src/org/jetbrains/plugins/coursecreator/{ => ui}/CCNewProjectPanel.form (97%) rename src/org/jetbrains/plugins/coursecreator/{ => ui}/CCNewProjectPanel.java (94%) create mode 100644 src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java create mode 100644 src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.form create mode 100644 src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java b/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java index 9cae302680d1..956b89f53c39 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java @@ -18,6 +18,7 @@ import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.coursecreator.format.Course; +import org.jetbrains.plugins.coursecreator.ui.CCNewProjectPanel; import javax.swing.*; diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java index 68239840dc12..a242dfcbb14d 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -9,13 +9,13 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.*; +import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog; public class AddTaskWindow extends DumbAwareAction { public AddTaskWindow() { @@ -51,12 +51,12 @@ public class AddTaskWindow extends DumbAwareAction { final Lesson lesson = course.getLesson(lessonDir.getName()); final Task task = lesson.getTask(taskDir.getName()); final TaskFile taskFile = task.getTaskFile(file.getName()); - - final String taskText = Messages.showMultilineInputDialog(project, "Add window task text", "Task Window Text", "", null, null); - final TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length, model.getSelectedText()); - taskWindow.setTaskText(StringUtil.notNullize(taskText)); - + CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow); + dlg.show(); + if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) { + return; + } taskFile.addTaskWindow(taskWindow); DaemonCodeAnalyzerImpl.getInstance(project).restart(file); } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java b/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java index a93cd5088f0d..96ece50a1f0d 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java @@ -4,10 +4,9 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.coursecreator.format.TaskWindow; +import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog; @SuppressWarnings("ComponentNotRegistered") public class ShowTaskWindowText extends DumbAwareAction { @@ -23,9 +22,8 @@ public class ShowTaskWindowText extends DumbAwareAction { public void actionPerformed(AnActionEvent e) { final Project project = e.getData(PlatformDataKeys.PROJECT); if (project == null) return; - final String taskText = Messages.showMultilineInputDialog(project, "Task window text", "Task Window Text", - myTaskWindow.getTaskText(), null, null); - myTaskWindow.setTaskText(StringUtil.notNullize(taskText)); + //TODO: copy task window and return if modification canceled + CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, myTaskWindow); + dlg.show(); } - } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 0cd993c7bed2..8cca5469916d 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -12,6 +12,7 @@ public class TaskWindow { @Expose public int length; public String myTaskText; public int myReplacementLength; + public String myHint; public TaskWindow() {} @@ -34,4 +35,12 @@ public class TaskWindow { public int getReplacementLength() { return myReplacementLength; } + + public void setHint(String hint) { + myHint = hint; + } + + public String getHintName() { + return myHint; + } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form b/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.form similarity index 97% rename from src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form rename to src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.form index dedce9ecd3fe..71e27857a2ee 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.form +++ b/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.form @@ -1,5 +1,5 @@ -
+ diff --git a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java b/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java similarity index 94% rename from src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java rename to src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java index ed1a6116bf32..73c8167bc592 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCNewProjectPanel.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java @@ -1,4 +1,4 @@ -package org.jetbrains.plugins.coursecreator; +package org.jetbrains.plugins.coursecreator.ui; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java new file mode 100644 index 000000000000..6b6438ac899c --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java @@ -0,0 +1,107 @@ +package org.jetbrains.plugins.coursecreator.ui; + +import com.intellij.ide.projectView.ProjectView; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.format.TaskWindow; + +import javax.swing.*; +import java.io.*; + +public class CreateTaskWindowDialog extends DialogWrapper { + + public static final String TITLE = "New Task Window"; + private static final Logger LOG = Logger.getInstance(CreateTaskWindowDialog.class.getName()); + private final TaskWindow myTaskWindow; + private final CreateTaskWindowPanel myPanel; + private final Project myProject; + + public CreateTaskWindowDialog(@NotNull final Project project, @NotNull final TaskWindow taskWindow) { + super(project, true); + setTitle(TITLE); + myTaskWindow = taskWindow; + myPanel = new CreateTaskWindowPanel(); + if (taskWindow.getHintName() != null) { + setHintText(project, taskWindow); + } + myProject = project; + String taskWindowTaskText = taskWindow.getTaskText(); + myPanel.setTaskWindowText(taskWindowTaskText != null ? taskWindowTaskText : ""); + String hintName = taskWindow.getHintName(); + myPanel.setHintName(hintName != null ? hintName : ""); + init(); + } + + private void setHintText(Project project, TaskWindow taskWindow) { + VirtualFile hints = project.getBaseDir().findChild("hints"); + if (hints != null) { + File file = new File(hints.getPath(), taskWindow.getHintName()); + StringBuilder hintText = new StringBuilder(); + if (file.exists()) { + try { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); + String line; + while ((line = bufferedReader.readLine()) != null) { + hintText.append(line).append("\n"); + } + myPanel.setHintText(hintText.toString()); + myPanel.enableHint(true); + } + catch (FileNotFoundException e) { + LOG.error("created hint was not found", e); + } + catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + @Override + protected void doOKAction() { + String taskWindowText = myPanel.getTaskWindowText(); + myTaskWindow.setTaskText(StringUtil.notNullize(taskWindowText)); + if (myPanel.createHint()) { + String hintName = myPanel.getHintName(); + myTaskWindow.setHint(hintName); + String hintText = myPanel.getHintText(); + createHint(hintName, hintText); + } + super.doOKAction(); + } + + private void createHint(String hintName, String hintText) { + VirtualFile hintsDir = myProject.getBaseDir().findChild("hints"); + if (hintsDir != null) { + File hintFile = new File(hintsDir.getPath(), hintName); + PrintWriter printWriter = null; + try { + printWriter = new PrintWriter(hintFile); + printWriter.print(hintText); + } + catch (FileNotFoundException e) { + //TODO:show error in UI + return; + } + finally { + if (printWriter != null) { + printWriter.close(); + } + } + } + VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); + ProjectView.getInstance(myProject).refresh(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myPanel; + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.form b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.form new file mode 100644 index 000000000000..ccd91e4154b0 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.form @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java new file mode 100644 index 000000000000..a023c84c9706 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java @@ -0,0 +1,66 @@ +package org.jetbrains.plugins.coursecreator.ui; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +public class CreateTaskWindowPanel extends JPanel { + + private JPanel myPanel; + private JTextArea myTaskWindowText; + private JTextField myHintName; + private JTextArea myHintText; + private JCheckBox myCreateHintCheckBox; + private JLabel myHintNameLabel; + private JLabel myHintTextLabel; + + public CreateTaskWindowPanel() { + super(new BorderLayout()); + add(myPanel, BorderLayout.CENTER); + enableHint(false); + myCreateHintCheckBox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + int state = e.getStateChange(); + // 1 for checked + enableHint(state == 1); + } + }); + } + + public void enableHint(boolean isEnable) { + myHintName.setEnabled(isEnable); + myHintText.setEnabled(isEnable); + myHintNameLabel.setEnabled(isEnable); + myHintTextLabel.setEnabled(isEnable); + } + + public void setTaskWindowText(String taskWindowText) { + myTaskWindowText.setText(taskWindowText); + } + + public void setHintName(String hintName) { + myHintName.setText(hintName); + } + + public void setHintText(String hintText) { + myHintText.setText(hintText); + } + + public String getTaskWindowText() { + return myTaskWindowText.getText(); + } + + public String getHintName() { + return myHintName.getText(); + } + + public String getHintText() { + return myHintText.getText(); + } + + public boolean createHint() { + return myHintName.isEnabled(); + } +} From 46e20765dfb91ff6c9d8ee84e54ba43b92de7c79 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Mon, 25 Aug 2014 12:09:59 +0400 Subject: [PATCH 14/24] delete hints with task window delete --- .../coursecreator/CCProjectService.java | 13 ++++++++++ .../actions/DeleteTaskWindowAction.java | 2 +- .../coursecreator/format/TaskWindow.java | 18 ++++++++++++++ .../ui/CreateTaskWindowDialog.java | 24 +++++++++++++++++-- .../ui/CreateTaskWindowPanel.java | 16 ++++++++++++- 5 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectService.java b/src/org/jetbrains/plugins/coursecreator/CCProjectService.java index 59226d1901b6..1a397e1a1932 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCProjectService.java +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectService.java @@ -15,16 +15,21 @@ */ package org.jetbrains.plugins.coursecreator; +import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.coursecreator.format.Course; +import java.io.File; + @State(name = "CCProjectService", storages = { @Storage(file = "$PROJECT_CONFIG_DIR$/course_service.xml") @@ -32,6 +37,7 @@ import org.jetbrains.plugins.coursecreator.format.Course; ) public class CCProjectService implements PersistentStateComponent { + private static final Logger LOG = Logger.getInstance(CCProjectService.class.getName()); public Course myCourse; public static final String COURSE_ELEMENT = "course"; @@ -63,4 +69,11 @@ public class CCProjectService implements PersistentStateComponent { return ServiceManager.getService(project, CCProjectService.class); } + public static void deleteProjectFile(File file, @NotNull final Project project) { + if (!file.delete()) { + LOG.info("Failed to delete file " + file.getPath()); + } + VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); + ProjectView.getInstance(project).refresh(); + } } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java index 0ac61478dfa0..07f5865036fc 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java @@ -46,10 +46,10 @@ public class DeleteTaskWindowAction extends DumbAwareAction { final TaskFile taskFile = task.getTaskFile(file.getName()); final List taskWindows = taskFile.getTaskWindows(); if (taskWindows.contains(myTaskWindow)) { + myTaskWindow.removeResources(project); taskWindows.remove(myTaskWindow); DaemonCodeAnalyzerImpl.getInstance(project).restart(file); } - } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 8cca5469916d..92ec0c7d4dd2 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -1,10 +1,17 @@ package org.jetbrains.plugins.coursecreator.format; import com.google.gson.annotations.Expose; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.coursecreator.CCProjectService; + +import java.io.File; public class TaskWindow { + private static final Logger LOG = Logger.getInstance(TaskWindow.class.getName()); @Expose public int line; @Expose public int start; @Expose public String hint; @@ -43,4 +50,15 @@ public class TaskWindow { public String getHintName() { return myHint; } + + public void removeResources(@NotNull final Project project) { + if (myHint != null) { + VirtualFile hints = project.getBaseDir().findChild("hints"); + if (hints == null) { + return; + } + File hintFile = new File(hints.getPath(), myHint); + CCProjectService.deleteProjectFile(hintFile, project); + } + } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java index 6b6438ac899c..d11e855fc293 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java @@ -9,6 +9,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.TaskWindow; import javax.swing.*; @@ -22,11 +23,15 @@ public class CreateTaskWindowDialog extends DialogWrapper { private final CreateTaskWindowPanel myPanel; private final Project myProject; + public Project getProject() { + return myProject; + } + public CreateTaskWindowDialog(@NotNull final Project project, @NotNull final TaskWindow taskWindow) { super(project, true); setTitle(TITLE); myTaskWindow = taskWindow; - myPanel = new CreateTaskWindowPanel(); + myPanel = new CreateTaskWindowPanel(this); if (taskWindow.getHintName() != null) { setHintText(project, taskWindow); } @@ -50,8 +55,10 @@ public class CreateTaskWindowDialog extends DialogWrapper { while ((line = bufferedReader.readLine()) != null) { hintText.append(line).append("\n"); } + myPanel.doClick(); + //myPanel.enableHint(true); myPanel.setHintText(hintText.toString()); - myPanel.enableHint(true); + } catch (FileNotFoundException e) { LOG.error("created hint was not found", e); @@ -99,6 +106,19 @@ public class CreateTaskWindowDialog extends DialogWrapper { ProjectView.getInstance(myProject).refresh(); } + public void deleteHint() { + VirtualFile hintsDir = myProject.getBaseDir().findChild("hints"); + if (hintsDir != null) { + String hintName = myTaskWindow.getHintName(); + File hintFile = new File(hintsDir.getPath(), hintName); + if (hintFile.exists()) { + CCProjectService.deleteProjectFile(hintFile, myProject); + myTaskWindow.setHint(null); + myPanel.resetHint(); + } + } + } + @Nullable @Override protected JComponent createCenterPanel() { diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java index a023c84c9706..8a8d86a87743 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java @@ -7,6 +7,7 @@ import java.awt.event.ItemListener; public class CreateTaskWindowPanel extends JPanel { + private final CreateTaskWindowDialog myDialog; private JPanel myPanel; private JTextArea myTaskWindowText; private JTextField myHintName; @@ -15,9 +16,10 @@ public class CreateTaskWindowPanel extends JPanel { private JLabel myHintNameLabel; private JLabel myHintTextLabel; - public CreateTaskWindowPanel() { + public CreateTaskWindowPanel(CreateTaskWindowDialog dialog) { super(new BorderLayout()); add(myPanel, BorderLayout.CENTER); + myDialog = dialog; enableHint(false); myCreateHintCheckBox.addItemListener(new ItemListener() { @Override @@ -25,6 +27,9 @@ public class CreateTaskWindowPanel extends JPanel { int state = e.getStateChange(); // 1 for checked enableHint(state == 1); + if (state == 2) { + myDialog.deleteHint(); + } } }); } @@ -63,4 +68,13 @@ public class CreateTaskWindowPanel extends JPanel { public boolean createHint() { return myHintName.isEnabled(); } + + public void doClick() { + myCreateHintCheckBox.doClick(); + } + + public void resetHint() { + myHintName.setText(""); + myHintText.setText(""); + } } From 9cdd3ca64769fe642ca6c6262d88ecc5fff5766e Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Mon, 25 Aug 2014 12:11:59 +0400 Subject: [PATCH 15/24] replaced print with logging --- .../plugins/coursecreator/ui/CreateTaskWindowDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java index d11e855fc293..17e8ad19419c 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java @@ -64,7 +64,7 @@ public class CreateTaskWindowDialog extends DialogWrapper { LOG.error("created hint was not found", e); } catch (IOException e) { - e.printStackTrace(); + LOG.error(e); } } } From 76740f606beeec6ec2313f85b838c93485de27db Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Mon, 25 Aug 2014 13:28:45 +0400 Subject: [PATCH 16/24] task window shown as range highlighters --- META-INF/plugin.xml | 3 + .../CCEditorFactoryListener.java | 35 ++++++++++++ .../coursecreator/CCProjectComponent.java | 55 +++++++++++++++++++ .../coursecreator/CCProjectService.java | 48 ++++++++++++++-- .../coursecreator/actions/AddTaskWindow.java | 1 + .../actions/DeleteTaskWindowAction.java | 7 +++ .../coursecreator/format/TaskWindow.java | 17 +++++- 7 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java create mode 100644 src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index e0a85ea9b984..e3249a1c2564 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -35,6 +35,9 @@ + + org.jetbrains.plugins.coursecreator.CCProjectComponent + diff --git a/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java b/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java new file mode 100644 index 000000000000..6fd8d9316e72 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java @@ -0,0 +1,35 @@ +package org.jetbrains.plugins.coursecreator; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.event.EditorFactoryEvent; +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 org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.coursecreator.format.Course; + +public class CCEditorFactoryListener implements EditorFactoryListener { + @Override + public void editorCreated(@NotNull EditorFactoryEvent event) { + Editor editor = event.getEditor(); + Project project = editor.getProject(); + if (project == null) { + return; + } + VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument()); + if (virtualFile == null) { + return; + } + Course course = CCProjectService.getInstance(project).getCourse(); + if (course == null) { + return; + } + CCProjectService.drawTaskWindows(virtualFile, editor, course); + } + + @Override + public void editorReleased(@NotNull EditorFactoryEvent event) { + + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java b/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java new file mode 100644 index 000000000000..b3a7af9d0ab5 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java @@ -0,0 +1,55 @@ +package org.jetbrains.plugins.coursecreator; + +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.coursecreator.format.Course; + +public class CCProjectComponent implements ProjectComponent { + private final Project myProject; + + public CCProjectComponent(Project project) { + myProject = project; + } + + public void initComponent() { + } + + public void disposeComponent() { + } + + @NotNull + public String getComponentName() { + return "CCProjectComponent"; + } + + public void projectOpened() { + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() { + @Override + public void run() { + Course course = CCProjectService.getInstance(myProject).getCourse(); + if (course != null) { + EditorFactory.getInstance().addEditorFactoryListener(new CCEditorFactoryListener(), myProject); + VirtualFile[] files = FileEditorManager.getInstance(myProject).getOpenFiles(); + for (VirtualFile file : files) { + FileEditor fileEditor = FileEditorManager.getInstance(myProject).getSelectedEditor(file); + if (fileEditor instanceof PsiAwareTextEditorImpl) { + Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor(); + CCProjectService.drawTaskWindows(file, editor, course); + } + } + } + } + }); + } + + public void projectClosed() { + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectService.java b/src/org/jetbrains/plugins/coursecreator/CCProjectService.java index 1a397e1a1932..d9ff744bdccb 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCProjectService.java +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectService.java @@ -21,18 +21,21 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Element; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.coursecreator.format.Course; +import org.jetbrains.plugins.coursecreator.format.*; import java.io.File; +import java.util.List; @State(name = "CCProjectService", storages = { - @Storage(file = "$PROJECT_CONFIG_DIR$/course_service.xml") + @Storage(file = "$PROJECT_CONFIG_DIR$/course_service.xml") } ) public class CCProjectService implements PersistentStateComponent { @@ -70,10 +73,45 @@ public class CCProjectService implements PersistentStateComponent { } public static void deleteProjectFile(File file, @NotNull final Project project) { - if (!file.delete()) { - LOG.info("Failed to delete file " + file.getPath()); - } + if (!file.delete()) { + LOG.info("Failed to delete file " + file.getPath()); + } VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); ProjectView.getInstance(project).refresh(); } + + public static void drawTaskWindows(@NotNull final VirtualFile virtualFile, @NotNull final Editor editor, @NotNull final Course course) { + VirtualFile taskDir = virtualFile.getParent(); + if (taskDir == null) { + return; + } + String taskDirName = taskDir.getName(); + if (!taskDirName.contains("task")) { + return; + } + VirtualFile lessonDir = taskDir.getParent(); + if (lessonDir == null) { + return; + } + String lessonDirName = lessonDir.getName(); + if (!lessonDirName.contains("lesson")) { + return; + } + Lesson lesson = course.getLessonsMap().get(lessonDirName); + if (lesson == null) { + return; + } + Task task = lesson.getTask(taskDirName); + if (task == null) { + return; + } + TaskFile taskFile = task.getTaskFile(virtualFile.getName()); + if (taskFile == null) { + return; + } + List taskWindows = taskFile.getTaskWindows(); + for (TaskWindow taskWindow : taskWindows) { + taskWindow.drawHighlighter(editor); + } + } } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java index a242dfcbb14d..6e6a3836a8e6 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -58,6 +58,7 @@ public class AddTaskWindow extends DumbAwareAction { return; } taskFile.addTaskWindow(taskWindow); + taskWindow.drawHighlighter(editor); DaemonCodeAnalyzerImpl.getInstance(project).restart(file); } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java index 07f5865036fc..67abe236214b 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java @@ -5,6 +5,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; @@ -32,6 +33,10 @@ public class DeleteTaskWindowAction extends DumbAwareAction { if (project == null) return; final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); if (file == null) return; + final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); + if (editor == null) { + return; + } final Document document = PsiDocumentManager.getInstance(project).getDocument(file); if (document == null) return; @@ -48,6 +53,8 @@ public class DeleteTaskWindowAction extends DumbAwareAction { if (taskWindows.contains(myTaskWindow)) { myTaskWindow.removeResources(project); taskWindows.remove(myTaskWindow); + editor.getMarkupModel().removeAllHighlighters(); + CCProjectService.drawTaskWindows(file.getVirtualFile(), editor, course); DaemonCodeAnalyzerImpl.getInstance(project).restart(file); } } diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 92ec0c7d4dd2..25064e2d261d 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -1,7 +1,12 @@ package org.jetbrains.plugins.coursecreator.format; import com.google.gson.annotations.Expose; -import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.colors.EditorColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.markup.HighlighterLayer; +import com.intellij.openapi.editor.markup.HighlighterTargetArea; +import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -11,7 +16,6 @@ import java.io.File; public class TaskWindow { - private static final Logger LOG = Logger.getInstance(TaskWindow.class.getName()); @Expose public int line; @Expose public int start; @Expose public String hint; @@ -61,4 +65,13 @@ public class TaskWindow { CCProjectService.deleteProjectFile(hintFile, project); } } + + public void drawHighlighter(@NotNull final Editor editor) { + int startOffset = editor.getDocument().getLineStartOffset(line) + start; + int endOffset = startOffset + myReplacementLength; + TextAttributes defaultTestAttributes = + EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.LIVE_TEMPLATE_ATTRIBUTES); + editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST + 1, defaultTestAttributes, + HighlighterTargetArea.EXACT_RANGE); + } } \ No newline at end of file From 29dad91be83900a5a315347a0956cd44f8505b66 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Mon, 25 Aug 2014 15:08:08 +0400 Subject: [PATCH 17/24] create task file action --- META-INF/plugin.xml | 38 +++--- .../actions/CreateTaskFileAction.java | 108 ++++++++++++++++++ 2 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index e3249a1c2564..923c7502e2e9 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -23,10 +23,11 @@ com.intellij.modules.python - - - - + + + + @@ -41,19 +42,22 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java new file mode 100644 index 000000000000..8fb8db00c7bf --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java @@ -0,0 +1,108 @@ +package org.jetbrains.plugins.coursecreator.actions; + +import com.intellij.ide.IdeView; +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.ide.util.DirectoryChooserUtil; +import com.intellij.ide.util.EditorHelper; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import icons.PythonPsiApiIcons; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.Course; +import org.jetbrains.plugins.coursecreator.format.Lesson; +import org.jetbrains.plugins.coursecreator.format.Task; + +public class CreateTaskFileAction extends DumbAwareAction { + + public CreateTaskFileAction() { + super("Task File", "Create new Task File", PythonPsiApiIcons.PythonFile); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final IdeView view = e.getData(LangDataKeys.IDE_VIEW); + final Project project = e.getData(CommonDataKeys.PROJECT); + + if (view == null || project == null) { + return; + } + final PsiDirectory taskDir = DirectoryChooserUtil.getOrChooseDirectory(view); + if (taskDir == null) return; + PsiDirectory lessonDir = taskDir.getParent(); + if (lessonDir == null) { + return; + } + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final Lesson lesson = course.getLesson(lessonDir.getName()); + final Task task = lesson.getTask(taskDir.getName()); + + final String taskFileName = Messages.showInputDialog("Name:", "Task File Name", null, null, null); + if (taskFileName == null) return; + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.py"); + try { + final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName + ".py", null, taskDir); + task.addTaskFile(taskPyFile.getContainingFile().getName()); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + EditorHelper.openInEditor(taskPyFile, false); + view.selectElement(taskPyFile); + } + }); + } + catch (Exception ignored) { + } + } + }); + } + + @Override + public void update(AnActionEvent event) { + final Presentation presentation = event.getPresentation(); + final Project project = event.getData(CommonDataKeys.PROJECT); + if (project == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + + final IdeView view = event.getData(LangDataKeys.IDE_VIEW); + if (view == null) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + + final PsiDirectory[] directories = view.getDirectories(); + if (directories.length == 0) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + if (course != null && directory != null && !directory.getName().contains("task")) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } + presentation.setVisible(true); + presentation.setEnabled(true); + } +} From fb514e3821bc233fc570ff02a99ca12f6e6a58d4 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Tue, 26 Aug 2014 11:27:22 +0400 Subject: [PATCH 18/24] pack course action improved --- META-INF/plugin.xml | 3 +- .../coursecreator/actions/GenerateJson.java | 66 ---------------- .../coursecreator/actions/PackCourse.java | 79 ++++++++++++++++--- .../coursecreator/format/TaskWindow.java | 9 +-- .../ui/CreateCourseArchiveDialog.java | 40 ++++++++++ .../ui/CreateCourseArchivePanel.form | 79 +++++++++++++++++++ .../ui/CreateCourseArchivePanel.java | 66 ++++++++++++++++ 7 files changed, 256 insertions(+), 86 deletions(-) delete mode 100644 src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java create mode 100644 src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java create mode 100644 src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form create mode 100644 src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index 923c7502e2e9..63bdd367517b 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -54,9 +54,8 @@ - - + diff --git a/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java b/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java deleted file mode 100644 index ac22a9839a85..000000000000 --- a/src/org/jetbrains/plugins/coursecreator/actions/GenerateJson.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.jetbrains.plugins.coursecreator.actions; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; -import org.jetbrains.plugins.coursecreator.CCProjectService; -import org.jetbrains.plugins.coursecreator.format.Course; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -public class GenerateJson extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(GenerateJson.class.getName()); - public GenerateJson() { - super("Generate course Json","Generate course Json", null); - } - - @Override - public void actionPerformed(AnActionEvent e) { - final Project project = e.getData(CommonDataKeys.PROJECT); - if (project == null) { - return; - } - - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); - final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); - final String json = gson.toJson(course); - final File courseJson = new File(project.getBasePath(), "course.json"); - FileWriter writer = null; - try { - writer = new FileWriter(courseJson); - writer.write(json); - } catch (IOException e1) { - LOG.error(e1); - } - finally { - try { - if (writer != null) - writer.close(); - } catch (IOException e1) { - LOG.error(e1); - } - } - } - - @Override - public void update(AnActionEvent event) { - final Presentation presentation = event.getPresentation(); - final Project project = event.getData(CommonDataKeys.PROJECT); - if (project == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - presentation.setVisible(true); - presentation.setEnabled(true); - - } -} \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java b/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java index d1ca6dac1465..77ba2759e3d6 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java @@ -1,5 +1,9 @@ package org.jetbrains.plugins.coursecreator.actions; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.intellij.icons.AllIcons; +import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.ApplicationManager; @@ -12,15 +16,16 @@ import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.io.ZipUtil; import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.*; +import org.jetbrains.plugins.coursecreator.ui.CreateCourseArchiveDialog; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -28,8 +33,19 @@ import java.util.zip.ZipOutputStream; public class PackCourse extends DumbAwareAction { private static final Logger LOG = Logger.getInstance(PackCourse.class.getName()); + String myZipName; + String myLocationDir; + + public void setZipName(String zipName) { + myZipName = zipName; + } + + public void setLocationDir(String locationDir) { + myLocationDir = locationDir; + } + public PackCourse() { - super("Generate course archive","Generate course archive", null); + super("Generate course archive", "Generate course archive", AllIcons.FileTypes.Archive); } @Override @@ -38,15 +54,18 @@ public class PackCourse extends DumbAwareAction { if (project == null) { return; } - final CCProjectService service = CCProjectService.getInstance(project); final Course course = service.getCourse(); if (course == null) return; + generateJson(project); + CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this); + dlg.show(); + if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) { + return; + } final VirtualFile baseDir = project.getBaseDir(); final Map lessons = course.getLessonsMap(); - List editorList = new ArrayList(); - for (Map.Entry lesson : lessons.entrySet()) { final VirtualFile lessonDir = baseDir.findChild(lesson.getKey()); if (lessonDir == null) continue; @@ -65,8 +84,9 @@ public class PackCourse extends DumbAwareAction { final int lineStartOffset = document.getLineStartOffset(taskWindow.line); final int offset = lineStartOffset + taskWindow.start; final FileEditor[] editors = FileEditorManager.getInstance(project).getEditors(file); - if (editors.length > 0) + if (editors.length > 0) { editorList.add(editors[0]); + } CommandProcessor.getInstance().executeCommand(project, new Runnable() { @Override @@ -80,14 +100,12 @@ public class PackCourse extends DumbAwareAction { }); } }, "x", "qwe"); - - } } } } try { - File zipFile = new File(project.getBasePath(), course.getName() + ".zip"); + File zipFile = new File(myLocationDir, myZipName + ".zip"); ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile))); for (Map.Entry entry : lessons.entrySet()) { @@ -100,13 +118,48 @@ public class PackCourse extends DumbAwareAction { ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "course.json"), "course.json", null, null); ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "test_helper.py"), "test_helper.py", null, null); zos.close(); - } catch (IOException e1) { + Messages.showInfoMessage("Course archive was saved to " + zipFile.getPath(), "Course Archive Was Created Successfully"); + } + catch (IOException e1) { LOG.error(e1); } for (FileEditor fileEditor : editorList) { UndoManager.getInstance(project).undo(fileEditor); } + + VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); + ProjectView.getInstance(project).refresh(); } + private void generateJson(Project project) { + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); + final String json = gson.toJson(course); + final File courseJson = new File(project.getBasePath(), "course.json"); + FileWriter writer = null; + try { + writer = new FileWriter(courseJson); + writer.write(json); + } + catch (IOException e) { + Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json"); + LOG.info(e); + } + catch (Exception e) { + Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json"); + LOG.info(e); + } + finally { + try { + if (writer != null) { + writer.close(); + } + } + catch (IOException e1) { + //close silently + } + } + } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 25064e2d261d..4ecbc60fc410 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -23,7 +23,6 @@ public class TaskWindow { @Expose public int length; public String myTaskText; public int myReplacementLength; - public String myHint; public TaskWindow() {} @@ -48,20 +47,20 @@ public class TaskWindow { } public void setHint(String hint) { - myHint = hint; + this.hint = hint; } public String getHintName() { - return myHint; + return hint; } public void removeResources(@NotNull final Project project) { - if (myHint != null) { + if (hint != null) { VirtualFile hints = project.getBaseDir().findChild("hints"); if (hints == null) { return; } - File hintFile = new File(hints.getPath(), myHint); + File hintFile = new File(hints.getPath(), hint); CCProjectService.deleteProjectFile(hintFile, project); } } diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java new file mode 100644 index 000000000000..1890e1840464 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java @@ -0,0 +1,40 @@ +package org.jetbrains.plugins.coursecreator.ui; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.actions.PackCourse; + +import javax.swing.*; + +public class CreateCourseArchiveDialog extends DialogWrapper { + + private CreateCourseArchivePanel myPanel; + private PackCourse myAction; + + public CreateCourseArchiveDialog(@NotNull final Project project, PackCourse action) { + super(project); + setTitle("Create Course Archive"); + myPanel = new CreateCourseArchivePanel(project, this); + myAction = action; + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myPanel; + } + + public void enableOKAction(boolean isEnabled) { + myOKAction.setEnabled(isEnabled); + } + + @Override + protected void doOKAction() { + myAction.setZipName(myPanel.getZipName()); + myAction.setLocationDir(myPanel.getLocationPath()); + super.doOKAction(); + } +} diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form new file mode 100644 index 000000000000..920dcb9494a7 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form @@ -0,0 +1,79 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java new file mode 100644 index 000000000000..c5b393b71c97 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java @@ -0,0 +1,66 @@ +package org.jetbrains.plugins.coursecreator.ui; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +public class CreateCourseArchivePanel extends JPanel { + private final Project myProject; + private JPanel myPanel; + private JTextField myNameField; + private TextFieldWithBrowseButton myLocationField; + private JLabel myErrorIcon; + private JLabel myErrorLabel; + private CreateCourseArchiveDialog myDlg; + + public CreateCourseArchivePanel(@NotNull final Project project, CreateCourseArchiveDialog dlg) { + setLayout(new BorderLayout()); + add(myPanel, BorderLayout.CENTER); + myProject = project; + myErrorIcon.setIcon(AllIcons.Actions.Lightning); + setState(false); + myDlg = dlg; + myDlg.enableOKAction(false); + FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); + myLocationField.addBrowseFolderListener("Choose location folder", null, myProject, descriptor); + myLocationField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String location = myLocationField.getText(); + File file = new File(location); + if (!file.exists() || !file.isDirectory()) { + myDlg.enableOKAction(false); + setError("Invalid location"); + } + myDlg.enableOKAction(true); + } + }); + } + + private void setState(boolean isVisible) { + myErrorIcon.setVisible(isVisible); + myErrorLabel.setVisible(isVisible); + } + + private void setError(String message) { + myErrorLabel.setText(message); + setState(true); + } + + public String getZipName() { + return myNameField.getText(); + } + + public String getLocationPath() { + return myLocationField.getText(); + } +} From 83afeb0adc175d3fa218c06a115961ef5c2b80b6 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Tue, 26 Aug 2014 12:47:06 +0400 Subject: [PATCH 19/24] suggest generated hint name --- .../coursecreator/actions/AddTaskWindow.java | 5 +++-- .../coursecreator/actions/CreateLesson.java | 1 + .../coursecreator/actions/CreateTask.java | 3 ++- .../actions/CreateTaskFileAction.java | 2 +- .../actions/ShowTaskWindowText.java | 18 ++++++++++++++++-- .../plugins/coursecreator/format/Lesson.java | 9 +++++++++ .../plugins/coursecreator/format/Task.java | 15 +++++++++++++-- .../plugins/coursecreator/format/TaskFile.java | 12 +++++++++++- .../coursecreator/format/TaskWindow.java | 10 ++++++++++ .../ui/CreateTaskWindowDialog.java | 6 +++++- .../ui/CreateTaskWindowPanel.java | 6 ++++++ 11 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java index 6e6a3836a8e6..b4f1e23a2c72 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -52,12 +52,13 @@ public class AddTaskWindow extends DumbAwareAction { final Task task = lesson.getTask(taskDir.getName()); final TaskFile taskFile = task.getTaskFile(file.getName()); final TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length, model.getSelectedText()); - CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow); + CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension()); dlg.show(); if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) { return; } - taskFile.addTaskWindow(taskWindow); + int index = taskFile.getTaskWindows().size() + 1; + taskFile.addTaskWindow(taskWindow, index); taskWindow.drawHighlighter(editor); DaemonCodeAnalyzerImpl.getInstance(project).restart(file); } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java index bbb9998579e9..15d9f8367db9 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java @@ -46,6 +46,7 @@ public class CreateLesson extends DumbAwareAction { if (lessonDirectory != null) { view.selectElement(lessonDirectory); final Lesson lesson = new Lesson(lessonName); + lesson.setIndex(size + 1); course.addLesson(lesson, lessonDirectory); } } diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java index 603ebd72ef53..3df976913f13 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java @@ -61,7 +61,8 @@ public class CreateTask extends DumbAwareAction { final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskName + ".py", null, taskDirectory); final Task task = new Task(taskName); - task.addTaskFile(taskPyFile.getContainingFile().getName()); + task.addTaskFile(taskPyFile.getContainingFile().getName(), size + 1); + task.setIndex(size + 1); lesson.addTask(task, taskDirectory); ApplicationManager.getApplication().invokeLater(new Runnable() { diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java index 8fb8db00c7bf..3dfb7acef9aa 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java @@ -56,7 +56,7 @@ public class CreateTaskFileAction extends DumbAwareAction { final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.py"); try { final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName + ".py", null, taskDir); - task.addTaskFile(taskPyFile.getContainingFile().getName()); + task.addTaskFile(taskPyFile.getContainingFile().getName(), task.task_files.size() + 1); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { diff --git a/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java b/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java index 96ece50a1f0d..c755fa0eed5e 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java @@ -1,11 +1,15 @@ package org.jetbrains.plugins.coursecreator.actions; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.coursecreator.format.TaskWindow; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.*; import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog; @SuppressWarnings("ComponentNotRegistered") @@ -22,8 +26,18 @@ public class ShowTaskWindowText extends DumbAwareAction { public void actionPerformed(AnActionEvent e) { final Project project = e.getData(PlatformDataKeys.PROJECT); if (project == null) return; + final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); + if (file == null) return; + final CCProjectService service = CCProjectService.getInstance(project); + final Course course = service.getCourse(); + final PsiDirectory taskDir = file.getContainingDirectory(); + final PsiDirectory lessonDir = taskDir.getParent(); + if (lessonDir == null) return; + + final Lesson lesson = course.getLesson(lessonDir.getName()); + final Task task = lesson.getTask(taskDir.getName()); //TODO: copy task window and return if modification canceled - CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, myTaskWindow); + CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, myTaskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension()); dlg.show(); } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/Lesson.java b/src/org/jetbrains/plugins/coursecreator/format/Lesson.java index 3c6e5601b0d2..38720140caf1 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/Lesson.java +++ b/src/org/jetbrains/plugins/coursecreator/format/Lesson.java @@ -13,6 +13,7 @@ public class Lesson { @Expose public String name; @Expose public List task_list = new ArrayList(); + public int myIndex; public Map myTasksMap = new HashMap(); public Lesson() {} @@ -33,4 +34,12 @@ public class Lesson { public List getTasklist() { return task_list; } + + public void setIndex(int index) { + myIndex = index; + } + + public int getIndex() { + return myIndex; + } } diff --git a/src/org/jetbrains/plugins/coursecreator/format/Task.java b/src/org/jetbrains/plugins/coursecreator/format/Task.java index eecf4de0c103..9946a6afa1df 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/Task.java +++ b/src/org/jetbrains/plugins/coursecreator/format/Task.java @@ -9,6 +9,7 @@ import java.util.Map; public class Task { @Expose public String name; @Expose public Map task_files = new HashMap(); + public int myIndex; public Task() {} @@ -16,11 +17,21 @@ public class Task { this.name = name; } - public void addTaskFile(@NotNull final String name) { - task_files.put(name, new TaskFile()); + public int getIndex() { + return myIndex; + } + + public void addTaskFile(@NotNull final String name, int index) { + TaskFile taskFile = new TaskFile(); + taskFile.setIndex(index); + task_files.put(name, taskFile); } public TaskFile getTaskFile(@NotNull final String name) { return task_files.get(name); } + + public void setIndex(int index) { + myIndex = index; + } } diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java index 16cc89d23129..79f94d711143 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java @@ -8,14 +8,24 @@ import java.util.List; public class TaskFile { @Expose public List task_windows = new ArrayList(); + public int myIndex; public TaskFile() {} - public void addTaskWindow(@NotNull final TaskWindow taskWindow) { + public void addTaskWindow(@NotNull final TaskWindow taskWindow, int index) { + taskWindow.setIndex(index); task_windows.add(taskWindow); } public List getTaskWindows() { return task_windows; } + + public void setIndex(int index) { + myIndex = index; + } + + public int getIndex() { + return myIndex; + } } diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 4ecbc60fc410..92c265ed8e9f 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -23,6 +23,7 @@ public class TaskWindow { @Expose public int length; public String myTaskText; public int myReplacementLength; + public int myIndex; public TaskWindow() {} @@ -73,4 +74,13 @@ public class TaskWindow { editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST + 1, defaultTestAttributes, HighlighterTargetArea.EXACT_RANGE); } + + public int getIndex() { + return myIndex; + } + + public void setIndex(int index) { + + myIndex = index; + } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java index 17e8ad19419c..be598038a2d7 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java @@ -27,7 +27,8 @@ public class CreateTaskWindowDialog extends DialogWrapper { return myProject; } - public CreateTaskWindowDialog(@NotNull final Project project, @NotNull final TaskWindow taskWindow) { + public CreateTaskWindowDialog(@NotNull final Project project, @NotNull final TaskWindow taskWindow, int lessonIndex, + int taskIndex, String taskFileName) { super(project, true); setTitle(TITLE); myTaskWindow = taskWindow; @@ -38,6 +39,9 @@ public class CreateTaskWindowDialog extends DialogWrapper { myProject = project; String taskWindowTaskText = taskWindow.getTaskText(); myPanel.setTaskWindowText(taskWindowTaskText != null ? taskWindowTaskText : ""); + int taskWindowIndex = taskWindow.getIndex() + 1; + String generatedHintName = "lesson" + lessonIndex + "task" + taskIndex + taskFileName + "_" + taskWindowIndex; + myPanel.setGeneratedHintName(generatedHintName); String hintName = taskWindow.getHintName(); myPanel.setHintName(hintName != null ? hintName : ""); init(); diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java index 8a8d86a87743..966e27f534e7 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java @@ -15,6 +15,7 @@ public class CreateTaskWindowPanel extends JPanel { private JCheckBox myCreateHintCheckBox; private JLabel myHintNameLabel; private JLabel myHintTextLabel; + private String myGeneratedHintName = ""; public CreateTaskWindowPanel(CreateTaskWindowDialog dialog) { super(new BorderLayout()); @@ -39,6 +40,7 @@ public class CreateTaskWindowPanel extends JPanel { myHintText.setEnabled(isEnable); myHintNameLabel.setEnabled(isEnable); myHintTextLabel.setEnabled(isEnable); + myHintName.setText(myGeneratedHintName); } public void setTaskWindowText(String taskWindowText) { @@ -77,4 +79,8 @@ public class CreateTaskWindowPanel extends JPanel { myHintName.setText(""); myHintText.setText(""); } + + public void setGeneratedHintName(String generatedHintName) { + myGeneratedHintName = generatedHintName; + } } From 5c4660771ee7c9e9047b7055d0665efffc6bd8a0 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Tue, 26 Aug 2014 12:59:02 +0400 Subject: [PATCH 20/24] default name and location for course archive --- META-INF/plugin.xml | 2 +- .../actions/{PackCourse.java => CreateCourseArchive.java} | 6 +++--- .../coursecreator/ui/CreateCourseArchiveDialog.java | 6 +++--- .../plugins/coursecreator/ui/CreateCourseArchivePanel.java | 7 +++---- 4 files changed, 10 insertions(+), 11 deletions(-) rename src/org/jetbrains/plugins/coursecreator/actions/{PackCourse.java => CreateCourseArchive.java} (97%) diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index 63bdd367517b..1d769776217c 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -54,7 +54,7 @@ - + diff --git a/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java similarity index 97% rename from src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java rename to src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java index 77ba2759e3d6..76a8fdb6724c 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/PackCourse.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java @@ -31,8 +31,8 @@ import java.util.List; import java.util.Map; import java.util.zip.ZipOutputStream; -public class PackCourse extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(PackCourse.class.getName()); +public class CreateCourseArchive extends DumbAwareAction { + private static final Logger LOG = Logger.getInstance(CreateCourseArchive.class.getName()); String myZipName; String myLocationDir; @@ -44,7 +44,7 @@ public class PackCourse extends DumbAwareAction { myLocationDir = locationDir; } - public PackCourse() { + public CreateCourseArchive() { super("Generate course archive", "Generate course archive", AllIcons.FileTypes.Archive); } diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java index 1890e1840464..d6c3bdb24af3 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java @@ -4,16 +4,16 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.coursecreator.actions.PackCourse; +import org.jetbrains.plugins.coursecreator.actions.CreateCourseArchive; import javax.swing.*; public class CreateCourseArchiveDialog extends DialogWrapper { private CreateCourseArchivePanel myPanel; - private PackCourse myAction; + private CreateCourseArchive myAction; - public CreateCourseArchiveDialog(@NotNull final Project project, PackCourse action) { + public CreateCourseArchiveDialog(@NotNull final Project project, CreateCourseArchive action) { super(project); setTitle("Create Course Archive"); myPanel = new CreateCourseArchivePanel(project, this); diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java index c5b393b71c97..4e7deedf7e47 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java @@ -14,7 +14,6 @@ import java.awt.event.ActionListener; import java.io.File; public class CreateCourseArchivePanel extends JPanel { - private final Project myProject; private JPanel myPanel; private JTextField myNameField; private TextFieldWithBrowseButton myLocationField; @@ -25,13 +24,13 @@ public class CreateCourseArchivePanel extends JPanel { public CreateCourseArchivePanel(@NotNull final Project project, CreateCourseArchiveDialog dlg) { setLayout(new BorderLayout()); add(myPanel, BorderLayout.CENTER); - myProject = project; myErrorIcon.setIcon(AllIcons.Actions.Lightning); setState(false); myDlg = dlg; - myDlg.enableOKAction(false); + myNameField.setText("course"); + myLocationField.setText(project.getBasePath()); FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); - myLocationField.addBrowseFolderListener("Choose location folder", null, myProject, descriptor); + myLocationField.addBrowseFolderListener("Choose location folder", null, project, descriptor); myLocationField.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { From 8927477448ec9d7ece4491533c15d2efb591f230 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Tue, 26 Aug 2014 13:59:22 +0400 Subject: [PATCH 21/24] generate task file names --- META-INF/plugin.xml | 2 +- .../plugins/coursecreator/actions/CreateTask.java | 2 +- .../{CreateTaskFileAction.java => CreateTaskFile.java} | 10 ++++++---- ...leteTaskWindowAction.java => DeleteTaskWindow.java} | 4 ++-- .../jetbrains/plugins/coursecreator/format/Task.java | 4 ++++ .../coursecreator/highlighting/TaskTextGutter.java | 4 ++-- 6 files changed, 16 insertions(+), 10 deletions(-) rename src/org/jetbrains/plugins/coursecreator/actions/{CreateTaskFileAction.java => CreateTaskFile.java} (94%) rename src/org/jetbrains/plugins/coursecreator/actions/{DeleteTaskWindowAction.java => DeleteTaskWindow.java} (94%) diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index 1d769776217c..ecb596c85598 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -45,7 +45,7 @@ - + diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java index 3df976913f13..0940135b97be 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java @@ -58,7 +58,7 @@ public class CreateTask extends DumbAwareAction { try { final PsiElement taskFile = FileTemplateUtil.createFromTemplate(template, "task.html", null, taskDirectory); final PsiElement testsFile = FileTemplateUtil.createFromTemplate(testsTemplate, "tests.py", null, taskDirectory); - final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskName + ".py", null, taskDirectory); + final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, "file1" + ".py", null, taskDirectory); final Task task = new Task(taskName); task.addTaskFile(taskPyFile.getContainingFile().getName(), size + 1); diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java similarity index 94% rename from src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java rename to src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java index 3dfb7acef9aa..5aafcebefd6e 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFileAction.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java @@ -22,9 +22,9 @@ import org.jetbrains.plugins.coursecreator.format.Course; import org.jetbrains.plugins.coursecreator.format.Lesson; import org.jetbrains.plugins.coursecreator.format.Task; -public class CreateTaskFileAction extends DumbAwareAction { +public class CreateTaskFile extends DumbAwareAction { - public CreateTaskFileAction() { + public CreateTaskFile() { super("Task File", "Create new Task File", PythonPsiApiIcons.PythonFile); } @@ -47,7 +47,9 @@ public class CreateTaskFileAction extends DumbAwareAction { final Lesson lesson = course.getLesson(lessonDir.getName()); final Task task = lesson.getTask(taskDir.getName()); - final String taskFileName = Messages.showInputDialog("Name:", "Task File Name", null, null, null); + final int index = task.getTaskFiles().size() + 1; + String generatedName = "file" + index; + final String taskFileName = Messages.showInputDialog("Name:", "Task File Name", null, generatedName, null); if (taskFileName == null) return; ApplicationManager.getApplication().runWriteAction(new Runnable() { @@ -56,7 +58,7 @@ public class CreateTaskFileAction extends DumbAwareAction { final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.py"); try { final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName + ".py", null, taskDir); - task.addTaskFile(taskPyFile.getContainingFile().getName(), task.task_files.size() + 1); + task.addTaskFile(taskPyFile.getContainingFile().getName(), index); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { diff --git a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindow.java similarity index 94% rename from src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java rename to src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindow.java index 67abe236214b..2724759a8c4f 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindowAction.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindow.java @@ -18,11 +18,11 @@ import org.jetbrains.plugins.coursecreator.format.*; import java.util.List; @SuppressWarnings("ComponentNotRegistered") -public class DeleteTaskWindowAction extends DumbAwareAction { +public class DeleteTaskWindow extends DumbAwareAction { @NotNull private final TaskWindow myTaskWindow; - public DeleteTaskWindowAction(@NotNull final TaskWindow taskWindow) { + public DeleteTaskWindow(@NotNull final TaskWindow taskWindow) { super("Delete task window","Delete task window", null); myTaskWindow = taskWindow; } diff --git a/src/org/jetbrains/plugins/coursecreator/format/Task.java b/src/org/jetbrains/plugins/coursecreator/format/Task.java index 9946a6afa1df..e6c085b5d6a1 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/Task.java +++ b/src/org/jetbrains/plugins/coursecreator/format/Task.java @@ -34,4 +34,8 @@ public class Task { public void setIndex(int index) { myIndex = index; } + + public Map getTaskFiles() { + return task_files; + } } diff --git a/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java b/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java index 009e9d67134c..80b267485192 100644 --- a/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java +++ b/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java @@ -7,7 +7,7 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.util.IconLoader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.coursecreator.actions.DeleteTaskWindowAction; +import org.jetbrains.plugins.coursecreator.actions.DeleteTaskWindow; import org.jetbrains.plugins.coursecreator.actions.ShowTaskWindowText; import org.jetbrains.plugins.coursecreator.format.TaskWindow; @@ -54,7 +54,7 @@ public class TaskTextGutter extends LineMarkerInfo.LineMarkerGutterIconRenderer @Override public ActionGroup getPopupMenuActions() { DefaultActionGroup group = new DefaultActionGroup(); - group.add(new DeleteTaskWindowAction(myTaskWindow)); + group.add(new DeleteTaskWindow(myTaskWindow)); return group; } } From a4650189f70153f7a8832d20954e167e2ef31a77 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Tue, 26 Aug 2014 15:10:10 +0400 Subject: [PATCH 22/24] validate hint file name --- .../coursecreator/actions/AddTaskWindow.java | 2 +- .../actions/ShowTaskWindowText.java | 3 +- .../ui/CreateTaskWindowDialog.java | 32 ++++++++++++++++--- .../ui/CreateTaskWindowPanel.java | 10 ++++++ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java index b4f1e23a2c72..ff88cea5fd42 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java @@ -52,7 +52,7 @@ public class AddTaskWindow extends DumbAwareAction { final Task task = lesson.getTask(taskDir.getName()); final TaskFile taskFile = task.getTaskFile(file.getName()); final TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length, model.getSelectedText()); - CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension()); + CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension(), taskFile.getTaskWindows().size() + 1); dlg.show(); if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) { return; diff --git a/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java b/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java index c755fa0eed5e..7c7e7fa727b8 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java @@ -36,8 +36,9 @@ public class ShowTaskWindowText extends DumbAwareAction { final Lesson lesson = course.getLesson(lessonDir.getName()); final Task task = lesson.getTask(taskDir.getName()); + final TaskFile taskFile = task.getTaskFile(file.getName()); //TODO: copy task window and return if modification canceled - CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, myTaskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension()); + CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, myTaskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension(), taskFile.getTaskWindows().size() + 1); dlg.show(); } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java index be598038a2d7..c7e8f715672c 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java @@ -4,6 +4,7 @@ import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.ValidationInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; @@ -28,23 +29,23 @@ public class CreateTaskWindowDialog extends DialogWrapper { } public CreateTaskWindowDialog(@NotNull final Project project, @NotNull final TaskWindow taskWindow, int lessonIndex, - int taskIndex, String taskFileName) { + int taskIndex, String taskFileName, int taskWindowIndex) { super(project, true); setTitle(TITLE); myTaskWindow = taskWindow; myPanel = new CreateTaskWindowPanel(this); + String generatedHintName = "lesson" + lessonIndex + "task" + taskIndex + taskFileName + "_" + taskWindowIndex; + myPanel.setGeneratedHintName(generatedHintName); if (taskWindow.getHintName() != null) { setHintText(project, taskWindow); } myProject = project; String taskWindowTaskText = taskWindow.getTaskText(); myPanel.setTaskWindowText(taskWindowTaskText != null ? taskWindowTaskText : ""); - int taskWindowIndex = taskWindow.getIndex() + 1; - String generatedHintName = "lesson" + lessonIndex + "task" + taskIndex + taskFileName + "_" + taskWindowIndex; - myPanel.setGeneratedHintName(generatedHintName); String hintName = taskWindow.getHintName(); myPanel.setHintName(hintName != null ? hintName : ""); init(); + initValidation(); } private void setHintText(Project project, TaskWindow taskWindow) { @@ -62,7 +63,6 @@ public class CreateTaskWindowDialog extends DialogWrapper { myPanel.doClick(); //myPanel.enableHint(true); myPanel.setHintText(hintText.toString()); - } catch (FileNotFoundException e) { LOG.error("created hint was not found", e); @@ -114,6 +114,9 @@ public class CreateTaskWindowDialog extends DialogWrapper { VirtualFile hintsDir = myProject.getBaseDir().findChild("hints"); if (hintsDir != null) { String hintName = myTaskWindow.getHintName(); + if (hintName == null) { + return; + } File hintFile = new File(hintsDir.getPath(), hintName); if (hintFile.exists()) { CCProjectService.deleteProjectFile(hintFile, myProject); @@ -128,4 +131,23 @@ public class CreateTaskWindowDialog extends DialogWrapper { protected JComponent createCenterPanel() { return myPanel; } + + @Nullable + @Override + public ValidationInfo doValidate() { + String name = myPanel.getHintName(); + VirtualFile hintsDir = myProject.getBaseDir().findChild("hints"); + if (hintsDir == null) { + return null; + } + VirtualFile child = hintsDir.findChild(name); + if (child == null) { + return null; + } + return myTaskWindow.getHintName() != null ? null : new ValidationInfo("Hint file with such filename already exists"); + } + + public void validateInput() { + super.initValidation(); + } } diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java index 966e27f534e7..21a7eb063c63 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java @@ -1,6 +1,9 @@ package org.jetbrains.plugins.coursecreator.ui; +import com.intellij.ui.DocumentAdapter; + import javax.swing.*; +import javax.swing.event.DocumentEvent; import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; @@ -33,6 +36,13 @@ public class CreateTaskWindowPanel extends JPanel { } } }); + + myHintName.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + myDialog.validateInput(); + } + }); } public void enableHint(boolean isEnable) { From 4de3f75ca60acba41558c51bdf47a0aafe6e8fd8 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Tue, 26 Aug 2014 15:35:42 +0400 Subject: [PATCH 23/24] validate course description while project being created --- .../coursecreator/CCProjectGenerator.java | 17 ++++++++++++++++- .../coursecreator/ui/CCNewProjectPanel.java | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java b/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java index 956b89f53c39..dbaa7265f5c1 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java @@ -1,5 +1,7 @@ package org.jetbrains.plugins.coursecreator; +import com.intellij.facet.ui.FacetEditorValidator; +import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.facet.ui.ValidationResult; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; @@ -74,13 +76,26 @@ public class CCProjectGenerator extends PythonProjectGenerator implements Direct @NotNull @Override public ValidationResult validate(@NotNull String s) { - return ValidationResult.OK; + String message = ""; + message = mySettingsPanel.getDescription().equals("") ? "Enter description" : message; + message = mySettingsPanel.getAuthor().equals("") ? "Enter author name" : message; + message = mySettingsPanel.getName().equals("") ? "Enter course name" : message; + return message.equals("")? ValidationResult.OK : new ValidationResult(message) ; } @Nullable @Override public JPanel extendBasePanel() throws ProcessCanceledException { mySettingsPanel = new CCNewProjectPanel(); + mySettingsPanel.registerValidators(new FacetValidatorsManager() { + public void registerValidator(FacetEditorValidator validator, JComponent... componentsToWatch) { + throw new UnsupportedOperationException(); + } + + public void validate() { + fireStateChanged(); + } + }); return mySettingsPanel.getMainPanel(); } } diff --git a/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java b/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java index 73c8167bc592..83a3458576b6 100644 --- a/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java +++ b/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java @@ -1,15 +1,19 @@ package org.jetbrains.plugins.coursecreator.ui; +import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.DocumentAdapter; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.event.DocumentEvent; public class CCNewProjectPanel { private JPanel myPanel; private JTextArea myDescription; private JTextField myName; private JTextField myAuthorField; + private FacetValidatorsManager myValidationManager; public CCNewProjectPanel() { @@ -17,6 +21,9 @@ public class CCNewProjectPanel { if (userName != null) { myAuthorField.setText(userName); } + myName.getDocument().addDocumentListener(new MyValidator()); + myDescription.getDocument().addDocumentListener(new MyValidator()); + myAuthorField.getDocument().addDocumentListener(new MyValidator()); } public JPanel getMainPanel() { @@ -37,4 +44,16 @@ public class CCNewProjectPanel { public String getAuthor() { return StringUtil.notNullize(myAuthorField.getText()); } + + public void registerValidators(FacetValidatorsManager manager) { + myValidationManager = manager; + } + + private class MyValidator extends DocumentAdapter { + + @Override + protected void textChanged(DocumentEvent e) { + myValidationManager.validate(); + } + } } From bbe9991f6af927829ee1bc0774faa791d6087d35 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Thu, 28 Aug 2014 14:24:18 +0400 Subject: [PATCH 24/24] add offset listeners --- .../CCEditorFactoryListener.java | 47 ++++++++++- .../coursecreator/CCProjectComponent.java | 5 +- .../coursecreator/CCProjectService.java | 21 +++++ .../coursecreator/StudyDocumentListener.java | 71 ++++++++++++++++ .../actions/CreateCourseArchive.java | 69 +++++++++++---- .../coursecreator/format/TaskFile.java | 84 ++++++++++++++++++- .../coursecreator/format/TaskWindow.java | 53 +++++++++++- 7 files changed, 325 insertions(+), 25 deletions(-) create mode 100644 src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java diff --git a/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java b/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java index 6fd8d9316e72..1eb8690aad22 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java +++ b/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java @@ -1,5 +1,6 @@ package org.jetbrains.plugins.coursecreator; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.EditorFactoryEvent; import com.intellij.openapi.editor.event.EditorFactoryListener; @@ -7,7 +8,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.coursecreator.format.Course; +import org.jetbrains.plugins.coursecreator.format.*; public class CCEditorFactoryListener implements EditorFactoryListener { @Override @@ -25,11 +26,55 @@ public class CCEditorFactoryListener implements EditorFactoryListener { if (course == null) { return; } + final VirtualFile taskDir = virtualFile.getParent(); + if (taskDir == null || !taskDir.getName().contains("task")) { + return; + } + final VirtualFile lessonDir = taskDir.getParent(); + if (lessonDir == null) return; + final Lesson lesson = course.getLesson(lessonDir.getName()); + final Task task = lesson.getTask(taskDir.getName()); + final TaskFile taskFile = task.getTaskFile(virtualFile.getName()); + TaskFileModificationListener listener = new TaskFileModificationListener(taskFile); + CCProjectService.addDocumentListener(editor.getDocument(), listener); + editor.getDocument().addDocumentListener(listener); CCProjectService.drawTaskWindows(virtualFile, editor, course); } @Override public void editorReleased(@NotNull EditorFactoryEvent event) { + Editor editor = event.getEditor(); + Document document = editor.getDocument(); + StudyDocumentListener listener = CCProjectService.getListener(document); + if (listener != null) { + document.removeDocumentListener(listener); + CCProjectService.removeListener(document); + } + editor.getMarkupModel().removeAllHighlighters(); + editor.getSelectionModel().removeSelection(); + } + private class TaskFileModificationListener extends StudyDocumentListener { + + private final TaskFile myTaskFile; + + public TaskFileModificationListener(TaskFile taskFile) { + super(taskFile); + myTaskFile = taskFile; + } + + @Override + protected void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change) { + int newLength = taskWindow.getReplacementLength() + change; + taskWindow.setReplacementLength(newLength <= 0 ? 0 : newLength); + if (fragment.equals("\n")) { + taskWindow.setReplacementLength(taskWindow.getLength() + 1); + } + } + + @Override + protected boolean needModify() { + return myTaskFile.isTrackChanges(); + } } } diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java b/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java index b3a7af9d0ab5..34de943d2ad0 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java @@ -3,10 +3,13 @@ package org.jetbrains.plugins.coursecreator; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.event.EditorFactoryEvent; +import com.intellij.openapi.editor.impl.EditorFactoryImpl; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -42,7 +45,7 @@ public class CCProjectComponent implements ProjectComponent { FileEditor fileEditor = FileEditorManager.getInstance(myProject).getSelectedEditor(file); if (fileEditor instanceof PsiAwareTextEditorImpl) { Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor(); - CCProjectService.drawTaskWindows(file, editor, course); + new CCEditorFactoryListener().editorCreated(new EditorFactoryEvent(new EditorFactoryImpl(ProjectManager.getInstance()), editor )); } } } diff --git a/src/org/jetbrains/plugins/coursecreator/CCProjectService.java b/src/org/jetbrains/plugins/coursecreator/CCProjectService.java index d9ff744bdccb..1e38bab865cb 100644 --- a/src/org/jetbrains/plugins/coursecreator/CCProjectService.java +++ b/src/org/jetbrains/plugins/coursecreator/CCProjectService.java @@ -21,6 +21,7 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -31,7 +32,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.coursecreator.format.*; import java.io.File; +import java.util.HashMap; import java.util.List; +import java.util.Map; @State(name = "CCProjectService", storages = { @@ -43,6 +46,7 @@ public class CCProjectService implements PersistentStateComponent { private static final Logger LOG = Logger.getInstance(CCProjectService.class.getName()); public Course myCourse; public static final String COURSE_ELEMENT = "course"; + private static final Map myDocumentListeners = new HashMap(); public void setCourse(@NotNull final Course course) { myCourse = course; @@ -114,4 +118,21 @@ public class CCProjectService implements PersistentStateComponent { taskWindow.drawHighlighter(editor); } } + + public static void addDocumentListener(Document document, StudyDocumentListener listener) { + myDocumentListeners.put(document, listener); + } + + public static StudyDocumentListener getListener(Document document) { + return myDocumentListeners.get(document); + } + + public static void removeListener(Document document) { + myDocumentListeners.remove(document); + } + + public static boolean indexIsValid(int index, List collection) { + int size = collection.size(); + return index >= 0 && index < size; + } } diff --git a/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java b/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java new file mode 100644 index 000000000000..d803e0e8fd97 --- /dev/null +++ b/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java @@ -0,0 +1,71 @@ +package org.jetbrains.plugins.coursecreator; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.impl.event.DocumentEventImpl; +import org.jetbrains.plugins.coursecreator.format.TaskFile; +import org.jetbrains.plugins.coursecreator.format.TaskWindow; + +/** + * author: liana + * data: 7/16/14. + * Listens changes in study files and updates + * coordinates of all the windows in current task file + */ +public abstract class StudyDocumentListener extends DocumentAdapter { + private final TaskFile myTaskFile; + private int oldLine; + private int oldLineStartOffset; + private TaskWindow myTaskWindow; + + public StudyDocumentListener(TaskFile taskFile) { + myTaskFile = taskFile; + } + + + //remembering old end before document change because of problems + // with fragments containing "\n" + @Override + public void beforeDocumentChange(DocumentEvent e) { + int offset = e.getOffset(); + int oldEnd = offset + e.getOldLength(); + Document document = e.getDocument(); + oldLine = document.getLineNumber(oldEnd); + oldLineStartOffset = document.getLineStartOffset(oldLine); + int line = document.getLineNumber(offset); + int offsetInLine = offset - document.getLineStartOffset(line); + LogicalPosition pos = new LogicalPosition(line, offsetInLine); + myTaskWindow = myTaskFile.getTaskWindow(document, pos); + + } + + @Override + public void documentChanged(DocumentEvent e) { + if (e instanceof DocumentEventImpl) { + if (!needModify()) { + return; + } + DocumentEventImpl event = (DocumentEventImpl)e; + Document document = e.getDocument(); + int offset = e.getOffset(); + int change = event.getNewLength() - event.getOldLength(); + if (myTaskWindow != null) { + updateTaskWindowLength(e.getNewFragment(), myTaskWindow, change); + } + int newEnd = offset + event.getNewLength(); + int newLine = document.getLineNumber(newEnd); + int lineChange = newLine - oldLine; + myTaskFile.incrementLines(oldLine + 1, lineChange); + int newEndOffsetInLine = offset + e.getNewLength() - document.getLineStartOffset(newLine); + int oldEndOffsetInLine = offset + e.getOldLength() - oldLineStartOffset; + myTaskFile.updateLine(lineChange, oldLine, newEndOffsetInLine, oldEndOffsetInLine); + } + } + + protected abstract void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change); + + protected abstract boolean needModify(); +} + diff --git a/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java b/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java index 76a8fdb6724c..05428f4e82d1 100644 --- a/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java +++ b/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java @@ -8,12 +8,9 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; @@ -22,13 +19,12 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.io.ZipUtil; import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.StudyDocumentListener; import org.jetbrains.plugins.coursecreator.format.*; import org.jetbrains.plugins.coursecreator.ui.CreateCourseArchiveDialog; import java.io.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.zip.ZipOutputStream; public class CreateCourseArchive extends DumbAwareAction { @@ -57,7 +53,6 @@ public class CreateCourseArchive extends DumbAwareAction { final CCProjectService service = CCProjectService.getInstance(project); final Course course = service.getCourse(); if (course == null) return; - generateJson(project); CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this); dlg.show(); if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) { @@ -65,7 +60,8 @@ public class CreateCourseArchive extends DumbAwareAction { } final VirtualFile baseDir = project.getBaseDir(); final Map lessons = course.getLessonsMap(); - List editorList = new ArrayList(); + //List editorList = new ArrayList(); + Map taskFiles = new HashMap(); for (Map.Entry lesson : lessons.entrySet()) { final VirtualFile lessonDir = baseDir.findChild(lesson.getKey()); if (lessonDir == null) continue; @@ -77,17 +73,16 @@ public class CreateCourseArchive extends DumbAwareAction { if (file == null) continue; final Document document = FileDocumentManager.getInstance().getDocument(file); if (document == null) continue; - final TaskFile taskFile = entry.getValue(); - for (final TaskWindow taskWindow : taskFile.getTaskWindows()) { + document.addDocumentListener(new InsertionListener(taskFile)); + taskFiles.put(file, taskFile); + taskFile.setTrackChanges(false); + Collections.sort(taskFile.getTaskWindows()); + for (int i = taskFile.getTaskWindows().size() - 1; i >=0 ; i--) { + final TaskWindow taskWindow = taskFile.getTaskWindows().get(i); final String taskText = taskWindow.getTaskText(); final int lineStartOffset = document.getLineStartOffset(taskWindow.line); final int offset = lineStartOffset + taskWindow.start; - final FileEditor[] editors = FileEditorManager.getInstance(project).getEditors(file); - if (editors.length > 0) { - editorList.add(editors[0]); - } - CommandProcessor.getInstance().executeCommand(project, new Runnable() { @Override public void run() { @@ -104,6 +99,7 @@ public class CreateCourseArchive extends DumbAwareAction { } } } + generateJson(project); try { File zipFile = new File(myLocationDir, myZipName + ".zip"); ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile))); @@ -124,10 +120,30 @@ public class CreateCourseArchive extends DumbAwareAction { LOG.error(e1); } - for (FileEditor fileEditor : editorList) { - UndoManager.getInstance(project).undo(fileEditor); + for (Map.Entry entry: taskFiles.entrySet()) { + TaskFile value = entry.getValue(); + final Document document = FileDocumentManager.getInstance().getDocument(entry.getKey()); + if (document == null) { + continue; + } + for (final TaskWindow taskWindow : value.getTaskWindows()){ + final int lineStartOffset = document.getLineStartOffset(taskWindow.line); + final int offset = lineStartOffset + taskWindow.start; + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + document.replaceString(offset, offset + taskWindow.length, taskWindow.getPossibleAnswer()); + FileDocumentManager.getInstance().saveDocument(document); + } + }); + } + }, "x", "qwe"); + } + value.setTrackChanges(true); } - VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); ProjectView.getInstance(project).refresh(); } @@ -162,4 +178,21 @@ public class CreateCourseArchive extends DumbAwareAction { } } } + + private class InsertionListener extends StudyDocumentListener { + + public InsertionListener(TaskFile taskFile) { + super(taskFile); + } + + @Override + protected void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change) { + //we don't need to update task window length + } + + @Override + protected boolean needModify() { + return true; + } + } } \ No newline at end of file diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java index 79f94d711143..85f0d91983f2 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java @@ -1,7 +1,11 @@ package org.jetbrains.plugins.coursecreator.format; import com.google.gson.annotations.Expose; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.LogicalPosition; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.CCProjectService; import java.util.ArrayList; import java.util.List; @@ -9,6 +13,15 @@ import java.util.List; public class TaskFile { @Expose public List task_windows = new ArrayList(); public int myIndex; + public boolean myTrackChanges = true; + + public boolean isTrackChanges() { + return myTrackChanges; + } + + public void setTrackChanges(boolean trackChanges) { + myTrackChanges = trackChanges; + } public TaskFile() {} @@ -25,7 +38,74 @@ public class TaskFile { myIndex = index; } - public int getIndex() { - return myIndex; + + /** + * @param pos position in editor + * @return task window located in specified position or null if there is no task window in this position + */ + @Nullable + public TaskWindow getTaskWindow(@NotNull final Document document, @NotNull final LogicalPosition pos) { + int line = pos.line; + if (line >= document.getLineCount()) { + return null; + } + int column = pos.column; + int offset = document.getLineStartOffset(line) + column; + for (TaskWindow tw : task_windows) { + if (tw.getLine() <= line) { + int twStartOffset = tw.getRealStartOffset(document); + final int length = tw.getReplacementLength() > 0 ? tw.getReplacementLength() : 0; + int twEndOffset = twStartOffset + length; + if (twStartOffset <= offset && offset <= twEndOffset) { + return tw; + } + } + } + return null; + } + + /** + * Updates task window lines + * + * @param startLine lines greater than this line and including this line will be updated + * @param change change to be added to line numbers + */ + public void incrementLines(int startLine, int change) { + for (TaskWindow taskTaskWindow : task_windows) { + if (taskTaskWindow.getLine() >= startLine) { + taskTaskWindow.setLine(taskTaskWindow.getLine() + change); + } + } + } + + /** + * Updates windows in specific line + * + * @param lineChange change in line number + * @param line line to be updated + * @param newEndOffsetInLine distance from line start to end of inserted fragment + * @param oldEndOffsetInLine distance from line start to end of changed fragment + */ + public void updateLine(int lineChange, int line, int newEndOffsetInLine, int oldEndOffsetInLine) { + for (TaskWindow w : task_windows) { + if ((w.getLine() == line) && (w.getStart() >= oldEndOffsetInLine)) { + int distance = w.getStart() - oldEndOffsetInLine; + boolean coveredByPrevTW = false; + int prevIndex = w.getIndex() - 1; + if (CCProjectService.indexIsValid(prevIndex, task_windows)) { + TaskWindow prevTW = task_windows.get(prevIndex); + if (prevTW.getLine() == line) { + int endOffset = prevTW.getStart() + prevTW.getLength(); + if (endOffset >= newEndOffsetInLine) { + coveredByPrevTW = true; + } + } + } + if (lineChange != 0 || newEndOffsetInLine <= w.getStart() || coveredByPrevTW) { + w.setStart(distance + newEndOffsetInLine); + w.setLine(line + lineChange); + } + } + } } } diff --git a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index 92c265ed8e9f..cb6418ec75d7 100644 --- a/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -1,11 +1,13 @@ package org.jetbrains.plugins.coursecreator.format; import com.google.gson.annotations.Expose; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.markup.HighlighterLayer; import com.intellij.openapi.editor.markup.HighlighterTargetArea; +import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -14,7 +16,7 @@ import org.jetbrains.plugins.coursecreator.CCProjectService; import java.io.File; -public class TaskWindow { +public class TaskWindow implements Comparable{ @Expose public int line; @Expose public int start; @@ -71,8 +73,11 @@ public class TaskWindow { int endOffset = startOffset + myReplacementLength; TextAttributes defaultTestAttributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.LIVE_TEMPLATE_ATTRIBUTES); - editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST + 1, defaultTestAttributes, - HighlighterTargetArea.EXACT_RANGE); + RangeHighlighter highlighter = + editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST + 1, defaultTestAttributes, + HighlighterTargetArea.EXACT_RANGE); + highlighter.setGreedyToLeft(true); + highlighter.setGreedyToRight(true); } public int getIndex() { @@ -83,4 +88,46 @@ public class TaskWindow { myIndex = index; } + + public void setReplacementLength(int replacementLength) { + myReplacementLength = replacementLength; + } + + public int getLine() { + return line; + } + + public int getRealStartOffset(Document document) { + return document.getLineStartOffset(line) + start; + } + + public void setLine(int line) { + this.line = line; + } + + public int getStart() { + return start; + } + + public void setStart(int start) { + this.start = start; + } + + @Override + public int compareTo(Object o) { + TaskWindow taskWindow = (TaskWindow)o; + int lineDiff = line - taskWindow.line; + if (lineDiff == 0) { + return start - taskWindow.start; + } + return lineDiff; + } + + public String getPossibleAnswer() { + return possible_answer; + } + + public int getLength() { + return length; + } } \ No newline at end of file