diff --git a/src/Application.vala b/src/Application.vala index afbf15e31..a8744594d 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -155,13 +155,13 @@ namespace Scratch { if (active_window == null) { if (location_jump_manager.has_selection_range () && location_jump_manager.has_override_target ()) { RestoreOverride restore_override = location_jump_manager.create_restore_override (); - add_window (new MainWindow.with_restore_override (true, restore_override)); + add_window (new MainWindow.primary_with_restore_override (restore_override)); } else { - add_window (new MainWindow (true)); // Will restore documents if required + add_window (new MainWindow.primary ()); // Will restore documents if required } } else if (create_new_window) { create_new_window = false; - add_window (new MainWindow (false)); // Will NOT restore documents in additional windows + add_window (new MainWindow.secondary ()); // Will NOT restore documents in additional windows } active_window.present (); diff --git a/src/FolderManager/FileView.vala b/src/FolderManager/FileView.vala index 2bb5035b3..712831390 100644 --- a/src/FolderManager/FileView.vala +++ b/src/FolderManager/FileView.vala @@ -38,23 +38,29 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane public const string ACTION_CLOSE_OTHER_FOLDERS = "close-other-folders"; public const string ACTION_SET_ACTIVE_PROJECT = "set-active-project"; + private const ActionEntry[] PRIMARY_ONLY_ACTION_ENTRIES = { + { ACTION_SET_ACTIVE_PROJECT, action_set_active_project, "s"}, + { ACTION_RENAME_FILE, action_rename_file, "s" }, + { ACTION_RENAME_FOLDER, action_rename_folder, "s" } + }; + private const ActionEntry[] ACTION_ENTRIES = { { ACTION_LAUNCH_APP_WITH_FILE_PATH, action_launch_app_with_file_path, "as" }, { ACTION_SHOW_APP_CHOOSER, action_show_app_chooser, "s" }, { ACTION_EXECUTE_CONTRACT_WITH_FILE_PATH, action_execute_contract_with_file_path, "as" }, - { ACTION_RENAME_FILE, action_rename_file, "s" }, - { ACTION_RENAME_FOLDER, action_rename_folder, "s" }, { ACTION_DELETE, action_delete, "s" }, { ACTION_NEW_FILE, add_new_file, "s" }, { ACTION_NEW_FOLDER, add_new_folder, "s"}, { ACTION_CLOSE_FOLDER, action_close_folder, "s"}, - { ACTION_CLOSE_OTHER_FOLDERS, action_close_other_folders, "s"}, - { ACTION_SET_ACTIVE_PROJECT, action_set_active_project, "s"} + { ACTION_CLOSE_OTHER_FOLDERS, action_close_other_folders, "s"} }; private GLib.Settings settings; private Scratch.Services.GitManager git_manager; - private Scratch.Services.PluginsManager plugins; + private unowned Scratch.MainWindow window; + + public Scratch.Services.PluginsManager plugins { get; construct; } + public bool is_primary { get; construct; } public new signal void activate (string file); public signal bool rename_request (File file); @@ -64,8 +70,12 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane public string icon_name { get; set; } public string title { get; set; } - public FileView (Scratch.Services.PluginsManager plugins_manager) { - plugins = plugins_manager; + public FileView (Scratch.Services.PluginsManager plugins_manager, bool is_primary) { + Object ( + root: new ExpandableItem (), //This does not get created by base class when chaining up + plugins: plugins_manager, + is_primary: is_primary + ); } construct { @@ -79,12 +89,22 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane actions = new SimpleActionGroup (); actions.add_action_entries (ACTION_ENTRIES, this); + if (is_primary) { + actions.add_action_entries (PRIMARY_ONLY_ACTION_ENTRIES, this); + } + insert_action_group (ACTION_GROUP, actions); realize.connect (() => { toplevel_action_group = get_action_group (MainWindow.ACTION_GROUP); assert_nonnull (toplevel_action_group); }); + + show_all (); + + realize.connect (() => { + window = (Scratch.MainWindow) (this.get_toplevel ()); + }); } private void action_close_folder (SimpleAction action, GLib.Variant? parameter) { @@ -122,7 +142,7 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane } //Make remaining project the active one - git_manager.active_project_path = path; + window.active_project_path = path; //Temporary fix write_settings (); } @@ -138,7 +158,7 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane return; } - git_manager.active_project_path = path; + window.active_project_path = path; write_settings (); } @@ -195,7 +215,7 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane public void collapse_other_projects () { unowned string path; - path = git_manager.active_project_path; + path = window.active_project_path; foreach (var child in root.children) { var project_folder = ((ProjectFolderItem) child); @@ -592,10 +612,13 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane yield; } - private bool is_open (File folder) { - foreach (var child in root.children) - if (folder.path == ((Item) child).path) + private bool is_open (File folder) requires (root != null) { + foreach (var child in root.children) { + if (folder.path == ((Item) child).path) { return true; + } + } + return false; } diff --git a/src/FolderManager/FolderItem.vala b/src/FolderManager/FolderItem.vala index 3f55c77c2..8aab7d590 100644 --- a/src/FolderManager/FolderItem.vala +++ b/src/FolderManager/FolderItem.vala @@ -116,7 +116,7 @@ namespace Scratch.FolderManager { } else { var root = get_root_folder (); if (root != null && - root.monitored_repo != null) { + root.has_monitored_repo) { //When toggled closed, update status to reflect hidden contents root.update_item_status (this); } diff --git a/src/FolderManager/Item.vala b/src/FolderManager/Item.vala index 19cb84f11..5a1ad924c 100644 --- a/src/FolderManager/Item.vala +++ b/src/FolderManager/Item.vala @@ -51,6 +51,10 @@ namespace Scratch.FolderManager { file.rename (new_name); } + public bool equal (Item b) { + return path == b.path; + } + public void trash () { file.trash (); } diff --git a/src/FolderManager/ProjectFolderItem.vala b/src/FolderManager/ProjectFolderItem.vala index c53be4d55..673dd72fc 100644 --- a/src/FolderManager/ProjectFolderItem.vala +++ b/src/FolderManager/ProjectFolderItem.vala @@ -31,7 +31,13 @@ namespace Scratch.FolderManager { public signal void closed (); - public Scratch.Services.MonitoredRepository? monitored_repo { get; private set; default = null; } + // public Scratch.Services.MonitoredRepository? monitored_repo { get; private set; default = null; } + private Scratch.Services.MonitoredRepository? monitored_repo; + public bool has_monitored_repo { + get { + return monitored_repo != null; + } + } // Cache the visible item in the project. private List visible_item_list = null; @@ -41,6 +47,7 @@ namespace Scratch.FolderManager { } } + public bool is_readonly { get; private set; } private Ggit.Repository? git_repo { get { return (is_git_repo ? monitored_repo.git_repo : null); @@ -74,7 +81,7 @@ namespace Scratch.FolderManager { } construct { - monitored_repo = Scratch.Services.GitManager.get_instance ().add_project (this); + is_readonly = Scratch.Services.GitManager.get_instance ().add_project (this, out monitored_repo); notify["name"].connect (branch_or_name_changed); if (monitored_repo != null) { checkout_local_branch_action = new SimpleAction.stateful ( @@ -142,7 +149,7 @@ namespace Scratch.FolderManager { } MenuItem set_active_folder_item; - if (is_git_repo) { + if (is_git_repo && !is_readonly) { set_active_folder_item = new GLib.MenuItem ( _("Set as Active Project"), GLib.Action.print_detailed_name ( diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 10e02af36..f401a9543 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -24,9 +24,12 @@ namespace Scratch { public const int FONT_SIZE_MIN = 7; private const uint MAX_SEARCH_TEXT_LENGTH = 255; + public bool is_primary { get; construct; } //TODO should this be mutable? public Scratch.Application app { get; private set; } public bool restore_docs { get; construct; } public RestoreOverride restore_override { get; construct set; } + public string active_project_path { get; set; } + public string default_globalsearch_path { owned get { if (document_view.current_document != null) { @@ -35,7 +38,7 @@ namespace Scratch { } } - return git_manager.active_project_path; + return active_project_path; } } @@ -87,7 +90,6 @@ namespace Scratch { public const string ACTION_ADD_MARK = "action_add_mark"; public const string ACTION_PREVIOUS_MARK = "action_previous_mark"; public const string ACTION_NEXT_MARK = "action_next_mark"; - public const string ACTION_UNDO = "action-undo"; public const string ACTION_REDO = "action-redo"; public const string ACTION_REVERT = "action-revert"; @@ -130,14 +132,19 @@ namespace Scratch { private Services.GitManager git_manager; - private const ActionEntry[] ACTION_ENTRIES = { + private const ActionEntry[] PRIMARY_ONLY_ACTION_ENTRIES = { { ACTION_CLONE_REPO, action_clone_repo }, + { ACTION_OPEN_FOLDER, action_open_folder, "s" }, + { ACTION_TOGGLE_TERMINAL, action_toggle_terminal, null, "false"}, + { ACTION_OPEN_IN_TERMINAL, action_open_in_terminal, "s"}, + { ACTION_NEW_BRANCH, action_new_branch, "s" }, + }; + private const ActionEntry[] ACTION_ENTRIES = { { ACTION_FIND, action_find, "s"}, { ACTION_FIND_NEXT, action_find_next }, { ACTION_FIND_PREVIOUS, action_find_previous }, { ACTION_FIND_GLOBAL, action_find_global, "s" }, { ACTION_OPEN, action_open }, - { ACTION_OPEN_FOLDER, action_open_folder, "s" }, { ACTION_COLLAPSE_ALL_FOLDERS, action_collapse_all_folders }, { ACTION_ORDER_FOLDERS, action_order_folders }, { ACTION_PREFERENCES, action_preferences }, @@ -151,7 +158,6 @@ namespace Scratch { { ACTION_NEW_TAB, action_new_tab }, { ACTION_NEW_FROM_CLIPBOARD, action_new_tab_from_clipboard }, { ACTION_DUPLICATE_TAB, action_duplicate_tab }, - { ACTION_PREFERENCES, action_preferences }, { ACTION_UNDO, action_undo }, { ACTION_REDO, action_redo }, { ACTION_SHOW_REPLACE, action_show_replace }, @@ -165,13 +171,10 @@ namespace Scratch { { ACTION_ZOOM_OUT, action_zoom_out}, { ACTION_TOGGLE_COMMENT, action_toggle_comment }, { ACTION_TOGGLE_SIDEBAR, action_toggle_sidebar, null, "true" }, - { ACTION_TOGGLE_TERMINAL, action_toggle_terminal, null, "false"}, - { ACTION_OPEN_IN_TERMINAL, action_open_in_terminal, "s"}, { ACTION_TOGGLE_OUTLINE, action_toggle_outline, null, "false" }, { ACTION_NEXT_TAB, action_next_tab }, { ACTION_PREVIOUS_TAB, action_previous_tab }, { ACTION_CLEAR_LINES, action_clear_lines }, - { ACTION_NEW_BRANCH, action_new_branch, "s" }, { ACTION_ADD_MARK, action_add_mark}, { ACTION_PREVIOUS_MARK, action_previous_mark}, { ACTION_NEXT_MARK, action_next_mark}, @@ -186,17 +189,26 @@ namespace Scratch { { ACTION_OPEN_IN_NEW_WINDOW, action_open_in_new_window, "s" }, }; - public MainWindow (bool restore_docs) { + public MainWindow.primary () { + Object ( + is_primary: true, + icon_name: Constants.PROJECT_NAME, + restore_docs: true + ); + } + public MainWindow.secondary () { Object ( + is_primary: false, icon_name: Constants.PROJECT_NAME, - restore_docs: restore_docs + restore_docs: false ); } - public MainWindow.with_restore_override (bool restore_docs, RestoreOverride restore_override) { + public MainWindow.primary_with_restore_override (RestoreOverride restore_override) { Object ( + is_primary: true, icon_name: Constants.PROJECT_NAME, - restore_docs: restore_docs, + restore_docs: true, restore_override: restore_override ); } @@ -279,6 +291,9 @@ namespace Scratch { actions = new SimpleActionGroup (); actions.add_action_entries (ACTION_ENTRIES, this); + if (is_primary) { + actions.add_action_entries (PRIMARY_ONLY_ACTION_ENTRIES, this); + } insert_action_group (ACTION_GROUP, actions); foreach (var action in action_accelerators.get_keys ()) { @@ -349,12 +364,22 @@ namespace Scratch { outline_action.set_state (saved_state.get_boolean ("outline-visible")); update_toolbar_button (ACTION_TOGGLE_OUTLINE, saved_state.get_boolean ("outline-visible")); - var terminal_action = Utils.action_from_group (ACTION_TOGGLE_TERMINAL, actions); - terminal_action.set_state (saved_state.get_boolean ("terminal-visible")); - update_toolbar_button (ACTION_TOGGLE_TERMINAL, saved_state.get_boolean ("terminal-visible")); + + if (is_primary) { + var terminal_action = Utils.action_from_group (ACTION_TOGGLE_TERMINAL, actions); + terminal_action.set_state (saved_state.get_boolean ("terminal-visible")); + update_toolbar_button (ACTION_TOGGLE_TERMINAL, saved_state.get_boolean ("terminal-visible")); + } else { + terminal.visible = false; + } Unix.signal_add (Posix.Signal.INT, quit_source_func, Priority.HIGH); Unix.signal_add (Posix.Signal.TERM, quit_source_func, Priority.HIGH); + + bind_property ("active-project-path", sidebar.choose_project_button, "active-project-path", BIDIRECTIONAL); + if (is_primary) { + settings.bind ("active-project-path", this, "active-project-path", DEFAULT); + } } private void update_style () { @@ -482,7 +507,7 @@ namespace Scratch { sidebar = new Code.Sidebar (); - folder_manager_view = new FolderManager.FileView (plugins); + folder_manager_view = new FolderManager.FileView (plugins, is_primary); sidebar.add_tab (folder_manager_view); folder_manager_view.show_all (); @@ -1012,7 +1037,7 @@ namespace Scratch { return; } - var new_window = new MainWindow (false); + var new_window = new MainWindow.secondary (); var file = File.new_for_path (path); var doc = new Scratch.Services.Document (new_window.actions, file); @@ -1045,8 +1070,8 @@ namespace Scratch { private void action_clone_repo (SimpleAction action, Variant? param) { var default_projects_folder = Scratch.settings.get_string ("default-projects-folder"); - if (default_projects_folder == "" && git_manager.active_project_path != "") { - default_projects_folder = Path.get_dirname (git_manager.active_project_path); + if (default_projects_folder == "" && active_project_path != "") { + default_projects_folder = Path.get_dirname (active_project_path); } var default_remote = Scratch.settings.get_string ("default-remote"); @@ -1338,7 +1363,7 @@ namespace Scratch { Utils.action_from_group (ACTION_TOGGLE_SHOW_FIND, actions).set_enabled (is_current_doc); Utils.action_from_group (ACTION_FIND_NEXT, actions).set_enabled (is_current_doc); Utils.action_from_group (ACTION_FIND_PREVIOUS, actions).set_enabled (is_current_doc); - var can_global_search = is_current_doc || git_manager.active_project_path != null; + var can_global_search = is_current_doc || active_project_path != null; Utils.action_from_group (ACTION_FIND_GLOBAL, actions).set_enabled (can_global_search); return Source.REMOVE; @@ -1518,7 +1543,7 @@ namespace Scratch { } if (path == "") { // Happens when keyboard accelerator is used - path = git_manager.active_project_path; + path = active_project_path; if (use_build_dir) { path = git_manager.get_default_build_dir (path); } diff --git a/src/Services/GitManager.vala b/src/Services/GitManager.vala index 5b80d3084..04f55e546 100644 --- a/src/Services/GitManager.vala +++ b/src/Services/GitManager.vala @@ -21,7 +21,6 @@ namespace Scratch.Services { public class GitManager : Object { public ListStore project_liststore { get; private set; } - public string active_project_path { get; set; default = "";} static Gee.HashMap project_gitrepo_map; static GitManager? instance; @@ -47,18 +46,27 @@ namespace Scratch.Services { construct { // Used to populate the ChooseProject popover in sorted order project_liststore = new ListStore (typeof (FolderManager.ProjectFolderItem)); - settings.bind ("active-project-path", this, "active-project-path", DEFAULT); } - public MonitoredRepository? add_project (FolderManager.ProjectFolderItem root_folder) { - var root_path = root_folder.file.file.get_path (); - MonitoredRepository? monitored_repo = null; + public bool add_project (FolderManager.ProjectFolderItem root_folder, out MonitoredRepository? monitored_repo) { + var root_path = root_folder.path; + monitored_repo = null; + uint position; + if (project_liststore.find_with_equal_func ( + root_folder, + (a, b) => { return ((FolderManager.Item) a).equal ((FolderManager.Item) b); }, + out position + )) { + + monitored_repo = project_gitrepo_map.@get (root_path); + return true; + } + try { var git_repo = Ggit.Repository.open (root_folder.file.file); if (!project_gitrepo_map.has_key (root_path)) { monitored_repo = new MonitoredRepository (git_repo); project_gitrepo_map.@set (root_path, monitored_repo); - return project_gitrepo_map.@get (root_path); } } catch (Error e) { debug ( @@ -74,7 +82,8 @@ namespace Scratch.Services { } // No longer need to set default project (restored from settings or left unset) - return project_gitrepo_map.@get (root_path); + monitored_repo = project_gitrepo_map.@get (root_path); + return false; } [CCode (instance_pos = -1)] @@ -100,17 +109,16 @@ namespace Scratch.Services { } // @project_path is the root of a project or null - public string get_default_build_dir (string? project_path) { - string build_path = project_path != null ? project_path : active_project_path; + public string get_default_build_dir (string project_path) { var default_build_dir = Scratch.settings.get_string ("default-build-directory"); - var build_file = GLib.File.new_for_path (Path.build_filename (build_path, default_build_dir)); + var build_file = GLib.File.new_for_path (Path.build_filename (project_path, default_build_dir)); if (build_file.query_exists ()) { - build_path = build_file.get_path (); + return build_file.get_path (); } else { warning ("build path not found %s", build_file.get_path ()); } - return build_path; + return project_path; } public async bool clone_repository ( diff --git a/src/Widgets/ChooseProjectButton.vala b/src/Widgets/ChooseProjectButton.vala index 3b035c5b2..5b17c9ed1 100644 --- a/src/Widgets/ChooseProjectButton.vala +++ b/src/Widgets/ChooseProjectButton.vala @@ -21,6 +21,7 @@ public class Code.ChooseProjectButton : Gtk.MenuButton { private const string PROJECT_TOOLTIP = N_("Active Git Project: %s"); private Gtk.Label label_widget; private Gtk.ListBox project_listbox; + public string active_project_path { get; set; } public signal void project_chosen (); @@ -99,7 +100,16 @@ public class Code.ChooseProjectButton : Gtk.MenuButton { popover = project_popover; + // Initialise with any pre-existing projects (needed for second and subsequent window) var git_manager = Scratch.Services.GitManager.get_instance (); + var src = git_manager.project_liststore; + for (int index = 0; index < src.n_items; index++) { + var item = src.get_object (index); + if (item is Scratch.FolderManager.ProjectFolderItem) { + var row = create_project_row ((Scratch.FolderManager.ProjectFolderItem)item); + project_listbox.insert (row, index); + } + } git_manager.project_liststore.items_changed.connect ((src, pos, n_removed, n_added) => { var rows = project_listbox.get_children (); @@ -119,40 +129,38 @@ public class Code.ChooseProjectButton : Gtk.MenuButton { project_listbox.remove.connect ((row) => { var project_row = row as ProjectRow; - var current_project = Scratch.Services.GitManager.get_instance ().active_project_path; + var current_project = active_project_path; if (project_row.project_path == current_project) { - Scratch.Services.GitManager.get_instance ().active_project_path = ""; + active_project_path = ""; // Label and active_path will be updated automatically } }); project_listbox.row_activated.connect ((row) => { var project_entry = ((ProjectRow) row); - git_manager.active_project_path = project_entry.project_path; + active_project_path = project_entry.project_path; project_chosen (); }); toggled.connect (() => { if (active) { - unowned var active_path = Scratch.Services.GitManager.get_instance ().active_project_path; foreach (var child in project_listbox.get_children ()) { var project_row = ((ProjectRow) child); // All paths must not end in directory separator so can be compared directly - project_row.active = active_path == project_row.project_path; + project_row.active = active_project_path == project_row.project_path; } } }); - git_manager.notify["active-project-path"].connect (update_button); + notify["active-project-path"].connect (update_button); update_button (); } // Set appearance (only) of project chooser button and list according to active path private void update_button () { - unowned var active_path = Scratch.Services.GitManager.get_instance ().active_project_path; - if (active_path != "") { - label_widget.label = Path.get_basename (active_path); - tooltip_text = _(PROJECT_TOOLTIP).printf (Scratch.Utils.replace_home_with_tilde (active_path)); + if (active_project_path != "") { + label_widget.label = Path.get_basename (active_project_path); + tooltip_text = _(PROJECT_TOOLTIP).printf (Scratch.Utils.replace_home_with_tilde (active_project_path)); } else { label_widget.label = Path.get_basename (_(NO_PROJECT_SELECTED)); tooltip_text = _(PROJECT_TOOLTIP).printf (_(NO_PROJECT_SELECTED)); diff --git a/src/Widgets/DocumentView.vala b/src/Widgets/DocumentView.vala index 30fa7f56c..9cdc7eb0f 100644 --- a/src/Widgets/DocumentView.vala +++ b/src/Widgets/DocumentView.vala @@ -219,7 +219,7 @@ public class Scratch.Widgets.DocumentView : Gtk.Box { return; } - var new_window = new MainWindow (false); + var new_window = new MainWindow.secondary (); tab_view.transfer_page (target, new_window.document_view.tab_view, 0); } @@ -546,7 +546,7 @@ public class Scratch.Widgets.DocumentView : Gtk.Box { } private unowned Hdy.TabView? on_doc_to_new_window (Hdy.TabView tab_view) { - var other_window = new MainWindow (false); + var other_window = new MainWindow.secondary (); return other_window.document_view.tab_view; }