Skip to content

Commit

Permalink
Do not allow simultaneous opening of projects with parent-child relat…
Browse files Browse the repository at this point in the history
…ionship (#1500)

* Check whether new project is parent or child of existing one

* Dialog before close parent/child projects

* Use ngettext for dialog text

* Update POTFILES

* Update copyright date on changed files

* Restore FileView completely before restore open documents

---------

Co-authored-by: Jeremy Wootten <[email protected]>
  • Loading branch information
jeremypw and Jeremy Wootten authored Feb 4, 2025
1 parent f0cdf42 commit 04ff180
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 31 deletions.
1 change: 1 addition & 0 deletions po/POTFILES
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ src/Dialogs/GlobalSearchDialog.vala
src/Dialogs/NewBranchDialog.vala
src/Dialogs/PreferencesDialog.vala
src/Dialogs/RestoreConfirmationDialog.vala
src/Dialogs/CloseProjectsConfirmationDialog.vala
src/Dialogs/OverwriteUncommittedConfirmationDialog.vala
src/FolderManager/File.vala
src/FolderManager/FileItem.vala
Expand Down
67 changes: 67 additions & 0 deletions src/Dialogs/CloseProjectsConfirmationDialog.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/

public class Scratch.Dialogs.CloseProjectsConfirmationDialog : Granite.MessageDialog {

public uint n_parents { get; construct; }
public uint n_children { get; construct; }

public CloseProjectsConfirmationDialog (MainWindow parent, uint n_parents, uint n_children) {
Object (
buttons: Gtk.ButtonsType.NONE,
transient_for: parent,
n_parents: n_parents,
n_children: n_children
);
}

construct {
image_icon = new ThemedIcon ("dialog-warning");
var button_label = "";
// We can assume that either n_parents or n_children is zero (but not both).
// We can assume n_parents is either zero or one
if (n_children > 0) {
primary_text = ngettext (
_("This folder is the parent of an open project"),
_("This folder is the parent of %u open projects").printf (n_children),
(ulong) n_children
);
;
secondary_text = ngettext (
_("Opening this folder will close the child project"),
_("Opening this folder will close all child projects"),
(ulong) n_children
);

button_label = ngettext (
_("Close Child Project"),
_("Close Child Projects"),
(ulong) n_children
);
} else {
primary_text = _("This folder is a child of an open project");
secondary_text = _("Opening this folder will close the parent project");
button_label = _("Close Parent Project");
}

add_button (_("Don't Open"), Gtk.ResponseType.REJECT);

var ignore_button = (Gtk.Button) add_button (button_label, Gtk.ResponseType.ACCEPT);
ignore_button.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION);
}
}
89 changes: 72 additions & 17 deletions src/FolderManager/FileView.vala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*-
* Copyright (c) 2017 - 2022 elementary LLC. (https://elementary.io),
* Copyright (c) 2017 - 2024 elementary LLC. (https://elementary.io),
* 2013 Julien Spautz <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -126,9 +126,9 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
write_settings ();
}

public void restore_saved_state () {
public async void restore_saved_state () {
foreach (unowned string path in settings.get_strv ("opened-folders")) {
add_folder (new File (path), false);
yield add_folder (new File (path), false);
}
}

Expand Down Expand Up @@ -505,7 +505,7 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
}
}

private void add_folder (File folder, bool expand) {
private async void add_folder (File folder, bool expand) {
if (is_open (folder)) {
warning ("Folder '%s' is already open.", folder.path);
return;
Expand All @@ -514,25 +514,80 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
return;
}

var folder_root = new ProjectFolderItem (folder, this); // Constructor adds project to GitManager
this.root.add (folder_root);
rename_items_with_same_name (folder_root);
var add_file = folder.file;
// Need to deal with case where folder is parent or child of an existing project
var parents = new List<ProjectFolderItem> ();
var children = new List<ProjectFolderItem> ();

folder_root.expanded = expand;
folder_root.closed.connect (() => {
toplevel_action_group.activate_action (MainWindow.ACTION_CLOSE_PROJECT_DOCS, new Variant.string (folder_root.path));
root.remove (folder_root);
foreach (var child in root.children) {
var child_folder = (ProjectFolderItem) child;
if (child_folder.name != child_folder.file.name) {
rename_items_with_same_name (child_folder);
foreach (var child in root.children) {
var item = (ProjectFolderItem) child;
if (add_file.get_relative_path (item.file.file) != null) {
debug ("Trying to add parent of existing project");
children.append (item);
} else if (item.file.file.get_relative_path (add_file) != null) {
debug ("Trying to add child of existing project");
parents.append (item);
}
}

if (parents.length () > 0 || children.length () > 0) {
assert (parents.length () <= 1);
assert (parents.length () == 0 || children.length () == 0);
var dialog = new Scratch.Dialogs.CloseProjectsConfirmationDialog (
(MainWindow) get_toplevel (),
parents.length (),
children.length ()
);

var close_projects = false;
dialog.response.connect ((res) => {
dialog.destroy ();
if (res == Gtk.ResponseType.ACCEPT) {
close_projects = true;
}
});

dialog.run ();

if (close_projects) {
foreach (var item in parents) {
item.closed ();
}

foreach (var item in children) {
item.closed ();
}
} else {
return;
}
Scratch.Services.GitManager.get_instance ().remove_project (folder_root);
}

// Process any closed signals emitted before proceeding
Idle.add (() => {
var folder_root = new ProjectFolderItem (folder, this); // Constructor adds project to GitManager
this.root.add (folder_root);
rename_items_with_same_name (folder_root);

folder_root.expanded = expand;
folder_root.closed.connect (() => {
toplevel_action_group.activate_action (MainWindow.ACTION_CLOSE_PROJECT_DOCS, new Variant.string (folder_root.path));
root.remove (folder_root);
foreach (var child in root.children) {
var child_folder = (ProjectFolderItem) child;
if (child_folder.name != child_folder.file.name) {
rename_items_with_same_name (child_folder);
}
}
Scratch.Services.GitManager.get_instance ().remove_project (folder_root);
write_settings ();
});

write_settings ();
add_folder.callback ();
return Source.REMOVE;
});

write_settings ();
yield;
}

private bool is_open (File folder) {
Expand Down
32 changes: 18 additions & 14 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,6 @@ namespace Scratch {
toolbar.templates_button.visible = (plugins.plugin_iface.template_manager.template_available);
});

// Restore session
restore_saved_state_extra ();

// Create folder for unsaved documents
create_unsaved_documents_directory ();

Expand Down Expand Up @@ -492,9 +489,6 @@ namespace Scratch {
}
});


folder_manager_view.restore_saved_state ();

folder_manager_view.rename_request.connect ((file) => {
var allow = true;
foreach (var window in app.get_windows ()) {
Expand Down Expand Up @@ -576,13 +570,11 @@ namespace Scratch {
});

hook_func ();

restore ();
});

document_view.realize.connect (() => {
if (restore_docs) {
restore_opened_documents ();
}

document_view.update_outline_visible ();
update_find_actions ();
});
Expand Down Expand Up @@ -697,6 +689,7 @@ namespace Scratch {
}
}

// DocumentView's number of docs updates asychronously so need Idle
Idle.add (() => {
document_view.request_placeholder_if_empty ();
restore_override = null;
Expand Down Expand Up @@ -761,8 +754,7 @@ namespace Scratch {
bool focus = true,
int cursor_position = 0) {

FolderManager.ProjectFolderItem? project = folder_manager_view.get_project_for_file (doc.file);
doc.source_view.project = project;
doc.source_view.project = folder_manager_view.get_project_for_file (doc.file);
document_view.open_document (doc, focus, cursor_position);
}

Expand All @@ -774,8 +766,7 @@ namespace Scratch {
return;
}

FolderManager.ProjectFolderItem? project = folder_manager_view.get_project_for_file (doc.file);
doc.source_view.project = project;
doc.source_view.project = folder_manager_view.get_project_for_file (doc.file);
document_view.open_document (doc, focus, 0, range);
}

Expand All @@ -797,6 +788,19 @@ namespace Scratch {
return true;
}

private void restore () {
// Plugin panes size
hp1.set_position (Scratch.saved_state.get_int ("hp1-size"));
vp.set_position (Scratch.saved_state.get_int ("vp-size"));
// Ensure foldermanager finishes loading projects before start opening documents
folder_manager_view.restore_saved_state.begin ((obj, res) => {
folder_manager_view.restore_saved_state.end (res);
if (restore_docs) {
restore_opened_documents ();
}
});
}

// Save session information different from window state
private void restore_saved_state_extra () {
// Plugin panes size
Expand Down
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ code_files = files(
'Utils.vala',
'Dialogs/PreferencesDialog.vala',
'Dialogs/RestoreConfirmationDialog.vala',
'Dialogs/CloseProjectsConfirmationDialog.vala',
'Dialogs/OverwriteUncommittedConfirmationDialog.vala',
'Dialogs/GlobalSearchDialog.vala',
'Dialogs/NewBranchDialog.vala',
Expand Down

0 comments on commit 04ff180

Please sign in to comment.