added ability to create lessons in the middle of course (part of EDU-331)
authorLiana Bakradze <liana.bakradze@jetbrains.com>
Tue, 11 Aug 2015 13:48:09 +0000 (16:48 +0300)
committerLiana Bakradze <liana.bakradze@jetbrains.com>
Tue, 11 Aug 2015 13:48:09 +0000 (16:48 +0300)
12 files changed:
python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java
python/educational/course-creator/course-creator.iml
python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java
python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java
python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java
python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemDialog.java [new file with mode: 0644]
python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.form [new file with mode: 0644]
python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java [new file with mode: 0644]
python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.form [new file with mode: 0644]
python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.java [new file with mode: 0644]
python/educational/src/com/jetbrains/edu/EduNames.java
python/educational/src/com/jetbrains/edu/EduUtils.java

index 1f82439e6ca38b3dc0b4d5feade03cb123ed5c49..3b8c4154f24b050db98a4b34d9937ff144dcec4a 100644 (file)
@@ -8,6 +8,7 @@ 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.diagnostic.Logger;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.progress.ProcessCanceledException;
 import com.intellij.openapi.project.Project;
@@ -30,6 +31,7 @@ import javax.swing.*;
 
 
 public class PyCCProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator {
+  private static final Logger LOG = Logger.getInstance(PyCCProjectGenerator.class);
   private CCNewProjectPanel mySettingsPanel;
 
   @Nls
@@ -83,7 +85,11 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire
         catch (Exception ignored) {
         }
         DirectoryUtil.createSubdirectories(EduNames.HINTS, projectDir, "\\/");
-        final PsiDirectory lessonDir = CCCreateLesson.createLessonDir(project, 1, null, null);
+        PsiDirectory lessonDir = CCCreateLesson.createLesson(null, project, baseDir, course);
+        if (lessonDir == null) {
+          LOG.error("Failed to create lesson");
+          return;
+        }
         CCCreateTask.createTask(null, project, lessonDir, false);
       }
     }.execute();
index b54f05e2bd7e906b60057a023a3864855219cbd1..46da5de456289b761d79801a9a20148f1357da8e 100644 (file)
@@ -12,5 +12,6 @@
     <orderEntry type="library" name="gson" level="project" />
     <orderEntry type="module" module-name="lang-impl" />
     <orderEntry type="module" module-name="educational" />
+    <orderEntry type="library" name="guava-tools" level="project" />
   </component>
 </module>
\ No newline at end of file
index 095cc44746b1a4acac5fd2018b93c65be08b28f4..a28b44709bae398302d3b2a76301f26f9e25a008 100644 (file)
@@ -55,7 +55,7 @@ class CCFileDeletedListener extends VirtualFileAdapter {
       public StudyOrderable fun(VirtualFile file) {
         return course.getLesson(file.getName());
       }
-    }, removedLesson, EduNames.LESSON);
+    }, removedLesson.getIndex(), EduNames.LESSON, -1);
     course.getLessons().remove(removedLesson);
   }
 
@@ -77,7 +77,7 @@ class CCFileDeletedListener extends VirtualFileAdapter {
       public StudyOrderable fun(VirtualFile file) {
         return lesson.getTask(file.getName());
       }
-    }, task, EduNames.TASK);
+    }, task.getIndex(), EduNames.TASK, -1);
     ModifiableRootModel model = EduUtils.getModel(lessonDir, project);
     if (model == null) {
       return;
index aaaccb4939c8b73be1ae7b84b75aa19b522ee127..9148afad4a3272af266ee89776e9e58ec9046d8d 100644 (file)
@@ -1,5 +1,7 @@
 package com.jetbrains.edu.coursecreator;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
 import com.intellij.lang.Language;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
@@ -7,12 +9,14 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.util.Function;
+import com.jetbrains.edu.EduUtils;
 import com.jetbrains.edu.courseFormat.Course;
 import com.jetbrains.edu.courseFormat.StudyOrderable;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.IOException;
+import java.util.*;
 
 public class CCUtils {
   private static final Logger LOG = Logger.getInstance(CCUtils.class);
@@ -20,7 +24,7 @@ public class CCUtils {
   @Nullable
   public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) {
     Language language = Language.findLanguageByID(course.getLanguage());
-    return language == null ? null :  CCLanguageManager.INSTANCE.forLanguage(language);
+    return language == null ? null : CCLanguageManager.INSTANCE.forLanguage(language);
   }
 
   public static boolean isAnswerFile(PsiElement element) {
@@ -34,37 +38,54 @@ public class CCUtils {
   /**
    * This method decreases index and updates directory names of
    * all tasks/lessons that have higher index than specified object
-   * @param dirs directories that are used to get tasks/lessons
+   *
+   * @param dirs              directories that are used to get tasks/lessons
    * @param getStudyOrderable function that is used to get task/lesson from VirtualFile. This function can return null
-   * @param thresholdObject task/lesson which index is used as threshold
-   * @param prefix task or lesson directory name prefix
+   * @param threshold         index is used as threshold
+   * @param prefix            task or lesson directory name prefix
    */
   public static void updateHigherElements(VirtualFile[] dirs,
-                                          @NotNull Function<VirtualFile, StudyOrderable> getStudyOrderable,
-                                          @NotNull final StudyOrderable thresholdObject,
-                                          final String prefix) {
-    int threshold = thresholdObject.getIndex();
-    for (final VirtualFile dir : dirs) {
-      final StudyOrderable orderable = getStudyOrderable.fun(dir);
-      if (orderable == null) {
-        continue;
-      }
-      int index = orderable.getIndex();
-      if (index > threshold) {
-        final int newIndex = index - 1;
-        orderable.setIndex(newIndex);
-        ApplicationManager.getApplication().runWriteAction(new Runnable() {
-          @Override
-          public void run() {
-            try {
-              dir.rename(this, prefix + newIndex);
-            }
-            catch (IOException e) {
-              LOG.error(e);
-            }
+                                          @NotNull final Function<VirtualFile, StudyOrderable> getStudyOrderable,
+                                          final int threshold,
+                                          final String prefix,
+                                          final int delta) {
+    ArrayList<VirtualFile> dirsToRename = new ArrayList<VirtualFile>
+      (Collections2.filter(Arrays.asList(dirs), new Predicate<VirtualFile>() {
+        @Override
+        public boolean apply(VirtualFile dir) {
+          final StudyOrderable orderable = getStudyOrderable.fun(dir);
+          if (orderable == null) {
+            return false;
           }
-        });
+          int index = orderable.getIndex();
+          return index > threshold;
+        }
+      }));
+    Collections.sort(dirsToRename, new Comparator<VirtualFile>() {
+      @Override
+      public int compare(VirtualFile o1, VirtualFile o2) {
+        StudyOrderable orderable1 = getStudyOrderable.fun(o1);
+        StudyOrderable orderable2 = getStudyOrderable.fun(o2);
+        //if we delete some dir we should start increasing numbers in dir names from the end
+        return (-delta) * EduUtils.INDEX_COMPARATOR.compare(orderable1, orderable2);
       }
+    });
+
+    for (final VirtualFile dir : dirsToRename) {
+      final StudyOrderable orderable = getStudyOrderable.fun(dir);
+      final int newIndex = orderable.getIndex() + delta;
+      orderable.setIndex(newIndex);
+      ApplicationManager.getApplication().runWriteAction(new Runnable() {
+        @Override
+        public void run() {
+          try {
+            dir.rename(this, prefix + newIndex);
+          }
+          catch (IOException e) {
+            LOG.error(e);
+          }
+        }
+      });
     }
   }
 }
index a305acffeaf6ec1fc000a6820b646323185af217..7259d34afa8023a45daa9a049a3637047516b1a1 100644 (file)
@@ -7,30 +7,33 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.LangDataKeys;
 import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.diagnostic.Logger;
 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.psi.PsiDirectory;
 import com.intellij.psi.PsiManager;
+import com.intellij.util.Function;
 import com.intellij.util.PlatformIcons;
 import com.jetbrains.edu.EduNames;
 import com.jetbrains.edu.EduUtils;
 import com.jetbrains.edu.courseFormat.Course;
 import com.jetbrains.edu.courseFormat.Lesson;
+import com.jetbrains.edu.courseFormat.StudyOrderable;
 import com.jetbrains.edu.coursecreator.CCProjectService;
+import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.coursecreator.ui.CCCreateStudyItemDialog;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.io.IOException;
-import java.util.List;
+import java.util.Collections;
 
 public class CCCreateLesson extends DumbAwareAction {
-  private static final Logger LOG = Logger.getInstance(CCCreateLesson.class.getName());
+  public static final String TITLE = "Create New " + EduNames.LESSON_TITLED;
 
   public CCCreateLesson() {
-    super("Lesson", "Create new Lesson", PlatformIcons.DIRECTORY_CLOSED_ICON);
+    super(EduNames.LESSON_TITLED, TITLE, PlatformIcons.DIRECTORY_CLOSED_ICON);
   }
 
   @Override
@@ -47,85 +50,87 @@ public class CCCreateLesson extends DumbAwareAction {
     if (course == null) {
       return;
     }
-    //"Create Lesson" invoked from project root creates new lesson as last lesson
-    if (directory.getVirtualFile().equals(project.getBaseDir())) {
-      final int size = course.getLessons().size();
-      createLesson(project, size + 1, view);
-      return;
-    }
-    //"Create Lesson" invoked from any of lesson directories creates new lesson as next lesson
-    Lesson lesson = course.getLesson(directory.getName());
-    if (lesson != null) {
-      int index = lesson.getIndex();
-      List<Lesson> lessons = course.getLessons();
-      int lessonNum = lessons.size();
-      for (int i = lessonNum; i >= index + 1; i--) {
-        updateLesson(project, EduNames.LESSON, i);
-      }
-      final PsiDirectory parent = directory.getParent();
-      if (parent == null) {
-        return;
-      }
-      createLesson(project, index + 1, view);
-    }
+    createLesson(view, project, directory.getVirtualFile(), course);
   }
 
-  private static void createLesson(@NotNull final Project project,
-                                   final int index,
-                                   final IdeView view) {
-    final String lessonName = Messages.showInputDialog("Name:", "Lesson Name", null, EduNames.LESSON + index, null);
-    if (lessonName == null) {
-      return;
+  @Nullable
+  public static PsiDirectory createLesson(@Nullable IdeView view, @NotNull final Project project,
+                                          @NotNull final VirtualFile directory, @NotNull final Course course) {
+    Lesson lesson = getLesson(directory, project, course, view);
+    if (lesson == null) {
+      return null;
     }
-    ApplicationManager.getApplication().runWriteAction(new Runnable() {
+    VirtualFile courseDir = project.getBaseDir();
+    int lessonIndex = lesson.getIndex();
+    CCUtils.updateHigherElements(courseDir.getChildren(), new Function<VirtualFile, StudyOrderable>() {
       @Override
-      public void run() {
-        createLessonDir(project, index, lessonName, view);
+      public StudyOrderable fun(VirtualFile file) {
+        return course.getLesson(file.getName());
       }
-    });
+    }, lessonIndex - 1, EduNames.LESSON, 1);
+    course.addLesson(lesson);
+    Collections.sort(course.getLessons(), EduUtils.INDEX_COMPARATOR);
+    return createLessonDir(project, lessonIndex, view);
   }
 
-  private void updateLesson(@NotNull final Project project, final String lessonDirName, int i) {
-    final VirtualFile lessonDir = project.getBaseDir().findChild(lessonDirName + i);
-    if (lessonDir == null) {
-      return;
+  @Nullable
+  private static Lesson getLesson(@NotNull final VirtualFile sourceDirectory,
+                                  @NotNull final Project project,
+                                  @NotNull final Course course,
+                                  @Nullable IdeView view) {
+
+    VirtualFile courseDir = project.getBaseDir();
+    String lessonName;
+    int lessonIndex;
+    if (sourceDirectory.equals(courseDir)) {
+      lessonIndex = course.getLessons().size() + 1;
+      String suggestedName = EduNames.LESSON + lessonIndex;
+      lessonName = view == null ? suggestedName : Messages.showInputDialog("Name:", TITLE, null, suggestedName, null);
+    } else {
+      Lesson sourceLesson = course.getLesson(sourceDirectory.getName());
+      if (sourceLesson == null) {
+        return null;
+      }
+      final int index = sourceLesson.getIndex();
+      CCCreateStudyItemDialog dialog = new CCCreateStudyItemDialog(project, EduNames.LESSON, sourceLesson.getName(), index);
+      dialog.show();
+      if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
+        return null;
+      }
+      lessonName = dialog.getName();
+      lessonIndex = index + dialog.getIndexDelta();
     }
-    final CCProjectService service = CCProjectService.getInstance(project);
-    Lesson l = service.getCourse().getLesson(lessonDir.getName());
-    if (l == null) {
-      return;
+    if (lessonName == null) {
+      return null;
     }
-    l.setIndex(l.getIndex() + 1);
-    final int next = i + 1;
+    return createAndInitLesson(course, lessonName, lessonIndex);
+  }
+
+  @NotNull
+  private static Lesson createAndInitLesson(@NotNull final Course course, @NotNull String lessonName, int lessonIndex) {
+    Lesson lesson = new Lesson();
+    lesson.setName(lessonName);
+    lesson.setCourse(course);
+    lesson.setIndex(lessonIndex);
+    return lesson;
+  }
+
+  @Nullable
+  public static PsiDirectory createLessonDir(@NotNull final Project project, final int index, final IdeView view) {
+    final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(project.getBaseDir());
+    final PsiDirectory[] lessonDirectory = new PsiDirectory[1];
     ApplicationManager.getApplication().runWriteAction(new Runnable() {
       @Override
       public void run() {
-        try {
-          lessonDir.rename(this, lessonDirName + next);
-        }
-        catch (IOException e1) {
-          LOG.error(e1);
-        }
+        lessonDirectory[0] = DirectoryUtil.createSubdirectories(EduNames.LESSON + index, projectDir, "\\/");
       }
     });
-  }
-
-  @Nullable
-  public static PsiDirectory createLessonDir(@NotNull final Project project, int index, String name, final IdeView view) {
-    String lessonFolderName = EduNames.LESSON + index;
-    final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(project.getBaseDir());
-    final PsiDirectory lessonDirectory = DirectoryUtil.createSubdirectories(EduNames.LESSON + index, projectDir, "\\/");
-    final CCProjectService service = CCProjectService.getInstance(project);
-    if (lessonDirectory != null) {
+    if (lessonDirectory[0] != null) {
       if (view != null) {
-        view.selectElement(lessonDirectory);
+        view.selectElement(lessonDirectory[0]);
       }
-      final Lesson lesson = new Lesson();
-      lesson.setName(name != null ? name : lessonFolderName);
-      lesson.setIndex(index);
-      service.getCourse().addLesson(lesson);
     }
-    return lessonDirectory;
+    return lessonDirectory[0];
   }
 
   @Override
@@ -151,9 +156,9 @@ public class CCCreateLesson extends DumbAwareAction {
     }
     final CCProjectService service = CCProjectService.getInstance(project);
     Course course = service.getCourse();
-
-    if (directory != null && course != null && project.getBaseDir().equals(directory)) {
-      EduUtils.enableAction(event, false);
+    if (directory != null && course != null && course.getLesson(directory.getName()) != null) {
+      EduUtils.enableAction(event, true);
+      return;
     }
     EduUtils.enableAction(event, false);
   }
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemDialog.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemDialog.java
new file mode 100644 (file)
index 0000000..f986357
--- /dev/null
@@ -0,0 +1,33 @@
+package com.jetbrains.edu.coursecreator.ui;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class CCCreateStudyItemDialog extends DialogWrapper {
+  private final CCCreateStudyItemPanel myPanel;
+
+  public CCCreateStudyItemDialog(@Nullable Project project, String itemName, String thresholdName, int thresholdIndex) {
+    super(project);
+    myPanel = new CCCreateStudyItemPanel(itemName, thresholdName, thresholdIndex);
+    setTitle("Create New " + StringUtil.toTitleCase(itemName));
+    init();
+  }
+
+  @Nullable
+  @Override
+  protected JComponent createCenterPanel() {
+   return myPanel;
+  }
+
+  public String getName() {
+    return myPanel.getItemName();
+  }
+
+  public int getIndexDelta() {
+    return myPanel.getIndexDelta();
+  }
+}
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.form b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.form
new file mode 100644 (file)
index 0000000..c1af900
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.edu.coursecreator.ui.CCCreateStudyItemPanel">
+  <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="608ec" class="javax.swing.JTextField" binding="myNameField">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="4" anchor="0" fill="1" indent="0" use-parent-layout="false">
+            <minimum-size width="150" height="-1"/>
+            <preferred-size width="300" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="fdff2" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Name:"/>
+        </properties>
+      </component>
+      <vspacer id="e16c7">
+        <constraints>
+          <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false">
+            <preferred-size width="-1" height="10"/>
+          </grid>
+        </constraints>
+      </vspacer>
+      <nested-form id="92938" form-file="com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.form" binding="myPositionalPanel" custom-create="true">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+      </nested-form>
+    </children>
+  </grid>
+</form>
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java
new file mode 100644 (file)
index 0000000..adf934b
--- /dev/null
@@ -0,0 +1,33 @@
+package com.jetbrains.edu.coursecreator.ui;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class CCCreateStudyItemPanel extends JPanel {
+  private final String myItemName;
+  private JPanel myPanel;
+  private JTextField myNameField;
+  private CCItemPositionPanel myPositionalPanel;
+  private String myThresholdName;
+  private int myThresholdIndex;
+
+  public CCCreateStudyItemPanel(String itemName, String thresholdName, int thresholdIndex) {
+    myThresholdName = thresholdName;
+    myThresholdIndex = thresholdIndex;
+    myItemName = itemName;
+    myNameField.setText(itemName + thresholdIndex);
+    add(myPanel, BorderLayout.CENTER);
+  }
+
+  private void createUIComponents() {
+    myPositionalPanel = new CCItemPositionPanel(myItemName, myThresholdName, myThresholdIndex);
+  }
+
+  public String getItemName() {
+    return myNameField.getText();
+  }
+
+  public int getIndexDelta() {
+    return myPositionalPanel.getIndexDelta();
+  }
+}
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.form b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.form
new file mode 100644 (file)
index 0000000..922a210
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.edu.coursecreator.ui.CCItemPositionPanel">
+  <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="e37cf" class="com.intellij.ui.components.JBRadioButton" binding="myBeforeButton" default-binding="true">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="before"/>
+        </properties>
+      </component>
+      <component id="57593" class="com.intellij.ui.components.JBRadioButton" binding="myAfterButton" default-binding="true">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="after"/>
+        </properties>
+      </component>
+      <component id="cedb7" class="com.intellij.ui.components.JBLabel" binding="mySpecifyPositionLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value=""/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.java
new file mode 100644 (file)
index 0000000..72fa366
--- /dev/null
@@ -0,0 +1,30 @@
+package com.jetbrains.edu.coursecreator.ui;
+
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.components.JBRadioButton;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class CCItemPositionPanel extends JPanel {
+  private JPanel myPanel;
+  private JBRadioButton myBeforeButton;
+  private JBRadioButton myAfterButton;
+  private JBLabel mySpecifyPositionLabel;
+
+  public CCItemPositionPanel(String itemName, String thresholdName, int thresholdNum) {
+    this.add(myPanel, BorderLayout.CENTER);
+    mySpecifyPositionLabel.setText("Specify " + itemName + " position:");
+    String postfix = itemName + " " + thresholdNum + " '" + thresholdName + "'";
+    ButtonGroup group = new ButtonGroup();
+    group.add(myBeforeButton);
+    group.add(myAfterButton);
+    myBeforeButton.setText("before " + postfix);
+    myAfterButton.setText("after " + postfix);
+    myBeforeButton.setSelected(true);
+  }
+
+  public int getIndexDelta() {
+    return myBeforeButton.isSelected() ? 0 : 1;
+  }
+}
index 4c45d9e33a7e21fbde5e1da85e9fe48283c688fa..e6b2fd99a427266ee25e29b1873cdaca2e54bae1 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.jetbrains.edu;
 
+import com.intellij.openapi.util.text.StringUtil;
 import org.jetbrains.annotations.NonNls;
 
 @NonNls
@@ -22,6 +23,7 @@ public class EduNames {
   public static final String TASK_HTML = "task.html";
   public static final String HINTS = "hints";
   public static final String LESSON = "lesson";
+  public static final String LESSON_TITLED = StringUtil.toTitleCase(LESSON);
   public static final String COURSE = "course";
   public static final String TEST_TAB_NAME = "test";
   public static final String USER_TEST_INPUT = "input";
index c4b305d11d47883f1412f6a090ba5db1c6717aad..de3d7f0a226421c5243cb3fcea6695f5f079b7ea 100644 (file)
@@ -19,6 +19,7 @@ import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileManager;
 import com.jetbrains.edu.courseFormat.AnswerPlaceholder;
+import com.jetbrains.edu.courseFormat.StudyOrderable;
 import com.jetbrains.edu.courseFormat.Task;
 import com.jetbrains.edu.courseFormat.TaskFile;
 import org.jetbrains.annotations.NotNull;
@@ -28,6 +29,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.Map;
 
 public class EduUtils {
@@ -35,6 +37,13 @@ public class EduUtils {
   }
   private static final Logger LOG = Logger.getInstance(EduUtils.class.getName());
 
+  public static Comparator<StudyOrderable> INDEX_COMPARATOR = new Comparator<StudyOrderable>() {
+    @Override
+    public int compare(StudyOrderable o1, StudyOrderable o2) {
+      return o1.getIndex() - o2.getIndex();
+    }
+  };
+
   public static void commitAndSaveModel(final ModifiableRootModel model) {
     ApplicationManager.getApplication().runWriteAction(new Runnable() {
       @Override
@@ -83,10 +92,14 @@ public class EduUtils {
    * @return index of object
    */
   public static int getIndex(@NotNull final String fullName, @NotNull final String logicalName) {
-    if (!fullName.contains(logicalName)) {
+    if (!fullName.startsWith(logicalName)) {
+      return -1;
+    }
+    try {
+      return Integer.parseInt(fullName.substring(logicalName.length())) - 1;
+    } catch(NumberFormatException e) {
       return -1;
     }
-    return Integer.parseInt(fullName.substring(logicalName.length())) - 1;
   }
 
   public static boolean indexIsValid(int index, Collection collection) {