From 911dcffb0dbf7abd93884b3422ed8ce73afbe824 Mon Sep 17 00:00:00 2001
From: Vitaly Livshic <shiche@yandex.ru>
Date: Sat, 26 Aug 2017 19:49:15 +0300
Subject: [PATCH] #73 Last opened files automatically opens

---
 src/dlangide/ui/frame.d                    | 32 ++++++++++++++++++----
 src/dlangide/workspace/workspace.d         | 10 +++++++
 src/dlangide/workspace/workspacesettings.d | 30 ++++++++++++++++++++
 views/res/i18n/en.ini                      |  8 ++++++
 views/res/i18n/ru.ini                      |  8 ++++++
 5 files changed, 82 insertions(+), 6 deletions(-)

diff --git a/src/dlangide/ui/frame.d b/src/dlangide/ui/frame.d
index 6d15c85..045738b 100644
--- a/src/dlangide/ui/frame.d
+++ b/src/dlangide/ui/frame.d
@@ -181,7 +181,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL
         if (!project)
             project = currentWorkspace.startupProject;
         if (!project) {
-            window.showMessageBox(UIString.fromRaw("Cannot debug project"d), UIString.fromRaw("Startup project is not specified"d));
+            window.showMessageBox(UIString.fromId("ERROR_CANNOT_DEBUG_PROJECT"c), UIString.fromId("ERROR_STARTUP_PROJECT_ABSENT"c));
             return;
         }
         buildProject(BuildOperation.Build, project, delegate(int result) {
@@ -451,7 +451,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL
             } else {
                 destroy(editor);
                 if (window)
-                    window.showMessageBox(UIString.fromRaw("File open error"d), UIString.fromRaw("Failed to open file "d ~ toUTF32(file.filename)));
+                    window.showMessageBox(UIString.fromId("ERROR_OPEN_FILE"c), UIString.fromId("ERROR_OPENING_FILE"c) ~ " " ~ toUTF32(file.filename));
                 return false;
             }
         }
@@ -569,7 +569,8 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL
         }
         string tabId = ed.id;
         // tab content is modified - ask for confirmation
-        window.showMessageBox(UIString.fromRaw("Close file "d ~ toUTF32(baseName(tabId))), UIString.fromRaw("Content of this file has been changed."d), 
+        auto header = UIString.fromRaw("HEADER_CLOSE_FILE"c);
+        window.showMessageBox(header ~ " " ~ toUTF32(baseName(tabId)), UIString.fromId("MSG_FILE_CONTENT_CHANGED"c), 
                               [ACTION_SAVE, ACTION_SAVE_ALL, ACTION_DISCARD_CHANGES, ACTION_DISCARD_ALL, ACTION_CANCEL], 
                               0, delegate(const Action result) {
                                   if (result == StandardAction.Save) {
@@ -608,7 +609,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL
             DSourceEdit d = cast(DSourceEdit)_tabs.tabBody(tabId);
             if (d && d.content.modified) {
                 // tab content is modified - ask for confirmation
-                window.showMessageBox(UIString.fromRaw("Close tab"d), UIString.fromRaw("Content of "d ~ toUTF32(baseName(tabId)) ~ " file has been changed."d), 
+                window.showMessageBox(UIString.fromId("HEADER_CLOSE_TAB"c), UIString.fromId("MSG_TAB_CONTENT_CHANGED"c) ~ ": " ~ toUTF32(baseName(tabId)), 
                                       [ACTION_SAVE, ACTION_DISCARD_CHANGES, ACTION_CANCEL], 
                                       0, delegate(const Action result) {
                                           if (result == StandardAction.Save) {
@@ -1294,6 +1295,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL
         dlg.show();
     }
 
+    // Applying settings to tabs/sources and it's opening
     void applySettings(IDESettings settings) {
         for (int i = _tabs.tabCount - 1; i >= 0; i--) {
             DSourceEdit ed = cast(DSourceEdit)_tabs.tabBody(i);
@@ -1332,7 +1334,12 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL
                     askForUnsavedEdits(delegate() {
                     setWorkspace(ws);
                     hideHomeScreen();
+                    // Write workspace to recent workspaces list
                     _settings.updateRecentWorkspace(filename);
+                    // All was opened, attempt to restore files
+                    const string[] files = currentWorkspace.files();
+                    for (int i; i < files.length; i++)
+                        openSourceFile(files[i]);
                 });
             } else {
                 window.showMessageBox(UIString.fromId("ERROR_OPEN_WORKSPACE"c).value, UIString.fromId("ERROR_OPENING_WORKSPACE"c).value);
@@ -1415,7 +1422,9 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL
         currentWorkspace = ws;
         _wsPanel.workspace = ws;
         requestActionsUpdate();
-        if (ws && ws.startupProject && ws.startupProject.mainSourceFile) {
+        // Open main file for project
+        if (ws && ws.startupProject && ws.startupProject.mainSourceFile 
+            && (currentWorkspace.files == null || currentWorkspace.files.length == 0)) {
             openSourceFile(ws.startupProject.mainSourceFile.filename);
             _tabs.setFocus();
         }
@@ -1492,8 +1501,19 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL
     /// return false to prevent closing
     bool onCanClose() {
         askForUnsavedEdits(delegate() {
-            if (currentWorkspace)
+            if (currentWorkspace) {
+                // Remember opened files
+                string[] files;
+                for (auto i = 0; i < _tabs.tabCount(); i++)
+                {
+                    auto edit = cast(DSourceEdit)_tabs.tabBody(i);
+                    if (edit !is null)
+                        files ~= edit.filename();
+                }
+                currentWorkspace.files(files);
+                // saving workspace
                 currentWorkspace.save();
+            }
             window.close();
         });
         return false;
diff --git a/src/dlangide/workspace/workspace.d b/src/dlangide/workspace/workspace.d
index a487c9e..fbaa827 100644
--- a/src/dlangide/workspace/workspace.d
+++ b/src/dlangide/workspace/workspace.d
@@ -96,6 +96,16 @@ class Workspace : WorkspaceItem {
         _frame.setProjectConfigurations(project.configurations.keys.map!(k => k.to!dstring).array); 
         _settings.startupProjectName = toUTF8(project.name);
     }
+    
+    /// Last opened files in workspace
+    @property string[] files() {
+        return _settings.files();
+    }
+    
+    /// Last opened files in workspace
+    @property void files(string[] fs) {
+        _settings.files(fs);
+    }
 
     /// setups currrent project configuration by name
     void setStartupProjectConfiguration(string conf)
diff --git a/src/dlangide/workspace/workspacesettings.d b/src/dlangide/workspace/workspacesettings.d
index 8d10a5a..cf65e2a 100644
--- a/src/dlangide/workspace/workspacesettings.d
+++ b/src/dlangide/workspace/workspacesettings.d
@@ -17,6 +17,8 @@ class WorkspaceSettings : SettingsFile {
 
     private Breakpoint[] _breakpoints;
     private EditorBookmark[] _bookmarks;
+    /// Last opened files in workspace
+    private string[] _files;
 
     private string _startupProjectName;
     @property string startupProjectName() {
@@ -30,6 +32,25 @@ class WorkspaceSettings : SettingsFile {
         }
     }
 
+    /// Last opened files in workspace    
+    @property string[] files() {
+        return _files;
+    }
+    
+    /// Last opened files in workspace
+    @property void files(string[] fs) {
+        _files = fs;
+        // Save to settings file
+        Setting obj = _setting.settingByPath("files", SettingType.ARRAY);
+        obj.clear(SettingType.ARRAY);
+        int index = 0;
+        foreach(file; fs) {
+            Setting single = new Setting();
+            single.setString("file", file);
+            obj[index++] = single;
+        }
+    }
+
     /// get all breakpoints for project (for specified source file only, if specified)
     Breakpoint[] getProjectBreakpoints(string projectName, string projectFilePath) {
         Breakpoint[] res;
@@ -151,6 +172,7 @@ class WorkspaceSettings : SettingsFile {
     /// override to do something after loading - e.g. set defaults
     override void afterLoad() {
         _startupProjectName = _setting.getString("startupProject");
+        // Loading breakpoints
         Setting obj = _setting.settingByPath("breakpoints", SettingType.ARRAY);
         _breakpoints = null;
         int maxBreakpointId = 0;
@@ -168,6 +190,7 @@ class WorkspaceSettings : SettingsFile {
             _breakpoints ~= bp;
         }
         _nextBreakpointId = maxBreakpointId + 1;
+        // Loading bookmarks
         obj = _setting.settingByPath("bookmarks", SettingType.ARRAY);
         _bookmarks = null;
         for (int i = 0; i < obj.length; i++) {
@@ -179,6 +202,13 @@ class WorkspaceSettings : SettingsFile {
             bp.line = cast(int)item.getInteger("line");
             _bookmarks ~= bp;
         }
+        // Loading files
+        _files = null;
+        obj = _setting.settingByPath("files", SettingType.ARRAY);
+        for (int i = 0; i < obj.length; i++) {
+            Setting item = obj[i];
+            _files ~= item.getString("file");
+        }
     }
 
     override void updateDefaults() {
diff --git a/views/res/i18n/en.ini b/views/res/i18n/en.ini
index 99f95ca..1409152 100644
--- a/views/res/i18n/en.ini
+++ b/views/res/i18n/en.ini
@@ -117,6 +117,8 @@ MENU_PROJECT_REVEAL_IN_EXPLORER=Reveal in Explorer
 HEADER_SETTINGS=DlangIDE settings
 HEADER_OPEN_WORKSPACE_OR_PROJECT=Open Workspace or Project
 HEADER_OPEN_TEXT_FILE=Open Text File
+HEADER_CLOSE_FILE=Close file
+HEADER_CLOSE_TAB=Close tab
 
 OPTION_ADD_TO_CURRENT_WORKSPACE=Add to current workspace
 OPTION_AUTO_OPEN_LAST_PROJECT=Auto open last project
@@ -159,13 +161,19 @@ OPTION_THEME=Theme
 OPTION_TOOLCHANS=Toolchains
 OPTION_USE_SPACES=Use spaces for tabs
 
+ERROR_CANNOT_DEBUG_PROJECT=Cannot debug project
+ERROR_STARTUP_PROJECT_ABSENT=Startup project is not specified
 ERROR_INVALID_WORKSPACE_FILE=Invalid workspace file
 ERROR_INVALID_WS_OR_PROJECT_FILE=This file is not a valid workspace or project file
+ERROR_OPEN_FILE=File open error
 ERROR_OPEN_PROJECT=Cannot open project
 ERROR_OPEN_WORKSPACE=Cannot open workspace
+ERROR_OPENING_FILE=Failed to open file
 ERROR_OPENING_PROJECT=Error occured while opening project
 ERROR_OPENING_WORKSPACE=Error occured while opening workspace
 
+MSG_FILE_CONTENT_CHANGED=Content of this file has been changed.
+MSG_TAB_CONTENT_CHANGED=Content of tab has been changed
 MSG_OPEN_PROJECT=Open project
 MSG_PROJECT_ALREADY_OPENED=Project is already in workspace
 MSG_STARTING=Starting
diff --git a/views/res/i18n/ru.ini b/views/res/i18n/ru.ini
index 7c0a4cc..504a249 100644
--- a/views/res/i18n/ru.ini
+++ b/views/res/i18n/ru.ini
@@ -117,6 +117,8 @@ MENU_PROJECT_REVEAL_IN_EXPLORER=Открыть в проводнике
 HEADER_SETTINGS=DlangIDE настройки
 HEADER_OPEN_WORKSPACE_OR_PROJECT=Открыть рабочее пространство или проект
 HEADER_OPEN_TEXT_FILE=Открыть текстовый файл
+HEADER_CLOSE_FILE=Закрыть файл
+HEADER_CLOSE_TAB=Закрыть вкладку
 
 OPTION_ADD_TO_CURRENT_WORKSPACE=Добавить в текущее пр-во
 OPTION_AUTO_OPEN_LAST_PROJECT=Авт.открывать последний проект
@@ -159,13 +161,19 @@ OPTION_THEME=Тема оформления
 OPTION_TOOLCHANS=Наборы инструментов
 OPTION_USE_SPACES=Использовать пробелы вместо табуляции
 
+ERROR_CANNOT_DEBUG_PROJECT=Невозможно отлаживать проект
+ERROR_STARTUP_PROJECT_ABSENT=Стартовый проект не указан
 ERROR_INVALID_WORKSPACE_FILE=Неверный файл раб.пространства
 ERROR_INVALID_WS_OR_PROJECT_FILE=Файл не является ни файлом раб.пространства, ни проектным
+ERROR_OPEN_FILE=Невозможно открыть файл
 ERROR_OPEN_PROJECT=Невозможно открыть проект
 ERROR_OPEN_WORKSPACE=Невозможно открыть рабочее пространство
+ERROR_OPENING_FILE=Ошибка открытия файла
 ERROR_OPENING_PROJECT=Ошибка в ходе открытия проекта
 ERROR_OPENING_WORKSPACE=Ошибка в ходе открытия рабочего пространства
 
+MSG_FILE_CONTENT_CHANGED=Содержимое этого файла изменено.
+MSG_TAB_CONTENT_CHANGED=Содержимое вкладки изменено
 MSG_OPEN_PROJECT=Открыть проект
 MSG_PROJECT_ALREADY_OPENED=Проект уже в рабочем пространстве
 MSG_STARTING=Выполняем