Skip to content

Commit

Permalink
[GTK4 Prep] Add FileView actions (#1444)
Browse files Browse the repository at this point in the history
  • Loading branch information
colinkiama authored Jul 2, 2024
1 parent 256d0bc commit 4ea4df8
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 86 deletions.
38 changes: 10 additions & 28 deletions src/FolderManager/FileItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -120,35 +120,17 @@ namespace Scratch.FolderManager {
var contractor_item = new Gtk.MenuItem.with_label (_("Other Actions"));
contractor_item.submenu = contractor_menu;

var rename_item = new Gtk.MenuItem.with_label (_("Rename"));
rename_item.activate.connect (() => {
// Ensure item selected else rename does not work
view.select_path (file.path);
if (view.start_editing_item (this)) {
ulong once = 0;
once = this.edited.connect ((new_name) => {
this.disconnect (once);
var new_path = Path.get_dirname (file.path) + Path.DIR_SEPARATOR_S + new_name;
view.toplevel_action_group.activate_action (MainWindow.ACTION_CLOSE_TAB, new Variant.string (this.path));
view.select (new_path); // Select and open newly named file
});

// Handle cancelled rename (which does not produce signal)
Timeout.add (200, () => {
if (view.editing) {
return Source.CONTINUE;
} else {
// Avoid selected but unopened item if rename cancelled (they would not open if clicked on)
view.unselect_all ();
return Source.REMOVE;
}
});
}
});
var rename_item = new Gtk.MenuItem.with_label (_("Rename")) {
action_name = FileView.ACTION_PREFIX + FileView.ACTION_RENAME_FILE,
action_target = new Variant.string (file.path),
};
var rename_action = Utils.action_from_group (FileView.ACTION_RENAME_FILE, view.actions);
rename_action.set_enabled (view.rename_request (file));

rename_item.sensitive = view.rename_request (file);
var delete_item = new Gtk.MenuItem.with_label (_("Move to Trash"));
delete_item.activate.connect (trash);
var delete_item = new Gtk.MenuItem.with_label (_("Move to Trash")) {
action_name = FileView.ACTION_PREFIX + FileView.ACTION_DELETE,
action_target = new Variant.string (file.path)
};

var menu = new Gtk.Menu ();
menu.append (open_in_terminal_pane_item);
Expand Down
206 changes: 191 additions & 15 deletions src/FolderManager/FileView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,34 @@
* SourceList that displays folders and their contents.
*/
public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.PaneSwitcher {
public const string ACTION_GROUP = "file-view";
public const string ACTION_PREFIX = ACTION_GROUP + ".";
public const string ACTION_RENAME_FILE = "rename-file";
public const string ACTION_RENAME_FOLDER = "rename-folder";
public const string ACTION_DELETE = "delete";
public const string ACTION_NEW_FILE = "new-file";
public const string ACTION_NEW_FOLDER = "new-folder";
public const string ACTION_CLOSE_FOLDER = "close-folder";
public const string ACTION_CLOSE_OTHER_FOLDERS = "close-other-folders";
private const ActionEntry[] ACTION_ENTRIES = {
{ 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"}
};

private GLib.Settings settings;
private Scratch.Services.GitManager git_manager;

public ActionGroup toplevel_action_group { get; private set; }
private Scratch.Services.PluginsManager plugins;

public signal void select (string file);
public signal bool rename_request (File file);

public SimpleActionGroup actions { get; private set; }
public ActionGroup toplevel_action_group { get; private set; }
public bool ignore_next_select { get; set; default = false; }
public string icon_name { get; set; }
public string title { get; set; }
Expand All @@ -54,12 +73,53 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane

git_manager = Scratch.Services.GitManager.get_instance ();

actions = new SimpleActionGroup ();
actions.add_action_entries (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);
});
}

private void action_close_folder (SimpleAction action, GLib.Variant? parameter) {
var path = parameter.get_string ();
if (path == null || path == "") {
return;
}

var project_item = find_path (root, path) as ProjectFolderItem;
if (project_item == null) {
return;
}

project_item.closed ();
}

private void action_close_other_folders (SimpleAction action, GLib.Variant? parameter) {
var path = parameter.get_string ();
if (path == null || path == "") {
return;
}

var folder_root = find_path (root, path) as ProjectFolderItem;
if (folder_root == null) {
return;
}

foreach (var child in root.children) {
var project_folder_item = (ProjectFolderItem) child;
if (project_folder_item != folder_root) {
toplevel_action_group.activate_action (MainWindow.ACTION_CLOSE_PROJECT_DOCS, new Variant.string (project_folder_item.path));
root.remove (project_folder_item);
Scratch.Services.GitManager.get_instance ().remove_project (project_folder_item);
}
}

write_settings ();
}

private void on_item_selected (Code.Widgets.SourceList.Item? item) {
// This is a workaround for SourceList silliness: you cannot remove an item
// without it automatically selecting another one.
Expand Down Expand Up @@ -246,6 +306,58 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
plugins.hook_folder_item_change (source, dest, event);
}

private void rename_file (string path) {
this.select_path (path);
if (this.start_editing_item (selected)) {
ulong once = 0;
once = selected.edited.connect ((new_name) => {
selected.disconnect (once);
var new_path = Path.get_dirname (path) + Path.DIR_SEPARATOR_S + new_name;
this.toplevel_action_group.activate_action (MainWindow.ACTION_CLOSE_TAB, new Variant.string (path));
this.select (new_path);
});
}

// Handle cancelled rename (which does not produce signal)
Timeout.add (200, () => {
if (this.editing) {
return Source.CONTINUE;
} else {
// Avoid selected but unopened item if rename cancelled (they would not open if clicked on)
this.unselect_all ();
return Source.REMOVE;
}
});
}

private void rename_folder (string path) {
var folder_to_rename = find_path (root, path) as FolderItem;
if (folder_to_rename == null) {
critical ("Could not find folder from given path to rename: %s", path);
return;
}

folder_to_rename.selectable = true;
if (start_editing_item (folder_to_rename)) {
// Need to poll view as no signal emited when editing cancelled and need to set
// selectable to false anyway.
Timeout.add (200, () => {
if (editing) {
return Source.CONTINUE;
} else {
unselect_all ();
// Must do this *after* unselecting all else sourcelist breaks
folder_to_rename.selectable = false;
}

return Source.REMOVE;
});
} else {
critical ("Could not rename %s", path);
folder_to_rename.selectable = false;
}
}

private void rename_items_with_same_name (Item item) {
string item_name = item.file.name;
foreach (var child in this.root.children) {
Expand All @@ -266,6 +378,83 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
item.name = item_name;
}

private void add_new_folder (SimpleAction action, Variant? param) {
// Using "path" of parent folder from params, call `on_add_new (true)` on `FolderItem`
var path = param.get_string ();

if (path == null || path == "") {
return;
}

var folder = find_path (root, path) as FolderItem;
if (folder == null) {
return;
}

folder.on_add_new (true);
}

private void add_new_file (SimpleAction action, Variant? param) {
// Using "path" of parent folder from params, call `on_add_new (false)` on `FolderItem`
var path = param.get_string ();

if (path == null || path == "") {
return;
}

var folder = find_path (root, path) as FolderItem;
if (folder == null) {
return;
}

folder.on_add_new (false);
}

private void action_rename_file (SimpleAction action, Variant? param) {
var path = param.get_string ();

if (path == null || path == "") {
return;
}

rename_file (path);
}

private void action_rename_folder (SimpleAction action, Variant? param) {
var path = param.get_string ();

if (path == null || path == "") {
return;
}

rename_folder (path);
}


private void action_delete (SimpleAction action, Variant? param) {
var path = param.get_string ();

if (path == null || path == "") {
return;
}

var item = find_path (root, path);
if (item != null) {
var item_to_delete = item as Scratch.FolderManager.Item;

// Wait for ProjectFolderItem closed signal handle logic to run before moving item to trash
if (item_to_delete is Scratch.FolderManager.ProjectFolderItem) {
item_to_delete.closed.connect_after (() => {
item_to_delete.trash ();
});
item_to_delete.closed ();
return;
}

item_to_delete.trash ();
}
}

private void add_folder (File folder, bool expand) {
if (is_open (folder)) {
warning ("Folder '%s' is already open.", folder.path);
Expand Down Expand Up @@ -293,19 +482,6 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
write_settings ();
});

folder_root.close_all_except.connect (() => {
foreach (var child in root.children) {
var project_folder_item = (ProjectFolderItem)child;
if (project_folder_item != folder_root) {
toplevel_action_group.activate_action (MainWindow.ACTION_CLOSE_PROJECT_DOCS, new Variant.string (project_folder_item.path));
root.remove (project_folder_item);
Scratch.Services.GitManager.get_instance ().remove_project (project_folder_item);
}
}

write_settings ();
});

write_settings ();
}

Expand Down
46 changes: 17 additions & 29 deletions src/FolderManager/FolderItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -121,31 +121,15 @@ namespace Scratch.FolderManager {
var contractor_item = new Gtk.MenuItem.with_label (_("Other Actions"));
contractor_item.submenu = contractor_menu;

var rename_menu_item = new Gtk.MenuItem.with_label (_("Rename"));
rename_menu_item.activate.connect (() => {
selectable = true;
if (view.start_editing_item (this)) {
// Need to poll view as no signal emited when editing cancelled and need to set
// selectable to false anyway.
Timeout.add (200, () => {
if (view.editing) {
return Source.CONTINUE;
} else {
view.unselect_all ();
// Must do this *after* unselecting all else sourcelist breaks
selectable = false;
}

return Source.REMOVE;
});
} else {
debug ("Could not rename %s", file.path);
selectable = false;
}
});
var rename_menu_item = new Gtk.MenuItem.with_label (_("Rename")) {
action_name = FileView.ACTION_PREFIX + FileView.ACTION_RENAME_FOLDER,
action_target = new Variant.string (file.path)
};

var delete_item = new Gtk.MenuItem.with_label (_("Move to Trash"));
delete_item.activate.connect (trash);
var delete_item = new Gtk.MenuItem.with_label (_("Move to Trash")) {
action_name = FileView.ACTION_PREFIX + FileView.ACTION_DELETE,
action_target = new Variant.string (file.path)
};

var search_item = new Gtk.MenuItem.with_label (_("Find in Folder…")) {
action_name = "win.action_find_global",
Expand Down Expand Up @@ -215,11 +199,15 @@ namespace Scratch.FolderManager {
}

protected Gtk.MenuItem create_submenu_for_new () {
var new_folder_item = new Gtk.MenuItem.with_label (_("Folder"));
new_folder_item.activate.connect (() => on_add_new (true));
var new_folder_item = new Gtk.MenuItem.with_label (_("Folder")) {
action_name = FileView.ACTION_PREFIX + FileView.ACTION_NEW_FOLDER,
action_target = new Variant.string (file.path)
};

var new_file_item = new Gtk.MenuItem.with_label (_("Empty File"));
new_file_item.activate.connect (() => on_add_new (false));
var new_file_item = new Gtk.MenuItem.with_label (_("Empty File")) {
action_name = FileView.ACTION_PREFIX + FileView.ACTION_NEW_FILE,
action_target = new Variant.string (file.path)
};

var new_menu = new Gtk.Menu ();
new_menu.append (new_folder_item);
Expand Down Expand Up @@ -377,7 +365,7 @@ namespace Scratch.FolderManager {
return null;
}

private void on_add_new (bool is_folder) {
public void on_add_new (bool is_folder) {
if (!file.is_executable) {
// This is necessary to avoid infinite loop below
warning ("Unable to open parent folder");
Expand Down
2 changes: 1 addition & 1 deletion src/FolderManager/Item.vala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ namespace Scratch.FolderManager {
file.rename (new_name);
}

protected void trash () {
public void trash () {
file.trash ();
}

Expand Down
Loading

0 comments on commit 4ea4df8

Please sign in to comment.