Snapshot idea/138.1980 from git://git.jetbrains.org/idea/community.git

Change-Id: Ib567c9c152d770212a7a3db20fbf591c210920bd
diff --git a/python/edu/build/desktop.ini b/python/edu/build/desktop.ini
index f56d43c..0ea32de 100644
--- a/python/edu/build/desktop.ini
+++ b/python/edu/build/desktop.ini
@@ -28,7 +28,7 @@
 [Field 4]
 Type=RadioButton
 Left=5
-Right=45
+Right=100
 Top=50
 Bottom=60
 State=1
@@ -36,8 +36,8 @@
 
 [Field 5]
 Type=RadioButton
-Left=95
-Right=135
+Left=120
+Right=-1
 Top=50
 Bottom=60
 State=0
diff --git a/python/edu/build/idea.nsi b/python/edu/build/idea.nsi
index d9903c9..05c3757 100644
--- a/python/edu/build/idea.nsi
+++ b/python/edu/build/idea.nsi
@@ -23,6 +23,7 @@
 ;------------------------------------------------------------------------------
 !include "MUI2.nsh"
 !include "FileFunc.nsh"
+!include "TextFunc.nsh"
 !include UAC.nsh
 !include "InstallOptions.nsh"
 !include StrFunc.nsh
@@ -32,6 +33,7 @@
 ${UnStrLoc}
 ${UnStrRep}
 ${StrRep}
+${StrTok}
 
 ReserveFile "desktop.ini"
 ReserveFile "DeleteSettings.ini"
@@ -700,49 +702,69 @@
     '$INSTDIR\bin\${PRODUCT_EXE_FILE} "%1"'
 FunctionEnd
 
+Function getPythonInfo
+  ClearErrors
+  FileOpen $3 $INSTDIR\python\python.txt r
+  IfErrors cantOpenFile ;file can not be open.  
+  ;get python2 info
+  FileRead $3 $4
+  ${StrTok} $0 $4 " " "1" "1"
+  ${StrTok} $1 $4 " " "2" "1"
+  ;get python3 info
+  FileRead $3 $4
+  ${StrTok} $R0 $4 " " "1" "1"
+  ${StrTok} $R1 $4 " " "2" "1"
+  goto Done
+cantOpenFile:
+  MessageBox MB_OK|MB_ICONEXCLAMATION "python.txt is not exist. Python will not be downloaded."
+  StrCpy $0 "Error"
+Done:
+FunctionEnd
+
+
 ;------------------------------------------------------------------------------
 ; Installer sections
 ;------------------------------------------------------------------------------
 Section "IDEA Files" CopyIdeaFiles
-;  StrCpy $baseRegKey "HKCU"
-;  !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 3" "State"
-;  StrCmp $R2 1 continue_for_current_user
-;  SetShellVarContext all
-;  StrCpy $baseRegKey "HKLM"
-;  continue_for_current_user:
-
-; create shortcuts
-
+  ${LineSum} "$INSTDIR\python\python.txt" $R0
+  IfErrors cantOpenFile
+  StrCmp $R0 "2" getPythonInfo ;info about 2 and 3 version of python
+cantOpenFile:  
+  MessageBox MB_OK|MB_ICONEXCLAMATION "python.txt is invalid. Python will not be downloaded."
+  goto skip_python_download
+getPythonInfo:  
+  Call getPythonInfo
+  StrCmp $0 "Error" skip_python_download
   !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 4" "State"
   StrCmp $R2 1 "" python3
-  StrCpy $R2 "2.7"
+  StrCpy $R2 $0
+  StrCpy $R3 $1
   goto check_python
 python3:  
-  StrCpy $R2 "3.4"
+  StrCpy $R2 $R0
+  StrCpy $R3 $R1
 check_python:  
-  ReadRegStr $1 "HKCU" "Software\Python\PythonCore\$R2\InstallPath" $0
+  ReadRegStr $1 "HKCU" "Software\Python\PythonCore\$R2\InstallPath" ""
   StrCmp $1 "" installation_for_all_users
   goto verefy_python_launcher
 installation_for_all_users:
-  ReadRegStr $1 "HKLM" "Software\Python\PythonCore\$R2\InstallPath" $0
+  ReadRegStr $1 "HKLM" "Software\Python\PythonCore\$R2\InstallPath" ""
   StrCmp $1 "" get_python
 verefy_python_launcher:
   IfFileExists $1\python.exe python_exists get_python
-
-get_python:
-  CreateDirectory "$INSTDIR\python"
-  StrCmp $R2 "2.7" get_python2
-  inetc::get "https://www.python.org/ftp/python/3.4.1/python-3.4.1.amd64.msi" "$INSTDIR\python\python_$R2.msi"
+get_python:  
+  inetc::get "$R3" "$INSTDIR\python\python_$R2.msi"
   goto validate_download
-get_python2:  
-  inetc::get "http://www.python.org/ftp/python/2.7.8/python-2.7.8.msi" "$INSTDIR\python\python_$R2.msi"
-validate_download:  
+validate_download:
   Pop $0
   ${If} $0 == "OK" 
-    ExecCmd::exec 'msiexec /i "$INSTDIR\python\python_$R2.msi" /quiet /qn /norestart /log "$INSTDIR\python\python_$R2_silent.log"'
+    ExecCmd::exec 'msiexec /i "$INSTDIR\python\python_$R2.msi" /quiet /qn /norestart'
+  ${Else}
+    MessageBox MB_OK|MB_ICONEXCLAMATION "The download is failed"
   ${EndIf}
-
 python_exists:  
+skip_python_download:  
+; create shortcuts
   !insertmacro INSTALLOPTIONS_READ $R2 "Desktop.ini" "Field 1" "State"
   StrCmp $R2 1 "" skip_desktop_shortcut
   CreateShortCut "$DESKTOP\${PRODUCT_FULL_NAME_WITH_VER}.lnk" \
@@ -882,6 +904,21 @@
   ${If} $0 == "1"
     !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 2" "Flags" "DISABLED"
   ${EndIf}
+  CreateDirectory "$INSTDIR\python"
+  inetc::get "http://www.jetbrains.com/updates/python.txt" "$INSTDIR\python\python.txt"
+  ${LineSum} "$INSTDIR\python\python.txt" $R0
+  IfErrors cantOpenFile
+  StrCmp $R0 "2" getPythonInfo
+cantOpenFile:  
+  MessageBox MB_OK|MB_ICONEXCLAMATION "python.txt is not exist. Python will not be downloaded."
+  goto association
+getPythonInfo:  
+  Call getPythonInfo
+  StrCmp $0 "Error" association
+  !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 4" "Text" "Python $0"
+  !insertmacro INSTALLOPTIONS_WRITE "Desktop.ini" "Field 5" "Text" "Python $R0"
+
+association:
   StrCmp "${ASSOCIATION}" "NoAssociation" skip_association
   StrCpy $R0 6
   push "${ASSOCIATION}"
diff --git a/python/edu/build/paths.nsi b/python/edu/build/paths.nsi
index 910f2b3..d854929 100644
--- a/python/edu/build/paths.nsi
+++ b/python/edu/build/paths.nsi
@@ -1,5 +1,5 @@
 ; Installer images
-!define IMAGES_LOCATION ${COMMUNITY_DIR}\python\build\resources
+!define IMAGES_LOCATION ${COMMUNITY_DIR}\python\edu\build\resources
 ;!define LICENSE_FILE ${BASE_DIR}\python\license\PyCharm_Preview_License
 !define PRODUCT_PROPERTIES_FILE ${BASE_DIR}\out\pycharmEDU\layout\bin\idea.properties
 !define PRODUCT_VM_OPTIONS_NAME pycharm.exe.vmoptions
diff --git a/python/edu/build/plugin-list.txt b/python/edu/build/plugin-list.txt
index bc4b94c..60ecf3f 100644
--- a/python/edu/build/plugin-list.txt
+++ b/python/edu/build/plugin-list.txt
@@ -4,4 +4,5 @@
 github
 IntelliLang
 IntelliLang-python
-learn-python
\ No newline at end of file
+learn-python
+course-creator
\ No newline at end of file
diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant
index 2ef0bed..28d829e 100644
--- a/python/edu/build/pycharm_edu_build.gant
+++ b/python/edu/build/pycharm_edu_build.gant
@@ -22,14 +22,17 @@
 includeTargets << new File("$home/build/scripts/ultimate_utils.gant")
 
 requireProperty("buildNumber", requireProperty("build.number", snapshot))
-setProperty("buildName", "PE-$buildNumber")
+setProperty("buildName", "EDU-$buildNumber")
 setProperty("ch", "$home/community")
 setProperty("pythonCommunityHome", "$ch/python")
 setProperty("pythonEduHome", "$ch/python/edu")
+requireProperty("jdk_bundled_mac", "1.7")
+def jdk_bundled_version = p("jdk_bundled_mac") == "1.8" ? "jdk8_mac_redist.tar" : "jdk_mac_redist.tar"
+ant.copy(file: "${home}/build/jdk/${jdk_bundled_version}", tofile: "${home}/build/jdk/jdk_mac_redist_for_${buildNumber}.tar")
 
 // load ApplicationInfo.xml properties
 ant.xmlproperty(file: "$pythonEduHome/resources/idea/PyCharmEduApplicationInfo.xml", collapseAttributes: "true")
-setProperty("system_selector", "PyCharm${p("component.version.major")}0")
+setProperty("system_selector", "PyCharmEdu${p("component.version.major")}0")
 setProperty("dryRun", false)
 setProperty("jdk16", guessJdk())
 
@@ -89,7 +92,6 @@
 }
 
 setProperty("paths", new Paths(home))
-setProperty("buildName", "PE-$buildNumber")
 
 target('default': "Build artifacts") {
 
@@ -196,12 +198,12 @@
 
   buildNSIS([paths.distAll, paths.distWin],
             "$pythonEduHome/build/strings.nsi", "$pythonEduHome/build/paths.nsi",
-            "pycharmPE-", false, true, ".py", system_selector)
+            "pycharmEDU-", false, true, ".py", system_selector)
 
-  String tarRoot = isEap() ? "pycharm-pe-$buildNumber" : "pycharm-pe-${p("component.version.major")}.${p("component.version.minor")}"
+  String tarRoot = isEap() ? "pycharm-edu-$buildNumber" : "pycharm-edu-${p("component.version.major")}.${p("component.version.minor")}"
   buildTarGz(tarRoot, "$paths.artifacts/pycharm${buildName}.tar", [paths.distAll, paths.distUnix])
 
-  String macAppRoot = isEap() ? "PyCharm PE ${p("component.version.major")}.${p("component.version.minor")} EAP.app/Contents" : "PyCharm PE.app/Contents"
+  String macAppRoot = isEap() ? "PyCharm Educational ${p("component.version.major")}.${p("component.version.minor")} EAP.app/Contents" : "PyCharm Educational.app/Contents"
   buildMacZip(macAppRoot, "${paths.artifacts}/pycharm${buildName}.sit", [paths.distAll], paths.distMac)
   ant.copy(file: "${paths.artifacts}/pycharm${buildName}.sit", tofile: "${paths.artifacts}/pycharm${buildName}-jdk-bundled.sit")
   ant.delete(file: "${paths.artifacts}/pycharm${buildName}.sit")
@@ -214,6 +216,7 @@
         fileset(dir: "$pythonEduHome/learn-python/resources/courses")
       }
     }
+    layouts.layoutPlugin("course-creator")
   }
 
   layouts.layoutCommunityPlugins(ch)
@@ -363,7 +366,7 @@
   winScripts(target, ch, "pycharm.bat", args)
   winVMOptions(target, null, "pycharm.exe")
 
-  ant.copy(file: "$home/python/help/pycharmhelp.jar", todir: "$target/help", failonerror: false)
+  ant.copy(file: "$home/python/help/pycharm-eduhelp.jar", todir: "$target/help", failonerror: false)
 }
 
 private layoutUnix(Map args, String target) {
@@ -380,7 +383,7 @@
   unixScripts(target, ch, "pycharm.sh", args)
   unixVMOptions(target, "pycharm")
 
-  ant.copy(file: "$home/python/help/pycharmhelp.jar", todir: "$target/help", failonerror: false)
+  ant.copy(file: "$home/python/help/pycharm-eduhelp.jar", todir: "$target/help", failonerror: false)
 }
 
 private layoutMac(Map _args, String target) {
diff --git a/python/edu/build/python.txt b/python/edu/build/python.txt
new file mode 100644
index 0000000..d6f247d
--- /dev/null
+++ b/python/edu/build/python.txt
@@ -0,0 +1,2 @@
+python2 2.7 https://www.python.org/ftp/python/2.7.8/python-2.7.8.amd64.msi
+python3 3.4 https://www.python.org/ftp/python/3.4.1/python-3.4.1.amd64.msi
\ No newline at end of file
diff --git a/python/edu/build/resources/logo.bmp b/python/edu/build/resources/logo.bmp
new file mode 100644
index 0000000..9ff6698
--- /dev/null
+++ b/python/edu/build/resources/logo.bmp
Binary files differ
diff --git a/python/edu/build/resources/logo.png b/python/edu/build/resources/logo.png
deleted file mode 100644
index 1ccbc07..0000000
--- a/python/edu/build/resources/logo.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/build/upload_pythonInfo.xml b/python/edu/build/upload_pythonInfo.xml
new file mode 100644
index 0000000..f8d9477
--- /dev/null
+++ b/python/edu/build/upload_pythonInfo.xml
@@ -0,0 +1,32 @@
+<project name="Upload updates.xml to jetbrains.com. Effective in half an hour" default="bootstrap">
+  <property name="home" value="${basedir}/../../../.."/>
+  <target name="upload">
+    <xmlvalidate file="${home}/build/eap/updates.xml"/>
+
+    <property name="host" value="ftp.labs.intellij.net"/>
+    <property name="user" value="idea"/>
+    <property name="password" value="4pawoMauoJjjlxpIl3XG"/>
+
+    <ftp server="${host}" action="send" binary="false" remotedir="updates" userid="${user}" password="${password}">
+      <fileset file="${home}/community/python/edu/build/python.txt"/>
+    </ftp>
+  </target>
+
+  <target name="bootstrap">
+    <java failonerror="true" classname="org.apache.tools.ant.Main" fork="true">
+      <classpath>
+        <fileset dir="${home}/community/lib/ant/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <fileset dir="${home}/community/lib">
+          <include name="commons-net-3.1.jar"/>
+          <include name="jsch-0.1.50.jar"/>
+        </fileset>
+      </classpath>
+
+      <arg value="-f"/>
+      <arg value="${ant.file}"/>
+      <arg value="upload"/>
+    </java>
+  </target>
+</project>
diff --git a/python/edu/course-creator/course-creator.iml b/python/edu/course-creator/course-creator.iml
new file mode 100644
index 0000000..fc8c6a14
--- /dev/null
+++ b/python/edu/course-creator/course-creator.iml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="python-community" />
+    <orderEntry type="module" module-name="lang-impl" />
+    <orderEntry type="library" name="gson" level="project" />
+  </component>
+</module>
+
diff --git a/python/edu/course-creator/resources/META-INF/plugin.xml b/python/edu/course-creator/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..6a9a9ea
--- /dev/null
+++ b/python/edu/course-creator/resources/META-INF/plugin.xml
@@ -0,0 +1,60 @@
+<idea-plugin version="2">
+  <id>org.jetbrains.plugins.coursecreator</id>
+  <name>Course Creator for PyCharm Educational</name>
+  <version>1.0</version>
+
+  <description><![CDATA[
+      Plugin allows you to create new course for PyCharm Education Edition.
+    ]]></description>
+
+  <change-notes><![CDATA[
+    ]]>
+  </change-notes>
+  <!-- please see http://confluence.jetbrains.com/display/IDEADEV/Build+Number+Ranges for description -->
+
+  <!-- please see http://confluence.jetbrains.com/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
+       on how to target different products -->
+  <!-- uncomment to enable plugin in all products
+  <depends>com.intellij.modules.lang</depends>
+  -->
+
+  <depends>com.intellij.modules.python</depends>
+
+  <extensions defaultExtensionNs="com.intellij">
+    <directoryProjectGenerator implementation="org.jetbrains.plugins.coursecreator.CCProjectGenerator"/>
+    <projectService serviceImplementation="org.jetbrains.plugins.coursecreator.CCProjectService"/>
+    <codeInsight.lineMarkerProvider language="Python"
+                                    implementationClass="org.jetbrains.plugins.coursecreator.highlighting.CCTaskLineMarkerProvider"/>
+    <treeStructureProvider implementation="org.jetbrains.plugins.coursecreator.projectView.CCTreeStructureProvider"/>
+  </extensions>
+
+  <application-components>
+    <!-- Add your application components here -->
+  </application-components>
+
+  <project-components>
+    <!-- Add your project components here -->
+    <component>
+      <implementation-class>org.jetbrains.plugins.coursecreator.CCProjectComponent</implementation-class>
+    </component>
+  </project-components>
+
+  <actions>
+    <action id="CreateLesson" class="org.jetbrains.plugins.coursecreator.actions.CreateLesson">
+      <add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
+    </action>
+    <action id="CreateTaskFile" class="org.jetbrains.plugins.coursecreator.actions.CreateTaskFile">
+      <add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
+    </action>
+    <action id="CreateTask" class="org.jetbrains.plugins.coursecreator.actions.CreateTask">
+      <add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
+    </action>
+    <action id="AddTaskWindow" class="org.jetbrains.plugins.coursecreator.actions.AddTaskWindow">
+      <add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="CopyReference"/>
+    </action>
+    <action id="PackCourse" class="org.jetbrains.plugins.coursecreator.actions.CreateCourseArchive">
+      <add-to-group group-id="MainToolBar" anchor="last" />
+    </action>
+  </actions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/python/edu/course-creator/resources/fileTemplates/internal/task.html.ft b/python/edu/course-creator/resources/fileTemplates/internal/task.html.ft
new file mode 100644
index 0000000..f683349
--- /dev/null
+++ b/python/edu/course-creator/resources/fileTemplates/internal/task.html.ft
@@ -0,0 +1,4 @@
+<html>
+Write your task text here.
+<br>
+</html>
\ No newline at end of file
diff --git a/python/edu/course-creator/resources/fileTemplates/internal/task.py.ft b/python/edu/course-creator/resources/fileTemplates/internal/task.py.ft
new file mode 100644
index 0000000..0256e10
--- /dev/null
+++ b/python/edu/course-creator/resources/fileTemplates/internal/task.py.ft
@@ -0,0 +1 @@
+# TODO: type solution here
\ No newline at end of file
diff --git a/python/edu/course-creator/resources/fileTemplates/internal/test_helper.py.ft b/python/edu/course-creator/resources/fileTemplates/internal/test_helper.py.ft
new file mode 100644
index 0000000..1182b78
--- /dev/null
+++ b/python/edu/course-creator/resources/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/python/edu/course-creator/resources/fileTemplates/internal/tests.py.ft b/python/edu/course-creator/resources/fileTemplates/internal/tests.py.ft
new file mode 100644
index 0000000..2e6fd4c
--- /dev/null
+++ b/python/edu/course-creator/resources/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/python/edu/course-creator/resources/icons/gutter.png b/python/edu/course-creator/resources/icons/gutter.png
new file mode 100644
index 0000000..244e6ca
--- /dev/null
+++ b/python/edu/course-creator/resources/icons/gutter.png
Binary files differ
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java
new file mode 100644
index 0000000..1eb8690
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java
@@ -0,0 +1,80 @@
+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;
+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.*;
+
+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;
+    }
+    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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java
new file mode 100644
index 0000000..34de943
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java
@@ -0,0 +1,58 @@
+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;
+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();
+              new CCEditorFactoryListener().editorCreated(new EditorFactoryEvent(new EditorFactoryImpl(ProjectManager.getInstance()), editor ));
+            }
+          }
+        }
+      }
+    });
+  }
+
+  public void projectClosed() {
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java
new file mode 100644
index 0000000..dbaa726
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java
@@ -0,0 +1,101 @@
+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;
+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 org.jetbrains.plugins.coursecreator.ui.CCNewProjectPanel;
+
+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) {
+    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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectService.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectService.java
new file mode 100644
index 0000000..1e38bab
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectService.java
@@ -0,0 +1,138 @@
+/*
+ * 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.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.editor.Document;
+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.*;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@State(name = "CCProjectService",
+       storages = {
+         @Storage(file = "$PROJECT_CONFIG_DIR$/course_service.xml")
+       }
+)
+public class CCProjectService implements PersistentStateComponent<Element> {
+
+  private static final Logger LOG = Logger.getInstance(CCProjectService.class.getName());
+  public Course myCourse;
+  public static final String COURSE_ELEMENT = "course";
+  private static final Map<Document, StudyDocumentListener> myDocumentListeners = new HashMap<Document, StudyDocumentListener>();
+
+  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);
+  }
+
+  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();
+  }
+
+  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<TaskWindow> taskWindows = taskFile.getTaskWindows();
+    for (TaskWindow taskWindow : taskWindows) {
+      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<TaskWindow> collection) {
+    int size = collection.size();
+    return index >= 0 && index < size;
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java
new file mode 100644
index 0000000..d803e0e8
--- /dev/null
+++ b/python/edu/course-creator/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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java
new file mode 100644
index 0000000..ff88cea
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java
@@ -0,0 +1,105 @@
+package org.jetbrains.plugins.coursecreator.actions;
+
+import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
+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.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+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() {
+    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 TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length, model.getSelectedText());
+    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;
+    }
+    int index = taskFile.getTaskWindows().size() + 1;
+    taskFile.addTaskWindow(taskWindow, index);
+    taskWindow.drawHighlighter(editor);
+    DaemonCodeAnalyzerImpl.getInstance(project).restart(file);
+  }
+
+  @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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java
new file mode 100644
index 0000000..05428f4
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java
@@ -0,0 +1,198 @@
+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;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+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.StudyDocumentListener;
+import org.jetbrains.plugins.coursecreator.format.*;
+import org.jetbrains.plugins.coursecreator.ui.CreateCourseArchiveDialog;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.ZipOutputStream;
+
+public class CreateCourseArchive extends DumbAwareAction {
+  private static final Logger LOG = Logger.getInstance(CreateCourseArchive.class.getName());
+  String myZipName;
+  String myLocationDir;
+
+  public void setZipName(String zipName) {
+    myZipName = zipName;
+  }
+
+  public void setLocationDir(String locationDir) {
+    myLocationDir = locationDir;
+  }
+
+  public CreateCourseArchive() {
+    super("Generate course archive", "Generate course archive", AllIcons.FileTypes.Archive);
+  }
+
+  @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;
+    CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this);
+    dlg.show();
+    if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
+      return;
+    }
+    final VirtualFile baseDir = project.getBaseDir();
+    final Map<String, Lesson> lessons = course.getLessonsMap();
+    //List<FileEditor> editorList = new ArrayList<FileEditor>();
+    Map<VirtualFile, TaskFile> taskFiles = new HashMap<VirtualFile, TaskFile>();
+    for (Map.Entry<String, Lesson> lesson : lessons.entrySet()) {
+      final VirtualFile lessonDir = baseDir.findChild(lesson.getKey());
+      if (lessonDir == null) continue;
+      for (Map.Entry<String, Task> task : lesson.getValue().myTasksMap.entrySet()) {
+        final VirtualFile taskDir = lessonDir.findChild(task.getKey());
+        if (taskDir == null) continue;
+        for (Map.Entry<String, TaskFile> 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();
+          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;
+            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");
+          }
+        }
+      }
+    }
+    generateJson(project);
+    try {
+      File zipFile = new File(myLocationDir, myZipName + ".zip");
+      ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
+
+      for (Map.Entry<String, Lesson> 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();
+      Messages.showInfoMessage("Course archive was saved to " + zipFile.getPath(), "Course Archive Was Created Successfully");
+    }
+    catch (IOException e1) {
+      LOG.error(e1);
+    }
+
+    for (Map.Entry<VirtualFile, TaskFile> 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();
+  }
+
+  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
+      }
+    }
+  }
+
+  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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java
new file mode 100644
index 0000000..15d9f83
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateLesson.java
@@ -0,0 +1,89 @@
+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.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.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 DumbAwareAction {
+  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);
+          lesson.setIndex(size + 1);
+          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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java
new file mode 100644
index 0000000..0940135
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java
@@ -0,0 +1,122 @@
+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.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 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 DumbAwareAction {
+  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, "file1" + ".py", null, taskDirectory);
+
+            final Task task = new Task(taskName);
+            task.addTaskFile(taskPyFile.getContainingFile().getName(), size + 1);
+            task.setIndex(size + 1);
+            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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java
new file mode 100644
index 0000000..5aafceb
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java
@@ -0,0 +1,110 @@
+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 CreateTaskFile extends DumbAwareAction {
+
+  public CreateTaskFile() {
+    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 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() {
+      @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(), index);
+            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);
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindow.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindow.java
new file mode 100644
index 0000000..2724759
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/DeleteTaskWindow.java
@@ -0,0 +1,62 @@
+package org.jetbrains.plugins.coursecreator.actions;
+
+import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
+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;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.coursecreator.CCProjectService;
+import org.jetbrains.plugins.coursecreator.format.*;
+
+import java.util.List;
+
+@SuppressWarnings("ComponentNotRegistered")
+public class DeleteTaskWindow extends DumbAwareAction {
+  @NotNull
+  private final TaskWindow myTaskWindow;
+
+  public DeleteTaskWindow(@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;
+    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;
+
+    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<TaskWindow> taskWindows = taskFile.getTaskWindows();
+    if (taskWindows.contains(myTaskWindow)) {
+      myTaskWindow.removeResources(project);
+      taskWindows.remove(myTaskWindow);
+      editor.getMarkupModel().removeAllHighlighters();
+      CCProjectService.drawTaskWindows(file.getVirtualFile(), editor, course);
+      DaemonCodeAnalyzerImpl.getInstance(project).restart(file);
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java
new file mode 100644
index 0000000..7c7e7fa
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/ShowTaskWindowText.java
@@ -0,0 +1,44 @@
+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.CCProjectService;
+import org.jetbrains.plugins.coursecreator.format.*;
+import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog;
+
+@SuppressWarnings("ComponentNotRegistered")
+public class ShowTaskWindowText extends DumbAwareAction {
+  @NotNull
+  private final TaskWindow myTaskWindow;
+
+  public ShowTaskWindowText(@NotNull final TaskWindow taskWindow) {
+    super("Add task window","Add task window", null);
+    myTaskWindow = taskWindow;
+  }
+
+  @Override
+  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());
+    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(), taskFile.getTaskWindows().size() + 1);
+    dlg.show();
+  }
+}
\ No newline at end of file
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java
new file mode 100644
index 0000000..eb62d59
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java
@@ -0,0 +1,55 @@
+package org.jetbrains.plugins.coursecreator.format;
+
+import com.google.gson.annotations.Expose;
+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 {
+  @Expose public List<Lesson> lessons = new ArrayList<Lesson>();
+  @Expose public String description;
+
+  @Expose public String name;
+  @Expose public String author;
+
+  public Map<String, Lesson> myLessonsMap = new HashMap<String, Lesson>();
+
+  public Map<String, Lesson> 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<Lesson> 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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Lesson.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Lesson.java
new file mode 100644
index 0000000..3872014
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Lesson.java
@@ -0,0 +1,45 @@
+package org.jetbrains.plugins.coursecreator.format;
+
+import com.google.gson.annotations.Expose;
+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 {
+  @Expose public String name;
+  @Expose public List<Task> task_list = new ArrayList<Task>();
+
+  public int myIndex;
+  public Map<String, Task> myTasksMap = new HashMap<String, Task>();
+
+  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<Task> getTasklist() {
+    return task_list;
+  }
+
+  public void setIndex(int index) {
+    myIndex = index;
+  }
+
+  public int getIndex() {
+    return myIndex;
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Task.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Task.java
new file mode 100644
index 0000000..e6c085b
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Task.java
@@ -0,0 +1,41 @@
+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 {
+  @Expose public String name;
+  @Expose public Map<String, TaskFile> task_files = new HashMap<String, TaskFile>();
+  public int myIndex;
+
+  public Task() {}
+
+  public Task(@NotNull final String name) {
+    this.name = name;
+  }
+
+  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;
+  }
+
+  public Map<String, TaskFile> getTaskFiles() {
+    return task_files;
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java
new file mode 100644
index 0000000..85f0d91
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java
@@ -0,0 +1,111 @@
+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;
+
+public class TaskFile {
+  @Expose public List<TaskWindow> task_windows = new ArrayList<TaskWindow>();
+  public int myIndex;
+  public boolean myTrackChanges = true;
+
+  public boolean isTrackChanges() {
+    return myTrackChanges;
+  }
+
+  public void setTrackChanges(boolean trackChanges) {
+    myTrackChanges = trackChanges;
+  }
+
+  public TaskFile() {}
+
+  public void addTaskWindow(@NotNull final TaskWindow taskWindow, int index) {
+    taskWindow.setIndex(index);
+    task_windows.add(taskWindow);
+  }
+
+  public List<TaskWindow> getTaskWindows() {
+    return task_windows;
+  }
+
+  public void setIndex(int index) {
+    myIndex = index;
+  }
+
+
+  /**
+   * @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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java
new file mode 100644
index 0000000..cb6418e
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java
@@ -0,0 +1,133 @@
+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;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.coursecreator.CCProjectService;
+
+import java.io.File;
+
+public class TaskWindow implements Comparable{
+
+  @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 int myReplacementLength;
+  public int myIndex;
+
+  public TaskWindow() {}
+
+  public TaskWindow(int line, int start, int length, String selectedText) {
+    this.line = line;
+    this.start = start;
+    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;
+  }
+
+  public void setHint(String hint) {
+    this.hint = hint;
+  }
+
+  public String getHintName() {
+    return hint;
+  }
+
+  public void removeResources(@NotNull final Project project) {
+    if (hint != null) {
+      VirtualFile hints = project.getBaseDir().findChild("hints");
+      if (hints == null) {
+        return;
+      }
+      File hintFile = new File(hints.getPath(), hint);
+      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);
+    RangeHighlighter highlighter =
+      editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST + 1, defaultTestAttributes,
+                                                  HighlighterTargetArea.EXACT_RANGE);
+    highlighter.setGreedyToLeft(true);
+    highlighter.setGreedyToRight(true);
+  }
+
+  public int getIndex() {
+    return myIndex;
+  }
+
+  public void setIndex(int index) {
+
+    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
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java
new file mode 100644
index 0000000..1818b65
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/highlighting/CCTaskLineMarkerProvider.java
@@ -0,0 +1,75 @@
+package org.jetbrains.plugins.coursecreator.highlighting;
+
+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.Document;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.openapi.project.Project;
+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.CCProjectService;
+import org.jetbrains.plugins.coursecreator.format.*;
+
+import java.util.Collection;
+import java.util.List;
+
+public class CCTaskLineMarkerProvider implements LineMarkerProvider {
+  private static final Logger LOG = Logger.getInstance(CCTaskLineMarkerProvider.class.getName());
+
+  @Nullable
+  @Override
+  public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
+    return null;
+  }
+
+  @Override
+  public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull final Collection<LineMarkerInfo> result) {
+    for (PsiElement element : elements) {
+      if (element instanceof PsiFile) {
+        final Project project = element.getProject();
+        final Course course = CCProjectService.getInstance(project).getCourse();
+        if (course == null) return;
+        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 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.getReplacementLength());
+          @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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java
new file mode 100644
index 0000000..80b2674
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/highlighting/TaskTextGutter.java
@@ -0,0 +1,60 @@
+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.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.util.IconLoader;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.coursecreator.actions.DeleteTaskWindow;
+import org.jetbrains.plugins.coursecreator.actions.ShowTaskWindowText;
+import org.jetbrains.plugins.coursecreator.format.TaskWindow;
+
+import javax.swing.*;
+
+public class TaskTextGutter extends LineMarkerInfo.LineMarkerGutterIconRenderer {
+  @NotNull
+  private final TaskWindow myTaskWindow;
+
+  public TaskTextGutter(@NotNull final TaskWindow taskWindow, LineMarkerInfo lineMarkerInfo) {
+    super(lineMarkerInfo);
+    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);
+  }
+
+  @Nullable
+  @Override
+  public ActionGroup getPopupMenuActions() {
+    DefaultActionGroup group = new DefaultActionGroup();
+    group.add(new DeleteTaskWindow(myTaskWindow));
+    return group;
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java
new file mode 100644
index 0000000..1a73041
--- /dev/null
+++ b/python/edu/course-creator/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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java
new file mode 100644
index 0000000..69b78ec
--- /dev/null
+++ b/python/edu/course-creator/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<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent,
+                                             @NotNull Collection<AbstractTreeNode> children,
+                                             ViewSettings settings) {
+    if (!needModify(parent)) {
+      return children;
+    }
+    Collection<AbstractTreeNode> nodes = new ArrayList<AbstractTreeNode>();
+    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<AbstractTreeNode> selected, String dataName) {
+    return null;
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.form b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.form
new file mode 100644
index 0000000..71e2785
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.form
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.coursecreator.ui.CCNewProjectPanel">
+  <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="3" 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="fd520" 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">
+            <preferred-size width="95" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value="Name:"/>
+        </properties>
+      </component>
+      <component id="7e88" class="javax.swing.JTextField" binding="myName">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="150" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="ec56c" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Description"/>
+        </properties>
+      </component>
+      <component id="2e2d7" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+            <preferred-size width="95" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value="Author:"/>
+        </properties>
+      </component>
+      <component id="41fe6" class="javax.swing.JTextField" binding="myAuthorField">
+        <constraints>
+          <grid row="1" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="150" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <grid id="12f06" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="2" column="1" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="line">
+          <color color="-6709600"/>
+        </border>
+        <children>
+          <component id="389a7" class="javax.swing.JTextArea" binding="myDescription">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="50"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+        </children>
+      </grid>
+    </children>
+  </grid>
+</form>
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java
new file mode 100644
index 0000000..83a3458
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java
@@ -0,0 +1,59 @@
+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() {
+    final String userName = System.getProperty("user.name");
+    if (userName != null) {
+      myAuthorField.setText(userName);
+    }
+    myName.getDocument().addDocumentListener(new MyValidator());
+    myDescription.getDocument().addDocumentListener(new MyValidator());
+    myAuthorField.getDocument().addDocumentListener(new MyValidator());
+  }
+
+  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());
+  }
+
+  public void registerValidators(FacetValidatorsManager manager) {
+    myValidationManager = manager;
+  }
+
+  private class MyValidator extends DocumentAdapter {
+
+    @Override
+    protected void textChanged(DocumentEvent e) {
+      myValidationManager.validate();
+    }
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchiveDialog.java
new file mode 100644
index 0000000..d6c3bdb
--- /dev/null
+++ b/python/edu/course-creator/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.CreateCourseArchive;
+
+import javax.swing.*;
+
+public class CreateCourseArchiveDialog extends DialogWrapper {
+
+  private CreateCourseArchivePanel myPanel;
+  private CreateCourseArchive myAction;
+
+  public CreateCourseArchiveDialog(@NotNull final  Project project, CreateCourseArchive 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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form
new file mode 100644
index 0000000..920dcb9
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.coursecreator.ui.CreateCourseArchivePanel">
+  <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" 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>
+      <grid id="a3b77" layout-manager="GridLayoutManager" row-count="4" 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>
+          <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="786af" 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>
+          <component id="160bb" class="javax.swing.JTextField" binding="myNameField" default-binding="true">
+            <constraints>
+              <grid row="0" column="1" row-span="2" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="628ab" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="1" column="0" row-span="2" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text value="Location:"/>
+            </properties>
+          </component>
+          <component id="aab8" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myLocationField">
+            <constraints>
+              <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+          </component>
+          <grid id="29be7" layout-manager="GridLayoutManager" row-count="1" 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>
+              <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="4fa15" class="javax.swing.JLabel" binding="myErrorIcon">
+                <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=""/>
+                </properties>
+              </component>
+              <component id="4bdcf" class="javax.swing.JLabel" binding="myErrorLabel">
+                <constraints>
+                  <grid row="0" column="1" 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=""/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+        </children>
+      </grid>
+    </children>
+  </grid>
+</form>
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java
new file mode 100644
index 0000000..4e7deed
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.java
@@ -0,0 +1,65 @@
+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 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);
+    myErrorIcon.setIcon(AllIcons.Actions.Lightning);
+    setState(false);
+    myDlg = dlg;
+    myNameField.setText("course");
+    myLocationField.setText(project.getBasePath());
+    FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+    myLocationField.addBrowseFolderListener("Choose location folder", null, project, 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();
+  }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java
new file mode 100644
index 0000000..c7e8f71
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java
@@ -0,0 +1,153 @@
+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.ui.ValidationInfo;
+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.CCProjectService;
+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 Project getProject() {
+    return myProject;
+  }
+
+  public CreateTaskWindowDialog(@NotNull final Project project, @NotNull final TaskWindow taskWindow, int lessonIndex,
+                                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 : "");
+    String hintName = taskWindow.getHintName();
+    myPanel.setHintName(hintName != null ? hintName : "");
+    init();
+    initValidation();
+  }
+
+  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.doClick();
+          //myPanel.enableHint(true);
+          myPanel.setHintText(hintText.toString());
+        }
+        catch (FileNotFoundException e) {
+          LOG.error("created hint was not found", e);
+        }
+        catch (IOException e) {
+          LOG.error(e);
+        }
+      }
+    }
+  }
+
+  @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();
+  }
+
+  public void deleteHint() {
+    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);
+        myTaskWindow.setHint(null);
+        myPanel.resetHint();
+      }
+    }
+  }
+
+  @Nullable
+  @Override
+  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/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.form b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.form
new file mode 100644
index 0000000..ccd91e4
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.form
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowPanel">
+  <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="4" 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="aaa28" 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>
+          <labelFor value="b712"/>
+          <text value="Text:"/>
+        </properties>
+      </component>
+      <component id="d2e2f" class="javax.swing.JLabel" binding="myHintNameLabel">
+        <constraints>
+          <grid row="2" 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>
+          <enabled value="true"/>
+          <labelFor value="ddeb1"/>
+          <text value="Hint name:"/>
+        </properties>
+      </component>
+      <component id="ddeb1" class="javax.swing.JTextField" binding="myHintName">
+        <constraints>
+          <grid row="2" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="150" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value=""/>
+        </properties>
+      </component>
+      <component id="d322a" class="javax.swing.JLabel" binding="myHintTextLabel">
+        <constraints>
+          <grid row="3" 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>
+          <labelFor value="d0efc"/>
+          <text value="Hint text:"/>
+        </properties>
+      </component>
+      <grid id="51a63" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="3" column="1" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="line">
+          <color color="-6709600"/>
+        </border>
+        <children>
+          <component id="d0efc" class="javax.swing.JTextArea" binding="myHintText">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <preferred-size width="300" height="100"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+        </children>
+      </grid>
+      <grid id="cbc70" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <minimum-size width="150" height="-1"/>
+            <preferred-size width="150" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="line">
+          <color color="-6709600"/>
+        </border>
+        <children>
+          <component id="b712" class="javax.swing.JTextArea" binding="myTaskWindowText">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <preferred-size width="300" height="100"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+        </children>
+      </grid>
+      <component id="f86b4" class="javax.swing.JCheckBox" binding="myCreateHintCheckBox" default-binding="true">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Create hint"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java
new file mode 100644
index 0000000..21a7eb0
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java
@@ -0,0 +1,96 @@
+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;
+
+public class CreateTaskWindowPanel extends JPanel {
+
+  private final CreateTaskWindowDialog myDialog;
+  private JPanel myPanel;
+  private JTextArea myTaskWindowText;
+  private JTextField myHintName;
+  private JTextArea myHintText;
+  private JCheckBox myCreateHintCheckBox;
+  private JLabel myHintNameLabel;
+  private JLabel myHintTextLabel;
+  private String myGeneratedHintName = "";
+
+  public CreateTaskWindowPanel(CreateTaskWindowDialog dialog) {
+    super(new BorderLayout());
+    add(myPanel, BorderLayout.CENTER);
+    myDialog = dialog;
+    enableHint(false);
+    myCreateHintCheckBox.addItemListener(new ItemListener() {
+      @Override
+      public void itemStateChanged(ItemEvent e) {
+        int state = e.getStateChange();
+        // 1 for checked
+        enableHint(state == 1);
+        if (state == 2) {
+          myDialog.deleteHint();
+        }
+      }
+    });
+
+    myHintName.getDocument().addDocumentListener(new DocumentAdapter() {
+      @Override
+      protected void textChanged(DocumentEvent e) {
+        myDialog.validateInput();
+      }
+    });
+  }
+
+  public void enableHint(boolean isEnable) {
+    myHintName.setEnabled(isEnable);
+    myHintText.setEnabled(isEnable);
+    myHintNameLabel.setEnabled(isEnable);
+    myHintTextLabel.setEnabled(isEnable);
+    myHintName.setText(myGeneratedHintName);
+  }
+
+  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();
+  }
+
+  public void doClick() {
+    myCreateHintCheckBox.doClick();
+  }
+
+  public void resetHint() {
+    myHintName.setText("");
+    myHintText.setText("");
+  }
+
+  public void setGeneratedHintName(String generatedHintName) {
+    myGeneratedHintName = generatedHintName;
+  }
+}
diff --git a/python/edu/learn-python/gen/icons/StudyIcons.java b/python/edu/learn-python/gen/icons/StudyIcons.java
index 2840910..04ae98d 100644
--- a/python/edu/learn-python/gen/icons/StudyIcons.java
+++ b/python/edu/learn-python/gen/icons/StudyIcons.java
@@ -13,17 +13,15 @@
     return IconLoader.getIcon(path, StudyIcons.class);
   }
 
-  public static final Icon Add = load("/icons/com/jetbrains/python/edu/add.png"); // 16x16
-  public static final Icon Checked = load("/icons/com/jetbrains/python/edu/checked.png"); // 32x32
-  public static final Icon Failed = load("/icons/com/jetbrains/python/edu/failed.png"); // 32x32
-  public static final Icon Next = load("/icons/com/jetbrains/python/edu/next.png"); // 24x24
-  public static final Icon Playground = load("/icons/com/jetbrains/python/edu/playground.png"); // 32x28
-  public static final Icon Prev = load("/icons/com/jetbrains/python/edu/prev.png"); // 24x24
-  public static final Icon Refresh = load("/icons/com/jetbrains/python/edu/refresh.png"); // 16x16
-  public static final Icon Refresh24 = load("/icons/com/jetbrains/python/edu/refresh24.png"); // 24x24
-  public static final Icon Resolve = load("/icons/com/jetbrains/python/edu/resolve.png"); // 24x24
-  public static final Icon Run = load("/icons/com/jetbrains/python/edu/Run.png"); // 24x24
+  public static final Icon EducationalProjectType = load("/icons/com/jetbrains/python/edu/EducationalProjectType.png"); // 32x32
+  public static final Icon Lesson = load("/icons/com/jetbrains/python/edu/Lesson.png"); // 16x16
+  public static final Icon LessonCompl = load("/icons/com/jetbrains/python/edu/LessonCompl.png"); // 16x16
+  public static final Icon Playground = load("/icons/com/jetbrains/python/edu/Playground.png"); // 16x16
+  public static final Icon Prev = load("/icons/com/jetbrains/python/edu/prev.png"); // 16x16
+  public static final Icon Resolve = load("/icons/com/jetbrains/python/edu/resolve.png"); // 16x16
   public static final Icon ShowHint = load("/icons/com/jetbrains/python/edu/showHint.png"); // 24x24
-  public static final Icon Unchecked = load("/icons/com/jetbrains/python/edu/unchecked.png"); // 32x32
+  public static final Icon Task = load("/icons/com/jetbrains/python/edu/Task.png"); // 16x16
+  public static final Icon TaskCompl = load("/icons/com/jetbrains/python/edu/TaskCompl.png"); // 16x16
+  public static final Icon TaskProbl = load("/icons/com/jetbrains/python/edu/TaskProbl.png"); // 16x16
   public static final Icon WatchInput = load("/icons/com/jetbrains/python/edu/WatchInput.png"); // 24x24
 }
diff --git a/python/edu/learn-python/learn-python.iml b/python/edu/learn-python/learn-python.iml
index bd539d1..613d675 100644
--- a/python/edu/learn-python/learn-python.iml
+++ b/python/edu/learn-python/learn-python.iml
@@ -15,6 +15,7 @@
     <orderEntry type="module" module-name="lang-impl" />
     <orderEntry type="library" name="gson" level="project" />
     <orderEntry type="library" name="JUnit4" level="project" />
+    <orderEntry type="module" module-name="python-ide-community" />
   </component>
 </module>
 
diff --git a/python/edu/learn-python/resources/META-INF/plugin.xml b/python/edu/learn-python/resources/META-INF/plugin.xml
index ec828eb4..8e8bddcc 100644
--- a/python/edu/learn-python/resources/META-INF/plugin.xml
+++ b/python/edu/learn-python/resources/META-INF/plugin.xml
@@ -50,7 +50,7 @@
     <action id="NextTaskAction" class="com.jetbrains.python.edu.actions.StudyNextStudyTaskAction" text="NextTaskAction" description="Next Task"/>
     <action id="PreviousTaskAction" class="com.jetbrains.python.edu.actions.StudyPreviousStudyTaskAction" text="PreviousTaskAction"
             description="Previous Task"/>
-    <action id="RefreshTaskAction" class="com.jetbrains.python.edu.actions.StudyRefreshTaskAction" text="RefreshTaskAction"
+    <action id="RefreshTaskAction" class="com.jetbrains.python.edu.actions.StudyRefreshTaskFileAction" text="RefreshTaskAction"
             description="Refresh current task"/>
     <action id="WatchInputAction" class="com.jetbrains.python.edu.actions.StudyEditInputAction" text="WatchInputAction"
             description="watch input"/>
@@ -59,6 +59,11 @@
             description="show hint">
       <add-to-group group-id="MainToolBar" anchor="last"/>
     </action>
+
+    <action id="WelcomeScreen.LearnPython" class="com.jetbrains.python.edu.actions.StudyNewProject" icon="StudyIcons.EducationalProjectType">
+      <add-to-group group-id="WelcomeScreen.QuickStart" anchor="first"/>
+    </action>
+
   </actions>
 
   <extensions defaultExtensionNs="com.intellij">
@@ -70,4 +75,7 @@
     <applicationService serviceInterface="com.intellij.openapi.fileEditor.impl.EditorEmptyTextPainter"
         serviceImplementation="com.jetbrains.python.edu.StudyInstructionPainter" overrides="true"/>
   </extensions>
+  <extensions defaultExtensionNs="Pythonid">
+    <visitorFilter language="Python" implementationClass="com.jetbrains.python.edu.highlighting.StudyVisitorFilter"/>
+  </extensions>
 </idea-plugin>
\ No newline at end of file
diff --git a/python/edu/learn-python/resources/courses/introduction_course.zip b/python/edu/learn-python/resources/courses/introduction_course.zip
index f3b24f2..c39695e 100644
--- a/python/edu/learn-python/resources/courses/introduction_course.zip
+++ b/python/edu/learn-python/resources/courses/introduction_course.zip
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/EducationalProjectType.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/EducationalProjectType.png
new file mode 100644
index 0000000..6340e89
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/EducationalProjectType.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/EducationalProjectType_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/EducationalProjectType_dark.png
new file mode 100644
index 0000000..d9ec6dc
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/EducationalProjectType_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Lesson.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Lesson.png
new file mode 100644
index 0000000..9cc8a4f
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Lesson.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected] b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
new file mode 100644
index 0000000..2b0c0b8
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected] b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
new file mode 100644
index 0000000..e18c639
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/LessonCompl.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/LessonCompl.png
new file mode 100644
index 0000000..bacc7de
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/LessonCompl.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Playground.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Playground.png
new file mode 100644
index 0000000..1086710
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Playground.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected] b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
new file mode 100644
index 0000000..58665fa
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Run.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Run.png
deleted file mode 100644
index 27a6e36..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Run.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task.png
new file mode 100644
index 0000000..b678c64
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected] b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
new file mode 100644
index 0000000..4abb95c
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task@2x_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task@2x_dark.png
new file mode 100644
index 0000000..78af691
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task@2x_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl.png
new file mode 100644
index 0000000..a7f8f77
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected] b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
new file mode 100644
index 0000000..d657aa6
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl@2x_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl@2x_dark.png
new file mode 100644
index 0000000..f5f29ee
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl@2x_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl_dark.png
new file mode 100644
index 0000000..481e9cd2
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskCompl_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl.png
new file mode 100644
index 0000000..6173d64
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected] b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
new file mode 100644
index 0000000..44aba9d
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/[email protected]
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl@2x_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl@2x_dark.png
new file mode 100644
index 0000000..f74c9de
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl@2x_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl_dark.png
new file mode 100644
index 0000000..0133a7f
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/TaskProbl_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task_dark.png
new file mode 100644
index 0000000..2ff286d
--- /dev/null
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/Task_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/add.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/add.png
deleted file mode 100644
index 9494f2d..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/add.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png
deleted file mode 100644
index 4105a01..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/checked.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png
deleted file mode 100644
index e2aaa55..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/failed.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/icon.jpg b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/icon.jpg
deleted file mode 100644
index 3a9716e..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/icon.jpg
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/next.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/next.png
deleted file mode 100644
index dd1a5d9..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/next.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png
deleted file mode 100644
index d12a751..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/playground.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/prev.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/prev.png
index 0656f81..fc51cb5 100644
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/prev.png
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/prev.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh.png
deleted file mode 100644
index d595f6b..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh24.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh24.png
deleted file mode 100644
index 218f075..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/refresh24.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve.png
index 7ef960b..78290f9 100644
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve.png
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve_dark.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve_dark.png
index 99aaa1d..b988adc 100644
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve_dark.png
+++ b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/resolve_dark.png
Binary files differ
diff --git a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png b/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png
deleted file mode 100644
index 2145982..0000000
--- a/python/edu/learn-python/resources/icons/com/jetbrains/python/edu/unchecked.png
+++ /dev/null
Binary files differ
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java
index d4831d9..59bd8bc 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java
@@ -53,7 +53,7 @@
   @NotNull
   @Override
   public String getName() {
-    return "Study project";
+    return "Learn Python";
   }
 
 
@@ -137,7 +137,7 @@
   @Nullable
   @Override
   public Icon getLogo() {
-    return StudyIcons.Playground;
+    return StudyIcons.EducationalProjectType;
   }
 
 
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java
index 9fdcf70..6ce1d09 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java
@@ -52,6 +52,9 @@
       if (myTaskWindow != null) {
         int newLength = myTaskWindow.getLength() + change;
         myTaskWindow.setLength(newLength <= 0 ? 0 : newLength);
+        if (e.getNewFragment().equals("\n")) {
+          myTaskWindow.setLength(myTaskWindow.getLength() + 1);
+        }
       }
       int newEnd = offset + event.getNewLength();
       int newLine = document.getLineNumber(newEnd);
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java
index 4f34bfb..96a44b2 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java
@@ -8,7 +8,6 @@
 import com.intellij.util.PairFunction;
 import com.intellij.util.ui.GraphicsUtil;
 import com.intellij.util.ui.UIUtil;
-import com.jetbrains.python.edu.ui.StudyCondition;
 
 import java.awt.*;
 
@@ -19,10 +18,6 @@
 public class StudyInstructionPainter extends EditorEmptyTextPainter {
   @Override
   public void paintEmptyText(final EditorsSplitters splitters, Graphics g) {
-    if (!StudyCondition.VALUE) {
-      super.paintEmptyText(splitters, g);
-      return;
-    }
     boolean isDarkBackground = UIUtil.isUnderDarcula();
     UIUtil.applyRenderingHints(g);
     GraphicsUtil.setupAntialiasing(g, true, false);
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyState.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyState.java
new file mode 100644
index 0000000..96dc3d9
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyState.java
@@ -0,0 +1,52 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.editor.StudyEditor;
+
+public class StudyState {
+  private final StudyEditor myStudyEditor;
+  private final Editor myEditor;
+  private final TaskFile myTaskFile;
+  private final VirtualFile myVirtualFile;
+  private final Task myTask;
+  private final VirtualFile myTaskDir;
+
+  public StudyState(final StudyEditor studyEditor) {
+    myStudyEditor = studyEditor;
+    myEditor = studyEditor != null ? studyEditor.getEditor() : null;
+    myTaskFile = studyEditor != null ? studyEditor.getTaskFile() : null;
+    myVirtualFile = myEditor != null ? FileDocumentManager.getInstance().getFile(myEditor.getDocument()) : null;
+    myTaskDir = myVirtualFile != null ? myVirtualFile.getParent() : null;
+    myTask = myTaskFile != null ? myTaskFile.getTask() : null;
+  }
+
+  public Editor getEditor() {
+    return myEditor;
+  }
+
+  public TaskFile getTaskFile() {
+    return myTaskFile;
+  }
+
+  public VirtualFile getVirtualFile() {
+    return myVirtualFile;
+  }
+
+  public Task getTask() {
+    return myTask;
+  }
+
+  public VirtualFile getTaskDir() {
+    return myTaskDir;
+  }
+
+  public boolean isValid() {
+    return myStudyEditor != null && myEditor != null &&
+           myTaskFile != null && myVirtualFile != null &&
+           myTask != null && myTaskDir != null;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java
index 213c1f7..3013fbc 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java
@@ -109,21 +109,29 @@
               StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() {
                 @Override
                 public void run() {
-                  ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW).show(null);
-                  FileEditor[] editors = FileEditorManager.getInstance(myProject).getSelectedEditors();
-                  if (editors.length > 0) {
-                    JComponent focusedComponent = editors[0].getPreferredFocusedComponent();
-                    if (focusedComponent != null) {
-                      IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true);
+                  ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW).show(new Runnable() {
+                    @Override
+                    public void run() {
+                      FileEditor[] editors = FileEditorManager.getInstance(myProject).getSelectedEditors();
+                      if (editors.length > 0) {
+                        final JComponent focusedComponent = editors[0].getPreferredFocusedComponent();
+                        if (focusedComponent != null) {
+                          ApplicationManager.getApplication().invokeLater(new Runnable() {
+                            @Override
+                            public void run() {
+                              IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true);
+                            }
+                          });
+                        }
+                      }
                     }
-                  }
+                  });
                 }
               });
               UISettings.getInstance().HIDE_TOOL_STRIPES = false;
               UISettings.getInstance().fireUISettingsChanged();
               ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
               String toolWindowId = StudyToolWindowFactory.STUDY_TOOL_WINDOW;
-              //TODO:decide smth with tool window position
               try {
                 Method method = toolWindowManager.getClass().getDeclaredMethod("registerToolWindow", String.class,
                                                                                JComponent.class,
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java
new file mode 100644
index 0000000..b0cd5ba
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java
@@ -0,0 +1,76 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.python.edu.course.Course;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.sdk.PythonSdkType;
+
+import java.io.*;
+import java.util.Map;
+
+public class StudyTestRunner {
+  public static final String TEST_OK = "#study_plugin test OK";
+  private static final String TEST_FAILED = "#study_plugin FAILED + ";
+  private static final String PYTHONPATH = "PYTHONPATH";
+  private static final Logger LOG = Logger.getInstance(StudyTestRunner.class);
+  private final Task myTask;
+  private final VirtualFile myTaskDir;
+
+  public StudyTestRunner(Task task, VirtualFile taskDir) {
+    myTask = task;
+    myTaskDir = taskDir;
+  }
+
+  public Process launchTests(Project project, String executablePath) throws ExecutionException {
+    Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
+    File testRunner = new File(myTaskDir.getPath(), myTask.getTestFile());
+    GeneralCommandLine commandLine = new GeneralCommandLine();
+    commandLine.setWorkDirectory(myTaskDir.getPath());
+    final Map<String, String> env = commandLine.getEnvironment();
+    final VirtualFile courseDir = project.getBaseDir();
+    if (courseDir != null) {
+      env.put(PYTHONPATH, courseDir.getPath());
+    }
+    if (sdk != null) {
+      String pythonPath = sdk.getHomePath();
+      if (pythonPath != null) {
+        commandLine.setExePath(pythonPath);
+        commandLine.addParameter(testRunner.getPath());
+        final Course course = StudyTaskManager.getInstance(project).getCourse();
+        assert course != null;
+        commandLine.addParameter(new File(course.getResourcePath()).getParent());
+        commandLine.addParameter(FileUtil.toSystemDependentName(executablePath));
+        return commandLine.createProcess();
+      }
+    }
+    return null;
+  }
+
+
+  public String getPassedTests(Process p) {
+    InputStream testOutput = p.getInputStream();
+    BufferedReader testOutputReader = new BufferedReader(new InputStreamReader(testOutput));
+    String line;
+    try {
+      while ((line = testOutputReader.readLine()) != null) {
+        if (line.contains(TEST_FAILED)) {
+          return line.substring(TEST_FAILED.length(), line.length());
+        }
+      }
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+    finally {
+      StudyUtils.closeSilently(testOutputReader);
+    }
+    return TEST_OK;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java
index d3ac1da..5d9bb13 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java
@@ -3,6 +3,7 @@
 import com.intellij.ide.SaveAndSyncHandlerImpl;
 import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
@@ -10,12 +11,12 @@
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileManager;
 import com.intellij.openapi.wm.ToolWindowManager;
 import com.intellij.util.ui.UIUtil;
-import com.jetbrains.python.edu.course.TaskFile;
-import com.jetbrains.python.edu.course.TaskWindow;
+import com.jetbrains.python.edu.course.*;
 import com.jetbrains.python.edu.editor.StudyEditor;
 import com.jetbrains.python.edu.ui.StudyToolWindowFactory;
 import org.jetbrains.annotations.NotNull;
@@ -70,7 +71,7 @@
       return wrapHTML ? UIUtil.toHtml(taskText.toString()) : taskText.toString();
     }
     catch (IOException e) {
-      LOG.error("Failed to get file text from file " + fileName, e);
+      LOG.info("Failed to get file text from file " + fileName, e);
     }
     finally {
       closeSilently(reader);
@@ -119,14 +120,18 @@
   }
 
   @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
-  public static VirtualFile flushWindows(Document document, TaskFile taskFile, VirtualFile file) {
+  public static VirtualFile flushWindows(TaskFile taskFile, VirtualFile file) {
     VirtualFile taskDir = file.getParent();
     VirtualFile fileWindows = null;
+    final Document document = FileDocumentManager.getInstance().getDocument(file);
+    if (document == null) {
+      LOG.debug("Couldn't flush windows");
+      return null;
+    }
     if (taskDir != null) {
       String name = file.getNameWithoutExtension() + "_windows";
       PrintWriter printWriter = null;
       try {
-
         fileWindows = taskDir.createChildData(taskFile, name);
         printWriter = new PrintWriter(new FileOutputStream(fileWindows.getPath()));
         for (TaskWindow taskWindow : taskFile.getTaskWindows()) {
@@ -137,6 +142,12 @@
           String windowDescription = document.getText(new TextRange(start, start + taskWindow.getLength()));
           printWriter.println("#study_plugin_window = " + windowDescription);
         }
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          @Override
+          public void run() {
+            FileDocumentManager.getInstance().saveDocument(document);
+          }
+        });
       }
       catch (IOException e) {
        LOG.error(e);
@@ -148,4 +159,27 @@
     }
     return fileWindows;
   }
+
+  public static void deleteFile(VirtualFile file) {
+    try {
+      file.delete(StudyUtils.class);
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+  }
+
+  public static File copyResourceFile(String sourceName, String copyName, Project project, Task task)
+    throws IOException {
+    StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+    Course course = taskManager.getCourse();
+    int taskNum = task.getIndex() + 1;
+    int lessonNum = task.getLesson().getIndex() + 1;
+    assert course != null;
+    String pathToResource =
+      FileUtil.join(new File(course.getResourcePath()).getParent(), Lesson.LESSON_DIR + lessonNum, Task.TASK_DIR + taskNum);
+    File resourceFile = new File(pathToResource, copyName);
+    FileUtil.copy(new File(pathToResource, sourceName), resourceFile);
+    return resourceFile;
+  }
 }
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java
index f8e10c9..5d02f71 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java
@@ -1,7 +1,6 @@
 package com.jetbrains.python.edu.actions;
 
 import com.intellij.execution.ExecutionException;
-import com.intellij.execution.configurations.GeneralCommandLine;
 import com.intellij.ide.projectView.ProjectView;
 import com.intellij.openapi.actionSystem.ActionManager;
 import com.intellij.openapi.actionSystem.AnActionEvent;
@@ -13,93 +12,81 @@
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.fileEditor.FileEditor;
 import com.intellij.openapi.fileEditor.FileEditorManager;
-import com.intellij.openapi.module.ModuleManager;
 import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.MessageType;
 import com.intellij.openapi.ui.popup.Balloon;
 import com.intellij.openapi.ui.popup.BalloonBuilder;
 import com.intellij.openapi.ui.popup.JBPopupFactory;
-import com.intellij.openapi.util.TextRange;
-import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.ui.JBColor;
+import com.intellij.openapi.wm.IdeFocusManager;
 import com.jetbrains.python.edu.StudyDocumentListener;
-import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.StudyState;
+import com.jetbrains.python.edu.StudyTestRunner;
 import com.jetbrains.python.edu.StudyUtils;
-import com.jetbrains.python.edu.course.*;
+import com.jetbrains.python.edu.course.StudyStatus;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import com.jetbrains.python.edu.course.TaskWindow;
 import com.jetbrains.python.edu.editor.StudyEditor;
-import com.jetbrains.python.sdk.PythonSdkType;
 import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 import java.awt.*;
-import java.io.*;
-import java.util.*;
-import java.util.List;
+import java.io.IOException;
+import java.util.Map;
 
 public class StudyCheckAction extends DumbAwareAction {
 
   private static final Logger LOG = Logger.getInstance(StudyCheckAction.class.getName());
-  public static final String PYTHONPATH = "PYTHONPATH";
+  private static final String ANSWERS_POSTFIX = "_answers.py";
 
-  static class StudyTestRunner {
-    public static final String TEST_OK = "#study_plugin test OK";
-    private static final String TEST_FAILED = "#study_plugin FAILED + ";
-    private final Task myTask;
-    private final VirtualFile myTaskDir;
 
-    StudyTestRunner(Task task, VirtualFile taskDir) {
-      myTask = task;
-      myTaskDir = taskDir;
-    }
-
-    Process launchTests(Project project, String executablePath) throws ExecutionException {
-      Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
-      File testRunner = new File(myTaskDir.getPath(), myTask.getTestFile());
-      GeneralCommandLine commandLine = new GeneralCommandLine();
-      commandLine.setWorkDirectory(myTaskDir.getPath());
-      final Map<String, String> env = commandLine.getEnvironment();
-      final VirtualFile courseDir = project.getBaseDir();
-      if (courseDir != null)
-        env.put(PYTHONPATH, courseDir.getPath());
-      if (sdk != null) {
-        String pythonPath = sdk.getHomePath();
-        if (pythonPath != null) {
-          commandLine.setExePath(pythonPath);
-          commandLine.addParameter(testRunner.getPath());
-          final Course course = StudyTaskManager.getInstance(project).getCourse();
-          assert course != null;
-          commandLine.addParameter(new File(course.getResourcePath()).getParent());
-          commandLine.addParameter(FileUtil.toSystemDependentName(executablePath));
-          return commandLine.createProcess();
-        }
+  private static void flushWindows(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
+    for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+      String name = entry.getKey();
+      TaskFile taskFile = entry.getValue();
+      VirtualFile virtualFile = taskDir.findChild(name);
+      if (virtualFile == null) {
+        continue;
       }
-      return null;
-    }
-
-
-    String getPassedTests(Process p) {
-      InputStream testOutput = p.getInputStream();
-      BufferedReader testOutputReader = new BufferedReader(new InputStreamReader(testOutput));
-      String line;
-      try {
-        while ((line = testOutputReader.readLine()) != null) {
-          if (line.contains(TEST_FAILED)) {
-             return line.substring(TEST_FAILED.length(), line.length());
-          }
-        }
-      }
-      catch (IOException e) {
-        LOG.error(e);
-      }
-      finally {
-        StudyUtils.closeSilently(testOutputReader);
-      }
-      return TEST_OK;
+      StudyUtils.flushWindows(taskFile, virtualFile);
     }
   }
 
+  private static void deleteWindowDescriptions(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
+    for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+      String name = entry.getKey();
+      VirtualFile virtualFile = taskDir.findChild(name);
+      if (virtualFile == null) {
+        continue;
+      }
+      String windowsFileName = virtualFile.getNameWithoutExtension() + "_windows";
+      VirtualFile windowsFile = taskDir.findChild(windowsFileName);
+      if (windowsFile != null) {
+        StudyUtils.deleteFile(windowsFile);
+      }
+    }
+  }
+
+  private static void drawAllTaskWindows(@NotNull final Project project, @NotNull final Task task, @NotNull final VirtualFile taskDir) {
+    for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+      String name = entry.getKey();
+      TaskFile taskFile = entry.getValue();
+      VirtualFile virtualFile = taskDir.findChild(name);
+      if (virtualFile == null) {
+        continue;
+      }
+      FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
+      if (fileEditor instanceof StudyEditor) {
+        StudyEditor studyEditor = (StudyEditor)fileEditor;
+        taskFile.drawAllWindows(studyEditor.getEditor());
+      }
+    }
+  }
+
+
   public void check(@NotNull final Project project) {
     ApplicationManager.getApplication().runWriteAction(new Runnable() {
       @Override
@@ -107,188 +94,138 @@
         CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
           @Override
           public void run() {
-        final Editor selectedEditor = StudyEditor.getSelectedEditor(project);
-        if (selectedEditor != null) {
-          final FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
-          final VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
-          if (openedFile != null) {
-            StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
-            final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
-            List<VirtualFile> filesToDelete = new ArrayList<VirtualFile>();
-            if (selectedTaskFile != null) {
-              final VirtualFile taskDir = openedFile.getParent();
-              Task currentTask = selectedTaskFile.getTask();
-              StudyStatus oldStatus = currentTask.getStatus();
-              Map<String, TaskFile> taskFiles = selectedTaskFile.getTask().getTaskFiles();
+            final StudyEditor selectedEditor = StudyEditor.getSelectedStudyEditor(project);
+            final StudyState studyState = new StudyState(selectedEditor);
+            if (!studyState.isValid()) {
+              LOG.error("StudyCheckAction was invokes outside study editor");
+              return;
+            }
+            Task task = studyState.getTask();
+            StudyStatus oldStatus = task.getStatus();
+            Map<String, TaskFile> taskFiles = task.getTaskFiles();
+            VirtualFile taskDir = studyState.getTaskDir();
+            flushWindows(task, taskDir);
+            StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
+            if (runAction != null && taskFiles.size() == 1) {
+              runAction.run(project);
+            }
+            ApplicationManager.getApplication().invokeLater(new Runnable() {
+              @Override
+              public void run() {
+                IdeFocusManager.getInstance(project).requestFocus(studyState.getEditor().getComponent(), true);
+              }
+            });
+            final StudyTestRunner testRunner = new StudyTestRunner(task, taskDir);
+            Process testProcess = null;
+            try {
+              testProcess = testRunner.launchTests(project, studyState.getVirtualFile().getPath());
+            }
+            catch (ExecutionException e) {
+              LOG.error(e);
+            }
+            if (testProcess == null) {
+              return;
+            }
+            String failedMessage = testRunner.getPassedTests(testProcess);
+            if (failedMessage.equals(StudyTestRunner.TEST_OK)) {
+              task.setStatus(StudyStatus.Solved, oldStatus);
+              createTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), project);
+            }
+            else {
+              task.setStatus(StudyStatus.Failed, oldStatus);
               for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
                 String name = entry.getKey();
                 TaskFile taskFile = entry.getValue();
-                VirtualFile virtualFile = taskDir.findChild(name);
-                if (virtualFile == null) {
+                if (taskFile.getTaskWindows().size() < 2) {
+                  taskFile.setStatus(StudyStatus.Failed, StudyStatus.Unchecked);
                   continue;
                 }
-                VirtualFile windowFile = StudyUtils.flushWindows(FileDocumentManager.getInstance().getDocument(virtualFile), taskFile, virtualFile);
-                filesToDelete.add(windowFile);
-                FileDocumentManager.getInstance().saveAllDocuments();
+                runSmartTestProcess(taskDir, testRunner, name, taskFile, project);
               }
-
-              StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
-              if (runAction != null && currentTask.getTaskFiles().size() == 1) {
-                runAction.run(project);
-              }
-              final StudyTestRunner testRunner = new StudyTestRunner(currentTask, taskDir);
-              Process testProcess = null;
-              try {
-                testProcess = testRunner.launchTests(project, openedFile.getPath());
-              }
-              catch (ExecutionException e) {
-                LOG.error(e);
-              }
-              if (testProcess != null) {
-                String failedMessage = testRunner.getPassedTests(testProcess);
-                if (failedMessage.equals(StudyTestRunner.TEST_OK)) {
-                  currentTask.setStatus(StudyStatus.Solved, oldStatus);
-                  StudyUtils.updateStudyToolWindow(project);
-                  selectedTaskFile.drawAllWindows(selectedEditor);
-                  ProjectView.getInstance(project).refresh();
-                  for (VirtualFile file:filesToDelete) {
-                    try {
-                      file.delete(this);
-                    }
-                    catch (IOException e) {
-                      LOG.error(e);
-                    }
-                  }
-                  createTestResultPopUp("Congratulations!", JBColor.GREEN, project);
-                  return;
-                }
-                for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
-                  String name = entry.getKey();
-                  TaskFile taskFile = entry.getValue();
-                  TaskFile answerTaskFile = new TaskFile();
-                  VirtualFile virtualFile = taskDir.findChild(name);
-                  if (virtualFile == null) {
-                    continue;
-                  }
-                  VirtualFile answerFile = getCopyWithAnswers(taskDir, virtualFile, taskFile, answerTaskFile);
-                  for (TaskWindow taskWindow : answerTaskFile.getTaskWindows()) {
-                    Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
-                    if (document == null) {
-                      continue;
-                    }
-                    if (!taskWindow.isValid(document)) {
-                      continue;
-                    }
-                    check(project, taskWindow, answerFile, answerTaskFile, taskFile, document, testRunner, virtualFile);
-                  }
-                  FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
-                  Editor editor = null;
-                  if (fileEditor instanceof StudyEditor) {
-                    StudyEditor studyEditor = (StudyEditor) fileEditor;
-                    editor = studyEditor.getEditor();
-                  }
-
-                  if (editor != null) {
-                    taskFile.drawAllWindows(editor);
-                    StudyUtils.synchronize();
-                  }
-                  try {
-                    answerFile.delete(this);
-                  }
-                  catch (IOException e) {
-                    LOG.error(e);
-                  }
-                }
-                for (VirtualFile file:filesToDelete) {
-                  try {
-                    file.delete(this);
-                  }
-                  catch (IOException e) {
-                    LOG.error(e);
-                  }
-                }
-                currentTask.setStatus(StudyStatus.Failed, oldStatus);
-                StudyUtils.updateStudyToolWindow(project);
-                createTestResultPopUp(failedMessage, JBColor.RED, project);
-              }
+              createTestResultPopUp(failedMessage, MessageType.ERROR.getPopupBackground(), project);
+              navigateToFailedTaskWindow(studyState, task, taskDir, project);
             }
+            StudyUtils.updateStudyToolWindow(project);
+            drawAllTaskWindows(project, task, taskDir);
+            ProjectView.getInstance(project).refresh();
+            deleteWindowDescriptions(task, taskDir);
           }
-        }
-
-         }
-      });
+        });
       }
     });
   }
 
-  private void check(Project project,
-                     TaskWindow taskWindow,
-                     VirtualFile answerFile,
-                     TaskFile answerTaskFile,
-                     TaskFile usersTaskFile,
-                     Document usersDocument,
-                     StudyTestRunner testRunner,
-                     VirtualFile openedFile) {
-
-    try {
-       VirtualFile windowCopy = answerFile.copy(this, answerFile.getParent(), answerFile.getNameWithoutExtension() + "_window" + taskWindow.getIndex() + ".py");
-      final FileDocumentManager documentManager = FileDocumentManager.getInstance();
-      final Document windowDocument = documentManager.getDocument(windowCopy);
-      if (windowDocument != null) {
-        StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
-        Course course = taskManager.getCourse();
-        Task task = usersTaskFile.getTask();
-        int taskNum = task.getIndex() + 1;
-        int lessonNum = task.getLesson().getIndex() + 1;
-        assert course != null;
-        String pathToResource = FileUtil.join(new File(course.getResourcePath()).getParent(), Lesson.LESSON_DIR + lessonNum,  Task.TASK_DIR + taskNum);
-        File resourceFile = new File(pathToResource, windowCopy.getName());
-        FileUtil.copy(new File(pathToResource, openedFile.getName()), resourceFile);
-        TaskFile windowTaskFile = new TaskFile();
-        TaskFile.copy(answerTaskFile, windowTaskFile);
-        StudyDocumentListener listener = new StudyDocumentListener(windowTaskFile);
-        windowDocument.addDocumentListener(listener);
-        int start = taskWindow.getRealStartOffset(windowDocument);
-        int end = start + taskWindow.getLength();
-        TaskWindow userTaskWindow = usersTaskFile.getTaskWindows().get(taskWindow.getIndex());
-        int userStart = userTaskWindow.getRealStartOffset(usersDocument);
-        int userEnd = userStart + userTaskWindow.getLength();
-        String text = usersDocument.getText(new TextRange(userStart, userEnd));
-        windowDocument.replaceString(start, end, text);
-        ApplicationManager.getApplication().runWriteAction(new Runnable() {
-          @Override
-          public void run() {
-            documentManager.saveDocument(windowDocument);
+  private static void navigateToFailedTaskWindow(@NotNull final StudyState studyState,
+                                                 @NotNull final Task task,
+                                                 @NotNull final VirtualFile taskDir,
+                                                 @NotNull final Project project) {
+    TaskFile selectedTaskFile = studyState.getTaskFile();
+    Editor editor = studyState.getEditor();
+    TaskFile taskFileToNavigate = selectedTaskFile;
+    VirtualFile fileToNavigate = studyState.getVirtualFile();
+    if (!selectedTaskFile.hasFailedTaskWindows()) {
+      for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+        String name = entry.getKey();
+        TaskFile taskFile = entry.getValue();
+        if (taskFile.hasFailedTaskWindows()) {
+          taskFileToNavigate = taskFile;
+          VirtualFile virtualFile = taskDir.findChild(name);
+          if (virtualFile == null) {
+            continue;
           }
-        });
-        VirtualFile fileWindows = StudyUtils.flushWindows(windowDocument, windowTaskFile, windowCopy);
-        Process smartTestProcess = testRunner.launchTests(project, windowCopy.getPath());
-        boolean res = testRunner.getPassedTests(smartTestProcess).equals(StudyTestRunner.TEST_OK);
-        userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed, StudyStatus.Unchecked);
-        windowCopy.delete(this);
-        fileWindows.delete(this);
-        if (!resourceFile.delete()) {
-          LOG.error("failed to delete", resourceFile.getPath());
+          FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
+          if (fileEditor instanceof StudyEditor) {
+            StudyEditor studyEditor = (StudyEditor)fileEditor;
+            editor = studyEditor.getEditor();
+          }
+          fileToNavigate = virtualFile;
+          break;
         }
       }
     }
-    catch (IOException e) {
-      LOG.error(e);
-    }
-    catch (ExecutionException e) {
-      LOG.error(e);
-    }
+    FileEditorManager.getInstance(project).openFile(fileToNavigate, true);
+    final Editor editorToNavigate = editor;
+    ApplicationManager.getApplication().invokeLater(new Runnable() {
+      @Override
+      public void run() {
+        IdeFocusManager.getInstance(project).requestFocus(editorToNavigate.getContentComponent(), true);
+      }
+    });
+    taskFileToNavigate.navigateToFirstFailedTaskWindow(editor);
   }
 
+  private void runSmartTestProcess(@NotNull final VirtualFile taskDir,
+                                   @NotNull final StudyTestRunner testRunner,
+                                   final String taskFileName,
+                                   @NotNull final TaskFile taskFile,
+                                   @NotNull final Project project) {
+    TaskFile answerTaskFile = new TaskFile();
+    VirtualFile virtualFile = taskDir.findChild(taskFileName);
+    if (virtualFile == null) {
+      return;
+    }
+    VirtualFile answerFile = getCopyWithAnswers(taskDir, virtualFile, taskFile, answerTaskFile);
+    for (TaskWindow taskWindow : answerTaskFile.getTaskWindows()) {
+      Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
+      if (document == null) {
+        continue;
+      }
+      if (!taskWindow.isValid(document)) {
+        continue;
+      }
+      taskWindow.smartCheck(project, answerFile, answerTaskFile, taskFile, testRunner, virtualFile, document);
+    }
+    StudyUtils.deleteFile(answerFile);
+  }
 
-  private VirtualFile getCopyWithAnswers(final VirtualFile taskDir,
-                                         final VirtualFile file,
-                                         final TaskFile source,
-                                         TaskFile target) {
+  private VirtualFile getCopyWithAnswers(@NotNull final VirtualFile taskDir,
+                                         @NotNull final VirtualFile file,
+                                         @NotNull final TaskFile source,
+                                         @NotNull final TaskFile target) {
     VirtualFile copy = null;
     try {
 
-      copy = file.copy(this, taskDir, file.getNameWithoutExtension() +"_answers.py");
+      copy = file.copy(this, taskDir, file.getNameWithoutExtension() + ANSWERS_POSTFIX);
       final FileDocumentManager documentManager = FileDocumentManager.getInstance();
       final Document document = documentManager.getDocument(copy);
       if (document != null) {
@@ -315,19 +252,18 @@
     catch (IOException e) {
       LOG.error(e);
     }
-
-
     return copy;
   }
 
   private static void createTestResultPopUp(final String text, Color color, @NotNull final Project project) {
     BalloonBuilder balloonBuilder =
       JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, color, null);
-    Balloon balloon = balloonBuilder.createBalloon();
+    final Balloon balloon = balloonBuilder.createBalloon();
     StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project);
     assert studyEditor != null;
     JButton checkButton = studyEditor.getCheckButton();
     balloon.showInCenterOf(checkButton);
+    Disposer.register(project, balloon);
   }
 
   @Override
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java
index 5b9a6fe..72660fc 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java
@@ -19,6 +19,7 @@
 import com.intellij.ui.tabs.TabInfo;
 import com.intellij.ui.tabs.TabsListener;
 import com.intellij.ui.tabs.impl.JBEditorTabs;
+import com.intellij.util.PlatformIcons;
 import com.jetbrains.python.edu.StudyTaskManager;
 import com.jetbrains.python.edu.StudyUtils;
 import com.jetbrains.python.edu.course.Task;
@@ -26,7 +27,6 @@
 import com.jetbrains.python.edu.course.UserTest;
 import com.jetbrains.python.edu.editor.StudyEditor;
 import com.jetbrains.python.edu.ui.StudyTestContentPanel;
-import icons.StudyIcons;
 import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
@@ -92,7 +92,7 @@
         i++;
       }
       TabInfo plusTab = new TabInfo(new JPanel());
-      plusTab.setIcon(StudyIcons.Add);
+      plusTab.setIcon(PlatformIcons.ADD_ICON);
       tabbedPane.addTabSilently(plusTab, tabbedPane.getTabCount());
       final JBPopup hint =
         JBPopupFactory.getInstance().createComponentPopupBuilder(tabbedPane.getComponent(), tabbedPane.getComponent())
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java
new file mode 100644
index 0000000..0b75c4b
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java
@@ -0,0 +1,34 @@
+/*
+ * 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 com.jetbrains.python.edu.actions;
+
+import com.jetbrains.python.edu.StudyDirectoryProjectGenerator;
+import com.jetbrains.python.newProject.actions.GenerateProjectCallback;
+import com.jetbrains.python.newProject.actions.ProjectSpecificAction;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class StudyNewProject extends ProjectSpecificAction {
+
+  public StudyNewProject(@NotNull final String name, @Nullable final Runnable runnable) {
+    super(new GenerateProjectCallback(runnable), new StudyDirectoryProjectGenerator(), name, true);
+  }
+
+  public StudyNewProject() {
+    this("Learn Python", null);
+  }
+
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java
index 81818a9..3c971c3 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java
@@ -2,13 +2,14 @@
 
 import com.jetbrains.python.edu.editor.StudyEditor;
 import com.jetbrains.python.edu.course.Task;
+import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 
 public class StudyNextStudyTaskAction extends StudyTaskNavigationAction {
 
   @Override
-  protected JButton getButton(StudyEditor selectedStudyEditor) {
+  protected JButton getButton(@NotNull final StudyEditor selectedStudyEditor) {
     return selectedStudyEditor.getNextTaskButton();
   }
 
@@ -18,7 +19,7 @@
   }
 
   @Override
-  protected Task getTargetTask(Task sourceTask) {
+  protected Task getTargetTask(@NotNull final Task sourceTask) {
     return sourceTask.next();
   }
 }
\ No newline at end of file
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java
index 595aeef..fcf9ef4 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java
@@ -1,8 +1,8 @@
 package com.jetbrains.python.edu.actions;
 
+import com.intellij.icons.AllIcons;
 import com.jetbrains.python.edu.StudyUtils;
 import com.jetbrains.python.edu.course.TaskWindow;
-import icons.StudyIcons;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
@@ -16,7 +16,7 @@
   public static final String SHORTCUT2 = "ctrl pressed ENTER";
 
   public StudyNextWindowAction() {
-    super("NextWindowAction", "Select next window", StudyIcons.Next);
+    super("NextWindowAction", "Select next window", AllIcons.Actions.Forward);
   }
 
   @Override
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java
index bc26c28..f6da6a0 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java
@@ -3,13 +3,14 @@
 
 import com.jetbrains.python.edu.editor.StudyEditor;
 import com.jetbrains.python.edu.course.Task;
+import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 
 public class StudyPreviousStudyTaskAction extends StudyTaskNavigationAction {
 
   @Override
-  protected JButton getButton(StudyEditor selectedStudyEditor) {
+  protected JButton getButton(@NotNull final StudyEditor selectedStudyEditor) {
     return selectedStudyEditor.getPrevTaskButton();
   }
 
@@ -19,7 +20,7 @@
   }
 
   @Override
-  protected Task getTargetTask(Task sourceTask) {
+  protected Task getTargetTask(@NotNull final Task sourceTask) {
     return sourceTask.prev();
   }
 }
\ No newline at end of file
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java
similarity index 90%
rename from python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java
rename to python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java
index f8abb0b..a9448dd 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java
@@ -14,6 +14,7 @@
 import com.intellij.openapi.ui.popup.Balloon;
 import com.intellij.openapi.ui.popup.BalloonBuilder;
 import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.wm.IdeFocusManager;
 import com.jetbrains.python.edu.StudyDocumentListener;
@@ -24,8 +25,8 @@
 
 import java.io.*;
 
-public class StudyRefreshTaskAction extends DumbAwareAction {
-  private static final Logger LOG = Logger.getInstance(StudyRefreshTaskAction.class.getName());
+public class StudyRefreshTaskFileAction extends DumbAwareAction {
+  private static final Logger LOG = Logger.getInstance(StudyRefreshTaskFileAction.class.getName());
 
   public void refresh(final Project project) {
         ApplicationManager.getApplication().invokeLater(new Runnable() {
@@ -92,14 +93,20 @@
                     document.addDocumentListener(listener);
                   }
                   selectedTaskFile.drawAllWindows(editor);
-                  IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
+                  ApplicationManager.getApplication().invokeLater(new Runnable() {
+                    @Override
+                    public void run() {
+                      IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
+                    }
+                  });
                   selectedTaskFile.navigateToFirstTaskWindow(editor);
                   BalloonBuilder balloonBuilder =
                     JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can now start again", MessageType.INFO, null);
-                  Balloon balloon = balloonBuilder.createBalloon();
+                  final Balloon balloon = balloonBuilder.createBalloon();
                   StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
                   assert selectedStudyEditor != null;
                   balloon.showInCenterOf(selectedStudyEditor.getRefreshButton());
+                  Disposer.register(project, balloon);
                 }
                 catch (FileNotFoundException e1) {
                   LOG.error(e1);
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java
index 1efa908..2952486 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java
@@ -5,19 +5,18 @@
 import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.LogicalPosition;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.popup.JBPopup;
 import com.intellij.openapi.ui.popup.JBPopupFactory;
-import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.util.Disposer;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.PsiManager;
+import com.jetbrains.python.edu.StudyState;
 import com.jetbrains.python.edu.StudyTaskManager;
 import com.jetbrains.python.edu.StudyUtils;
 import com.jetbrains.python.edu.course.Course;
-import com.jetbrains.python.edu.course.TaskFile;
 import com.jetbrains.python.edu.course.TaskWindow;
 import com.jetbrains.python.edu.editor.StudyEditor;
 import icons.StudyIcons;
@@ -33,58 +32,56 @@
   }
 
   public void actionPerformed(AnActionEvent e) {
-    Project project = e.getProject();
-    if (project != null) {
+    final Project project = e.getProject();
+    if (project == null) {
+      return;
+    }
+    Course course = StudyTaskManager.getInstance(project).getCourse();
+    if (course == null) {
+      return;
+    }
+    StudyState studyState = new StudyState(StudyEditor.getSelectedStudyEditor(project));
+    if (!studyState.isValid()) {
+      return;
+    }
+    PsiFile file = PsiManager.getInstance(project).findFile(studyState.getVirtualFile());
+    final Editor editor = studyState.getEditor();
+    LogicalPosition pos = editor.getCaretModel().getLogicalPosition();
+    TaskWindow taskWindow = studyState.getTaskFile().getTaskWindow(editor.getDocument(), pos);
+    if (file == null || taskWindow == null) {
+      return;
+    }
+    String hint = taskWindow.getHint();
+    if (hint == null) {
+      return;
+    }
+    File resourceFile = new File(course.getResourcePath());
+    File resourceRoot = resourceFile.getParentFile();
+    if (resourceRoot == null || !resourceRoot.exists()) {
+      return;
+    }
+    File hintsDir = new File(resourceRoot, Course.HINTS_DIR);
+    if (hintsDir.exists()) {
+      String hintText = StudyUtils.getFileText(hintsDir.getAbsolutePath(), hint, true);
+      int offset = editor.getDocument().getLineStartOffset(pos.line) + pos.column;
+      PsiElement element = file.findElementAt(offset);
+      if (hintText == null || element == null) {
+        return;
+      }
+
       DocumentationManager documentationManager = DocumentationManager.getInstance(project);
       DocumentationComponent component = new DocumentationComponent(documentationManager);
-      Editor selectedEditor = StudyEditor.getSelectedEditor(project);
-      FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
-      assert selectedEditor != null;
-      VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
-      if (openedFile != null) {
-        StudyTaskManager taskManager = StudyTaskManager.getInstance(e.getProject());
-        TaskFile taskFile = taskManager.getTaskFile(openedFile);
-        if (taskFile != null) {
-          PsiFile file = PsiManager.getInstance(project).findFile(openedFile);
-          if (file != null) {
-            LogicalPosition pos = selectedEditor.getCaretModel().getLogicalPosition();
-            TaskWindow taskWindow = taskFile.getTaskWindow(selectedEditor.getDocument(), pos);
-            if (taskWindow != null) {
-              String hint = taskWindow.getHint();
-              if (hint == null) {
-                return;
-              }
-              Course course = taskManager.getCourse();
-              if (course != null) {
-                File resourceFile = new File(course.getResourcePath());
-                File resourceRoot = resourceFile.getParentFile();
-                if (resourceRoot != null && resourceRoot.exists()) {
-                  File hintsDir = new File(resourceRoot, Course.HINTS_DIR);
-                  if (hintsDir.exists()) {
-                    String hintText = StudyUtils.getFileText(hintsDir.getAbsolutePath(), hint, true);
-                    if (hintText != null) {
-                      int offset = selectedEditor.getDocument().getLineStartOffset(pos.line) + pos.column;
-                      PsiElement element = file.findElementAt(offset);
-                      if (element != null) {
-                        component.setData(element, hintText, true, null);
-                        final JBPopup popup =
-                          JBPopupFactory.getInstance().createComponentPopupBuilder(component, component)
-                            .setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false)
-                            .setResizable(true)
-                            .setMovable(true)
-                            .setRequestFocus(true)
-                            .createPopup();
-                        component.setHint(popup);
-                        popup.showInBestPositionFor(selectedEditor);
-                      }
-                    }
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
+      component.setData(element, hintText, true, null);
+      final JBPopup popup =
+        JBPopupFactory.getInstance().createComponentPopupBuilder(component, component)
+          .setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false)
+          .setResizable(true)
+          .setMovable(true)
+          .setRequestFocus(true)
+          .createPopup();
+      component.setHint(popup);
+      popup.showInBestPositionFor(editor);
+      Disposer.dispose(component);
     }
   }
 
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java
index b781e7d..46c0981 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java
@@ -1,8 +1,6 @@
 package com.jetbrains.python.edu.actions;
 
 import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.project.Project;
@@ -11,37 +9,34 @@
 import com.intellij.openapi.ui.popup.BalloonBuilder;
 import com.intellij.openapi.ui.popup.JBPopupFactory;
 import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.python.edu.StudyTaskManager;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.jetbrains.python.edu.StudyState;
 import com.jetbrains.python.edu.course.Lesson;
 import com.jetbrains.python.edu.course.Task;
 import com.jetbrains.python.edu.course.TaskFile;
 import com.jetbrains.python.edu.editor.StudyEditor;
+import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 import java.util.Map;
 
-/**
- * author: liana
- * data: 7/21/14.
- */
+
 abstract public class StudyTaskNavigationAction extends DumbAwareAction {
-  public void navigateTask(Project project) {
-    Editor selectedEditor = StudyEditor.getSelectedEditor(project);
-    FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
-    assert selectedEditor != null;
-    VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
-    StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
-    assert openedFile != null;
-    TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
-    assert selectedTaskFile != null;
-    Task currentTask = selectedTaskFile.getTask();
-    Task nextTask = getTargetTask(currentTask);
+  public void navigateTask(@NotNull final Project project) {
+    StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project);
+    StudyState studyState = new StudyState(studyEditor);
+    if (!studyState.isValid()) {
+      return;
+    }
+    Task nextTask = getTargetTask(studyState.getTask());
     if (nextTask == null) {
       BalloonBuilder balloonBuilder =
         JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(getNavigationFinishedMessage(), MessageType.INFO, null);
       Balloon balloon = balloonBuilder.createBalloon();
-      StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
-      balloon.showInCenterOf(getButton(selectedStudyEditor));
+      assert studyEditor != null;
+      balloon.showInCenterOf(getButton(studyEditor));
       return;
     }
     for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) {
@@ -82,16 +77,24 @@
     if (shouldBeActive != null) {
       FileEditorManager.getInstance(project).openFile(shouldBeActive, true);
     }
+    ToolWindow runToolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.RUN);
+    if (runToolWindow != null) {
+      runToolWindow.hide(null);
+    }
   }
 
-  protected abstract JButton getButton(StudyEditor selectedStudyEditor);
+  protected abstract JButton getButton(@NotNull final StudyEditor selectedStudyEditor);
 
   @Override
   public void actionPerformed(AnActionEvent e) {
-    navigateTask(e.getProject());
+    Project project = e.getProject();
+    if (project == null) {
+      return;
+    }
+    navigateTask(project);
   }
 
   protected abstract String getNavigationFinishedMessage();
 
-  protected abstract Task getTargetTask(Task sourceTask);
+  protected abstract Task getTargetTask(@NotNull final Task sourceTask);
 }
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java
index 4f17fc0..c46c4f5 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java
@@ -21,7 +21,7 @@
  * which is visible to student in project view
  */
 
-public class TaskFile implements Stateful{
+public class TaskFile implements Stateful {
   public List<TaskWindow> taskWindows = new ArrayList<TaskWindow>();
   private Task myTask;
   @Transient
@@ -173,7 +173,18 @@
     for (TaskWindow w : taskWindows) {
       if ((w.getLine() == line) && (w.getStart() >= oldEndOffsetInLine)) {
         int distance = w.getStart() - oldEndOffsetInLine;
-        if (lineChange != 0 || newEndOffsetInLine <= w.getStart()) {
+        boolean coveredByPrevTW = false;
+        int prevIndex = w.getIndex() - 1;
+        if (StudyUtils.indexIsValid(prevIndex, taskWindows)) {
+          TaskWindow prevTW = taskWindows.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);
         }
@@ -217,12 +228,33 @@
   public void navigateToFirstTaskWindow(@NotNull final Editor editor) {
     if (!taskWindows.isEmpty()) {
       TaskWindow firstTaskWindow = StudyUtils.getFirst(taskWindows);
-      mySelectedTaskWindow = firstTaskWindow;
-      LogicalPosition taskWindowStart = new LogicalPosition(firstTaskWindow.getLine(), firstTaskWindow.getStart());
-      editor.getCaretModel().moveToLogicalPosition(taskWindowStart);
-      int startOffset = firstTaskWindow.getRealStartOffset(editor.getDocument());
-      int endOffset = startOffset + firstTaskWindow.getLength();
-      editor.getSelectionModel().setSelection(startOffset, endOffset);
+      navigateToTaskWindow(editor, firstTaskWindow);
     }
   }
+
+  private void navigateToTaskWindow(@NotNull final Editor editor, @NotNull final TaskWindow firstTaskWindow) {
+    if (!firstTaskWindow.isValid(editor.getDocument())) {
+      return;
+    }
+    mySelectedTaskWindow = firstTaskWindow;
+    LogicalPosition taskWindowStart = new LogicalPosition(firstTaskWindow.getLine(), firstTaskWindow.getStart());
+    editor.getCaretModel().moveToLogicalPosition(taskWindowStart);
+    int startOffset = firstTaskWindow.getRealStartOffset(editor.getDocument());
+    int endOffset = startOffset + firstTaskWindow.getLength();
+    editor.getSelectionModel().setSelection(startOffset, endOffset);
+  }
+
+  public void navigateToFirstFailedTaskWindow(@NotNull final Editor editor) {
+    for (TaskWindow taskWindow : taskWindows) {
+      if (taskWindow.getStatus() != StudyStatus.Failed) {
+        continue;
+      }
+      navigateToTaskWindow(editor, taskWindow);
+      break;
+    }
+  }
+
+  public boolean hasFailedTaskWindows() {
+    return taskWindows.size() > 0 && getStatus() == StudyStatus.Failed;
+  }
 }
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java
index 4fb112c..dc4a75a 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java
@@ -1,5 +1,8 @@
 package com.jetbrains.python.edu.course;
 
+import com.intellij.execution.ExecutionException;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.colors.EditorColors;
@@ -8,16 +11,27 @@
 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.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.ui.JBColor;
+import com.jetbrains.python.edu.StudyDocumentListener;
+import com.jetbrains.python.edu.StudyTestRunner;
+import com.jetbrains.python.edu.StudyUtils;
 import org.jetbrains.annotations.NotNull;
 
+import java.io.File;
+import java.io.IOException;
+
 /**
  * Implementation of windows which user should type in
  */
 
 
 public class TaskWindow implements Comparable, Stateful {
-
+  private static final String WINDOW_POSTFIX = "_window.py";
+  private static final Logger LOG = Logger.getInstance(TaskWindow.class);
   public int line = 0;
   public int start = 0;
   public String hint = "";
@@ -174,4 +188,55 @@
   public int getIndex() {
     return myIndex;
   }
+
+  public void smartCheck(@NotNull final Project project,
+                         @NotNull final VirtualFile answerFile,
+                         @NotNull final TaskFile answerTaskFile,
+                         @NotNull final TaskFile usersTaskFile,
+                         @NotNull final StudyTestRunner testRunner,
+                         @NotNull final VirtualFile virtualFile,
+                         @NotNull final Document usersDocument) {
+
+    try {
+      VirtualFile windowCopy =
+        answerFile.copy(this, answerFile.getParent(), answerFile.getNameWithoutExtension() + WINDOW_POSTFIX);
+      final FileDocumentManager documentManager = FileDocumentManager.getInstance();
+      final Document windowDocument = documentManager.getDocument(windowCopy);
+      if (windowDocument != null) {
+        File resourceFile = StudyUtils.copyResourceFile(virtualFile.getName(), windowCopy.getName(), project, usersTaskFile.getTask());
+        TaskFile windowTaskFile = new TaskFile();
+        TaskFile.copy(answerTaskFile, windowTaskFile);
+        StudyDocumentListener listener = new StudyDocumentListener(windowTaskFile);
+        windowDocument.addDocumentListener(listener);
+        int start = getRealStartOffset(windowDocument);
+        int end = start + getLength();
+        TaskWindow userTaskWindow = usersTaskFile.getTaskWindows().get(getIndex());
+        int userStart = userTaskWindow.getRealStartOffset(usersDocument);
+        int userEnd = userStart + userTaskWindow.getLength();
+        String text = usersDocument.getText(new TextRange(userStart, userEnd));
+        windowDocument.replaceString(start, end, text);
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          @Override
+          public void run() {
+            documentManager.saveDocument(windowDocument);
+          }
+        });
+        VirtualFile fileWindows = StudyUtils.flushWindows(windowTaskFile, windowCopy);
+        Process smartTestProcess = testRunner.launchTests(project, windowCopy.getPath());
+        boolean res = testRunner.getPassedTests(smartTestProcess).equals(StudyTestRunner.TEST_OK);
+        userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed, StudyStatus.Unchecked);
+        StudyUtils.deleteFile(windowCopy);
+        StudyUtils.deleteFile(fileWindows);
+        if (!resourceFile.delete()) {
+          LOG.error("failed to delete", resourceFile.getPath());
+        }
+      }
+    }
+    catch (ExecutionException e) {
+      LOG.error(e);
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+  }
 }
\ No newline at end of file
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java
index 69c5acc..6b27c4a 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java
@@ -1,8 +1,10 @@
 package com.jetbrains.python.edu.editor;
 
 import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
+import com.intellij.icons.AllIcons;
 import com.intellij.ide.structureView.StructureViewBuilder;
 import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.EditorFactory;
@@ -17,9 +19,15 @@
 import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.openapi.wm.ToolWindowManager;
 import com.intellij.pom.Navigatable;
+import com.intellij.ui.BrowserHyperlinkListener;
 import com.intellij.ui.HideableTitledPanel;
 import com.intellij.ui.JBColor;
+import com.intellij.util.ui.EmptyClipboardOwner;
 import com.intellij.util.ui.UIUtil;
 import com.jetbrains.python.edu.StudyDocumentListener;
 import com.jetbrains.python.edu.StudyTaskManager;
@@ -36,8 +44,8 @@
 import javax.swing.text.StyleConstants;
 import javax.swing.text.StyledDocument;
 import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.*;
 import java.beans.PropertyChangeListener;
 import java.util.HashMap;
 import java.util.Map;
@@ -50,12 +58,13 @@
   private static final String TASK_TEXT_HEADER = "Task Text";
   private final FileEditor myDefaultEditor;
   private final JComponent myComponent;
+  private final TaskFile myTaskFile;
   private JButton myCheckButton;
   private JButton myNextTaskButton;
   private JButton myPrevTaskButton;
   private JButton myRefreshButton;
   private static final Map<Document, StudyDocumentListener> myDocumentListeners = new HashMap<Document, StudyDocumentListener>();
-  private Project myProject;
+  private final Project myProject;
 
   public JButton getCheckButton() {
     return myCheckButton;
@@ -65,6 +74,10 @@
     return myPrevTaskButton;
   }
 
+  public TaskFile getTaskFile() {
+    return myTaskFile;
+  }
+
   private static JButton addButton(@NotNull final JComponent parentComponent, String toolTipText, Icon icon) {
     JButton newButton = new JButton();
     newButton.setToolTipText(toolTipText);
@@ -89,26 +102,72 @@
     myComponent = myDefaultEditor.getComponent();
     JPanel studyPanel = new JPanel();
     studyPanel.setLayout(new BoxLayout(studyPanel, BoxLayout.Y_AXIS));
-    TaskFile taskFile = StudyTaskManager.getInstance(myProject).getTaskFile(file);
-    if (taskFile != null) {
-      Task currentTask = taskFile.getTask();
+    myTaskFile = StudyTaskManager.getInstance(myProject).getTaskFile(file);
+    if (myTaskFile != null) {
+      Task currentTask = myTaskFile.getTask();
       String taskText = currentTask.getResourceText(project, currentTask.getText(), false);
       initializeTaskText(studyPanel, taskText);
       JPanel studyButtonPanel = new JPanel(new GridLayout(1, 2));
       JPanel taskActionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
       studyButtonPanel.add(taskActionsPanel);
       studyButtonPanel.add(new JPanel());
-      initializeButtons(taskActionsPanel, taskFile);
+      initializeButtons(taskActionsPanel, myTaskFile);
       studyPanel.add(studyButtonPanel);
       myComponent.add(studyPanel, BorderLayout.NORTH);
     }
   }
 
-  private static void initializeTaskText(JPanel studyPanel, @Nullable String taskText) {
+  class CopyListener extends MouseAdapter {
+    final JTextPane myTextPane;
+
+    public CopyListener(JTextPane textPane) {
+      myTextPane = textPane;
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+      ApplicationManager.getApplication().invokeLater(new Runnable() {
+        @Override
+        public void run() {
+          ToolWindow projectView = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW);
+          if (projectView == null) {
+            return;
+          }
+          final Component focusComponent = projectView.getComponent();
+          IdeFocusManager.getInstance(myProject).requestFocus(focusComponent, true);
+          final String text = myTextPane.getSelectedText();
+          if (text == null) {
+            return;
+          }
+          KeyAdapter keyAdapter = new KeyAdapter() {
+            @Override
+            public void keyPressed(KeyEvent ev) {
+              if (ev.getKeyCode() == KeyEvent.VK_C
+                  && ev.getModifiers() == InputEvent.CTRL_MASK) {
+                StringSelection selection = new StringSelection(text);
+                Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, EmptyClipboardOwner.INSTANCE);
+                ApplicationManager.getApplication().invokeLater(new Runnable() {
+                  @Override
+                  public void run() {
+                    IdeFocusManager.getInstance(myProject).requestFocus(myDefaultEditor.getComponent(), true);
+                  }
+                });
+              }
+            }
+          };
+          focusComponent.addKeyListener(keyAdapter);
+        }
+      });
+    }
+  }
+
+  private void initializeTaskText(JPanel studyPanel, @Nullable String taskText) {
     JTextPane taskTextPane = new JTextPane();
+    taskTextPane.addMouseListener(new CopyListener(taskTextPane));
     taskTextPane.setContentType("text/html");
     taskTextPane.setEditable(false);
     taskTextPane.setText(taskText);
+    taskTextPane.addHyperlinkListener(new BrowserHyperlinkListener());
     EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getGlobalScheme();
     int fontSize = editorColorsScheme.getEditorFontSize();
     String fontName = editorColorsScheme.getEditorFontName();
@@ -134,10 +193,10 @@
   private void initializeButtons(@NotNull final JPanel taskActionsPanel, @NotNull final TaskFile taskFile) {
     myCheckButton = addButton(taskActionsPanel, "Check task", StudyIcons.Resolve);
     myPrevTaskButton = addButton(taskActionsPanel, "Prev Task", StudyIcons.Prev);
-    myNextTaskButton = addButton(taskActionsPanel, "Next Task", StudyIcons.Next);
-    myRefreshButton = addButton(taskActionsPanel, "Start task again", StudyIcons.Refresh24);
+    myNextTaskButton = addButton(taskActionsPanel, "Next Task", AllIcons.Actions.Forward);
+    myRefreshButton = addButton(taskActionsPanel, "Start task again", AllIcons.Actions.Refresh);
     if (!taskFile.getTask().getUserTests().isEmpty()) {
-      JButton runButton = addButton(taskActionsPanel, "Run", StudyIcons.Run);
+      JButton runButton = addButton(taskActionsPanel, "Run", AllIcons.General.Run);
       runButton.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
@@ -149,7 +208,8 @@
       watchInputButton.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
-          StudyEditInputAction studyEditInputAction = (StudyEditInputAction)ActionManager.getInstance().getAction("WatchInputAction");
+          StudyEditInputAction studyEditInputAction =
+            (StudyEditInputAction)ActionManager.getInstance().getAction("WatchInputAction");
           studyEditInputAction.showInput(myProject);
         }
       });
@@ -165,7 +225,8 @@
     myNextTaskButton.addActionListener(new ActionListener() {
       @Override
       public void actionPerformed(ActionEvent e) {
-        StudyNextStudyTaskAction studyNextTaskAction = (StudyNextStudyTaskAction)ActionManager.getInstance().getAction("NextTaskAction");
+        StudyNextStudyTaskAction studyNextTaskAction =
+          (StudyNextStudyTaskAction)ActionManager.getInstance().getAction("NextTaskAction");
         studyNextTaskAction.navigateTask(myProject);
       }
     });
@@ -180,7 +241,8 @@
     myRefreshButton.addActionListener(new ActionListener() {
       @Override
       public void actionPerformed(ActionEvent e) {
-        StudyRefreshTaskAction studyRefreshTaskAction = (StudyRefreshTaskAction)ActionManager.getInstance().getAction("RefreshTaskAction");
+        StudyRefreshTaskFileAction studyRefreshTaskAction =
+          (StudyRefreshTaskFileAction)ActionManager.getInstance().getAction("RefreshTaskAction");
         studyRefreshTaskAction.refresh(myProject);
       }
     });
@@ -300,7 +362,8 @@
       if (fileEditor instanceof StudyEditor) {
         return (StudyEditor)fileEditor;
       }
-    } catch (Exception e) {
+    }
+    catch (Exception e) {
       return null;
     }
     return null;
@@ -325,8 +388,9 @@
   @NotNull
   @Override
   public Editor getEditor() {
-    if (myDefaultEditor instanceof TextEditor)
+    if (myDefaultEditor instanceof TextEditor) {
       return ((TextEditor)myDefaultEditor).getEditor();
+    }
     return EditorFactory.getInstance().createViewer(new DocumentImpl(""), myProject);
   }
 
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/highlighting/StudyVisitorFilter.java b/python/edu/learn-python/src/com/jetbrains/python/edu/highlighting/StudyVisitorFilter.java
new file mode 100644
index 0000000..dc7495a
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/highlighting/StudyVisitorFilter.java
@@ -0,0 +1,18 @@
+package com.jetbrains.python.edu.highlighting;
+
+import com.intellij.psi.PsiFile;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.inspections.PythonVisitorFilter;
+import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
+import org.jetbrains.annotations.NotNull;
+
+public class StudyVisitorFilter implements PythonVisitorFilter {
+  @Override
+  public boolean isSupported(@NotNull final Class visitorClass, @NotNull final PsiFile file) {
+    if (StudyTaskManager.getInstance(file.getProject()).getCourse() == null) return true;
+    if (visitorClass == PyUnresolvedReferencesInspection.class) {
+      return false;
+    }
+    return true;
+  }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java
index abf648c..2f80dba 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java
@@ -32,7 +32,7 @@
 
   @Override
   protected void updateImpl(PresentationData data) {
-    data.setIcon(StudyIcons.Unchecked);
+    data.setIcon(StudyIcons.Task);
     String valueName = myValue.getName();
     StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(myProject);
     Course course = studyTaskManager.getCourse();
@@ -41,7 +41,7 @@
     }
     if (valueName.equals(myProject.getName())) {
       data.clearText();
-      data.addText(course.getName(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, JBColor.BLUE));
+      data.addText(course.getName(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.BLACK));
       data.addText(" (" + valueName + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
       return;
     }
@@ -91,15 +91,16 @@
     StudyStatus taskStatus = stateful.getStatus();
     switch (taskStatus) {
       case Unchecked: {
-        updatePresentation(data, additionalName, JBColor.blue, StudyIcons.Unchecked);
+        updatePresentation(data, additionalName, JBColor.BLACK, stateful instanceof Lesson ? StudyIcons.Lesson : StudyIcons.Task);
         break;
       }
       case Solved: {
-        updatePresentation(data, additionalName, new JBColor(new Color(0, 134, 0), new Color(98, 150, 85)), StudyIcons.Checked);
+        updatePresentation(data, additionalName, new JBColor(new Color(0, 134, 0), new Color(98, 150, 85)),
+                           stateful instanceof Lesson ? StudyIcons.LessonCompl : StudyIcons.TaskCompl);
         break;
       }
       case Failed: {
-        updatePresentation(data, additionalName, JBColor.RED, StudyIcons.Failed);
+        updatePresentation(data, additionalName, JBColor.RED, stateful instanceof Lesson ? StudyIcons.Lesson : StudyIcons.TaskProbl);
       }
     }
   }
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form
index 133c38d..8dd6506 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form
@@ -39,11 +39,9 @@
           </component>
         </children>
       </grid>
-      <component id="6c40c" class="javax.swing.JLabel">
+      <component id="6c40c" class="javax.swing.JLabel" binding="myLabel">
         <constraints>
-          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
-            <preferred-size width="81" height="-1"/>
-          </grid>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
         </constraints>
         <properties>
           <font/>
@@ -67,9 +65,7 @@
       </component>
       <component id="f1e10" class="javax.swing.JButton" binding="myRefreshButton">
         <constraints>
-          <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false">
-            <minimum-size width="30" height="23"/>
-          </grid>
+          <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
         </constraints>
         <properties>
           <hideActionText value="false"/>
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java
index 0f1ec08..6edad63 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java
@@ -2,6 +2,7 @@
 
 import com.intellij.facet.ui.FacetValidatorsManager;
 import com.intellij.facet.ui.ValidationResult;
+import com.intellij.icons.AllIcons;
 import com.intellij.openapi.fileChooser.FileChooser;
 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -9,7 +10,6 @@
 import com.jetbrains.python.edu.StudyDirectoryProjectGenerator;
 import com.jetbrains.python.edu.StudyUtils;
 import com.jetbrains.python.edu.course.CourseInfo;
-import icons.StudyIcons;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -32,6 +32,7 @@
   private JPanel myContentPanel;
   private JLabel myAuthorLabel;
   private JLabel myDescriptionLabel;
+  private JLabel myLabel;
   private final StudyDirectoryProjectGenerator myGenerator;
   private static final String CONNECTION_ERROR = "<html>Failed to download courses.<br>Check your Internet connection.</html>";
   private static final String INVALID_COURSE = "Selected course is invalid";
@@ -56,7 +57,9 @@
     }
     initListeners();
     myRefreshButton.setVisible(true);
-    myRefreshButton.setIcon(StudyIcons.Refresh);
+    myRefreshButton.setIcon(AllIcons.Actions.Refresh);
+
+    myLabel.setPreferredSize(new JLabel("Project name").getPreferredSize());
   }
 
   private void initListeners() {
diff --git a/python/edu/main_pycharm_edu.iml b/python/edu/main_pycharm_edu.iml
index 12efe9b3..17f1d67 100644
--- a/python/edu/main_pycharm_edu.iml
+++ b/python/edu/main_pycharm_edu.iml
@@ -16,6 +16,7 @@
     <orderEntry type="module" module-name="ShortcutPromoter" />
     <orderEntry type="module" module-name="python-educational" />
     <orderEntry type="module" module-name="learn-python" />
+    <orderEntry type="module" module-name="course-creator" />
   </component>
 </module>
 
diff --git a/python/edu/resources/idea/PyCharmEduApplicationInfo.xml b/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
index 0fdf0d4..eed232d 100644
--- a/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
+++ b/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
@@ -19,5 +19,5 @@
 
   <feedback eap-url="http://www.jetbrains.com/feedback/feedback.jsp?product=PyCharm&amp;build=$BUILD&amp;timezone=$TIMEZONE&amp;eval=$EVAL"
             release-url="http://www.jetbrains.com/feedback/feedback.jsp?product=PyCharm&amp;build=$BUILD&amp;timezone=$TIMEZONE&amp;eval=$EVAL"/>
-  <help file="pycharmhelp.jar" root="pycharm"/>
+  <help file="pycharm-eduhelp.jar" root="pycharm"/>
 </component>
diff --git a/python/edu/src/META-INF/PyCharmEduPlugin.xml b/python/edu/src/META-INF/PyCharmEduPlugin.xml
index d5b2fdf..87739a9 100644
--- a/python/edu/src/META-INF/PyCharmEduPlugin.xml
+++ b/python/edu/src/META-INF/PyCharmEduPlugin.xml
@@ -19,6 +19,10 @@
     </component>
   </application-components>
 
+  <extensions defaultExtensionNs="com.intellij">
+      <codeInsight.lineMarkerProvider language="Python" implementationClass="com.jetbrains.python.edu.PyExecuteFileLineMarkerProvider"/>
+  </extensions>
+
   <actions>
     <group overrides="true" class="com.intellij.openapi.actionSystem.EmptyActionGroup" id="ToolsMenu"/>
 
@@ -38,5 +42,11 @@
     <action overrides="true" class="com.intellij.openapi.actionSystem.EmptyAction" id="NewHtmlFile"/>
 
 
+    <group id="PyRunMenu">
+      <action id="runCurrentFile" class="com.jetbrains.python.edu.PyRunCurrentFileAction"/>
+      <add-to-group group-id="RunMenu" anchor="first"/>
+    </group>
+
+
   </actions>
 </idea-plugin>
diff --git a/python/edu/src/com/intellij/openapi/application/PyCharmEduConfigImportSettings.java b/python/edu/src/com/intellij/openapi/application/PyCharmEduConfigImportSettings.java
new file mode 100644
index 0000000..989c750
--- /dev/null
+++ b/python/edu/src/com/intellij/openapi/application/PyCharmEduConfigImportSettings.java
@@ -0,0 +1,12 @@
+package com.intellij.openapi.application;
+
+import com.intellij.ide.plugins.PluginManagerCore;
+
+// see com.intellij.openapi.application.ConfigImportHelper.getConfigImportSettings
+@SuppressWarnings("UnusedDeclaration")
+public class PyCharmEduConfigImportSettings extends ConfigImportSettings {
+  public PyCharmEduConfigImportSettings() {
+    PluginManagerCore.disablePlugin("org.jetbrains.plugins.coursecreator");
+  }
+
+}
diff --git a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
index ecf3d79..2954f7c 100644
--- a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
+++ b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
@@ -35,14 +35,15 @@
 import com.intellij.openapi.keymap.Keymap;
 import com.intellij.openapi.keymap.ex.KeymapManagerEx;
 import com.intellij.openapi.keymap.impl.KeymapImpl;
+import com.intellij.openapi.project.DumbAwareRunnable;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectManager;
 import com.intellij.openapi.project.ProjectManagerAdapter;
 import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.startup.StartupManager;
+import com.intellij.openapi.util.registry.Registry;
 import com.intellij.openapi.vfs.VfsUtil;
-import com.intellij.openapi.wm.ToolWindowEP;
-import com.intellij.openapi.wm.ToolWindowId;
-import com.intellij.openapi.wm.WindowManager;
+import com.intellij.openapi.wm.*;
 import com.intellij.platform.DirectoryProjectConfigurator;
 import com.intellij.platform.PlatformProjectViewOpener;
 import com.intellij.psi.codeStyle.CodeStyleSettings;
@@ -62,9 +63,9 @@
  */
 @SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UtilityClassWithPublicConstructor"})
 public class PyCharmEduInitialConfigurator {
-  @NonNls private static final String DISPLAYED_PROPERTY = "PyCharm.initialConfigurationShown";
+  @NonNls private static final String DISPLAYED_PROPERTY = "PyCharmEDU.initialConfigurationShown";
 
-  @NonNls private static final String CONFIGURED = "PyCharm.InitialConfiguration";
+  @NonNls private static final String CONFIGURED = "PyCharmEDU.InitialConfiguration";
 
 
   public static class First {
@@ -93,6 +94,8 @@
       uiSettings.SHOW_MAIN_TOOLBAR = false;
       codeInsightSettings.REFORMAT_ON_PASTE = CodeInsightSettings.NO_REFORMAT;
 
+      Registry.get("ide.new.settings.dialog").setValue(true);
+
       GeneralSettings.getInstance().setShowTipsOnStartup(false);
 
       EditorSettingsExternalizable.getInstance().setVirtualSpace(false);
@@ -113,7 +116,7 @@
           });
         }
       });
-      PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = true;
+      PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = false;
     }
 
     if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) {
@@ -147,6 +150,29 @@
         }
 
         patchProjectAreaExtensions(project);
+
+        StartupManager.getInstance(project).runWhenProjectIsInitialized(new DumbAwareRunnable() {
+          @Override
+          public void run() {
+            if (project.isDisposed()) return;
+
+            ToolWindowManager.getInstance(project).invokeLater(new Runnable() {
+              int count = 0;
+
+              public void run() {
+                if (project.isDisposed()) return;
+                if (count++ < 3) { // we need to call this after ToolWindowManagerImpl.registerToolWindowsFromBeans
+                  ToolWindowManager.getInstance(project).invokeLater(this);
+                  return;
+                }
+                ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow("Project");
+                if (toolWindow.getType() != ToolWindowType.SLIDING) {
+                  toolWindow.activate(null);
+                }
+              }
+            });
+          }
+        });
       }
     });
   }
@@ -205,6 +231,11 @@
 
   private static void showInitialConfigurationDialog() {
     final JFrame frame = WindowManager.getInstance().findVisibleFrame();
-    new InitialConfigurationDialog(frame, "Python").show();
+    new InitialConfigurationDialog(frame, "Python") {
+      @Override
+      protected boolean canCreateLauncherScript() {
+        return false;
+      }
+    }.show();
   }
 }
diff --git a/python/edu/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java b/python/edu/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java
new file mode 100644
index 0000000..03522bb
--- /dev/null
+++ b/python/edu/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java
@@ -0,0 +1,91 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.codeHighlighting.Pass;
+import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
+import com.intellij.codeInsight.daemon.LineMarkerInfo;
+import com.intellij.codeInsight.daemon.LineMarkerProvider;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiWhiteSpace;
+import com.intellij.psi.util.PsiUtilBase;
+import com.intellij.util.Function;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyImportStatement;
+import com.jetbrains.python.psi.PyStatement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.MouseEvent;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author traff
+ */
+public class PyExecuteFileLineMarkerProvider implements LineMarkerProvider {
+  @Nullable
+  @Override
+  public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
+    return null;
+  }
+
+  @Override
+  public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
+    for (PsiElement element : elements) {
+      if (isFirstCodeLine(element)) {
+        result.add(new LineMarkerInfo<PsiElement>(
+          element, element.getTextRange(), AllIcons.Actions.Execute, Pass.UPDATE_OVERRIDEN_MARKERS,
+          new Function<PsiElement, String>() {
+            @Override
+            public String fun(PsiElement e) {
+              return "Execute '" + e.getContainingFile().getName() + "'";
+            }
+          },
+          new GutterIconNavigationHandler<PsiElement>() {
+            @Override
+            public void navigate(MouseEvent e, PsiElement elt) {
+              executeCurrentScript(elt);
+            }
+          },
+          GutterIconRenderer.Alignment.RIGHT));
+      }
+    }
+  }
+
+  private static void executeCurrentScript(PsiElement elt) {
+    Editor editor = PsiUtilBase.findEditor(elt);
+    assert editor != null;
+
+    final ConfigurationContext context =
+      ConfigurationContext.getFromContext(DataManager.getInstance().getDataContext(editor.getComponent()));
+    PyRunCurrentFileAction.run(context);
+  }
+
+  private static boolean isFirstCodeLine(PsiElement element) {
+    return element instanceof PyStatement &&
+           element.getParent() instanceof PyFile &&
+           !isNothing(element) &&
+           nothingBefore(element);
+  }
+
+  private static boolean nothingBefore(PsiElement element) {
+    element = element.getPrevSibling();
+    while (element != null) {
+      if (!isNothing(element)) {
+        return false;
+      }
+      element = element.getPrevSibling();
+    }
+
+    return true;
+  }
+
+  private static boolean isNothing(PsiElement element) {
+    return (element instanceof PsiComment) || (element instanceof PyImportStatement) || (element instanceof PsiWhiteSpace);
+  }
+}
diff --git a/python/edu/src/com/jetbrains/python/edu/PyRunCurrentFileAction.java b/python/edu/src/com/jetbrains/python/edu/PyRunCurrentFileAction.java
new file mode 100644
index 0000000..4d30fa2
--- /dev/null
+++ b/python/edu/src/com/jetbrains/python/edu/PyRunCurrentFileAction.java
@@ -0,0 +1,56 @@
+package com.jetbrains.python.edu;
+
+import com.intellij.execution.Location;
+import com.intellij.execution.RunManagerEx;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.execution.runners.ExecutionUtil;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.jetbrains.python.PythonFileType;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author traff
+ */
+public class PyRunCurrentFileAction extends AnAction {
+  public PyRunCurrentFileAction() {
+    getTemplatePresentation().setIcon(AllIcons.Actions.Execute);
+  }
+
+  @Override
+  public void update(AnActionEvent e) {
+    Presentation presentation = e.getPresentation();
+    final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext());
+    Location location = context.getLocation();
+    if (location != null && location.getPsiElement().getContainingFile() != null && location.getPsiElement().getContainingFile().getFileType() == PythonFileType.INSTANCE) {
+      presentation.setEnabled(true);
+      presentation.setText("Run '" + location.getPsiElement().getContainingFile().getName() + "'");
+    }
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext());
+
+    run(context);
+  }
+
+  public static void run(@NotNull ConfigurationContext context) {
+    RunnerAndConfigurationSettings configuration = context.findExisting();
+    final RunManagerEx runManager = (RunManagerEx)context.getRunManager();
+    if (configuration == null) {
+      configuration = context.getConfiguration();
+      if (configuration == null) {
+        return;
+      }
+      runManager.setTemporaryConfiguration(configuration);
+    }
+    runManager.setSelectedConfiguration(configuration);
+
+    ExecutionUtil.runConfiguration(configuration, DefaultRunExecutor.getRunExecutorInstance());
+  }
+}