Skip to content

Commit

Permalink
Merge branch 'master' into fix-disambiguate
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy Wootten committed Mar 2, 2023
2 parents 6fdf947 + 83b09e4 commit 1270974
Show file tree
Hide file tree
Showing 411 changed files with 48,873 additions and 161,213 deletions.
412 changes: 38 additions & 374 deletions data/io.elementary.files.appdata.xml.in.in

Large diffs are not rendered by default.

Binary file modified data/screenshot-column.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified data/screenshot-grid.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified data/screenshot-list.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 7 additions & 2 deletions filechooser-portal/FileChooserDialog.vala
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ public class Files.FileChooserDialog : Hdy.Window, Xdp.Request {
next_button_clicked = false;

location_bar.set_display_path (current_path);
tree_view.grab_focus ();
});

chooser.file_activated.connect (() => {
Expand All @@ -291,6 +290,12 @@ public class Files.FileChooserDialog : Hdy.Window, Xdp.Request {
});

chooser.set_current_folder_uri (settings.get_string ("last-folder-uri"));

if (action == Gtk.FileChooserAction.SAVE) {
entry.grab_focus ();
} else {
tree_view.grab_focus ();
}
}

private static T find_child_by_name<T> (Gtk.Widget root, string path) requires (root is Gtk.Container) {
Expand Down Expand Up @@ -368,6 +373,7 @@ public class Files.FileChooserDialog : Hdy.Window, Xdp.Request {

// bind the accept_button sensitivity with the entry text
entry = find_child_by_name (grid, "<GtkFileChooserEntry>");
entry.set_placeholder_text (_("Enter new filename"));
entry.bind_property ("text-length", accept_button, "sensitive", BindingFlags.SYNC_CREATE);
entry.activate.connect (() => {
if (accept_button.sensitive) {
Expand Down Expand Up @@ -419,7 +425,6 @@ public class Files.FileChooserDialog : Hdy.Window, Xdp.Request {

public void set_current_name (string text) {
chooser.set_current_name (text);
entry.grab_focus ();
}

public string get_uri () {
Expand Down
114 changes: 105 additions & 9 deletions filechooser-portal/Main.vala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*-
* Copyright 2020-2021 elementary LLC <https://elementary.io>
* Copyright 2020-2023 elementary, Inc. (https://elementary.io)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
Expand Down Expand Up @@ -38,6 +38,38 @@ public class Files.FileChooserPortal : Object {
dialogs = new HashTable<string, FileChooserDialog> (str_hash, str_equal);
}

/**
* Requests the user for a URI.
*
* The URI should point to a file, if "multiple" is true, more than one URI
* can be chosen by the user. If "directory" is true, the URI should point
* to a folder instead.
*
* A filter can be specified with "current_filter", or "filters", if more
* than one filter could be used. Application specific options can be added
* to the dialog with "choices" and a label to the select button set with
* "accept_label".
*
* @param handle
* Object path where the request should be exported
* @param app_id
* Application originating the call (not used)
* @param parent_window
* Transient parent handle, in format "type:handle". if type is "x11",
* handle should be a XID in hexadecimal format, if type is "wayland",
* handle should be a xdg_foreign handle. Other types aren't supported.
* @param title
* title for the file chooser dialog
* @param options
* Dictionary of extra options. valid keys are: "accept_label",
* "choices", "current_filter", "directory", "filters", "multiple"
* and "modal".
* @param response
* User response, 0 on success, 1 if cancelled, 2 on fail.
* @param results
* Dictionary with the user choices, possible keys are: "uris",
* "choices", "writable" and "current_filter".
*/
public async void open_file (
ObjectPath handle,
string app_id,
Expand Down Expand Up @@ -139,6 +171,38 @@ public class Files.FileChooserPortal : Object {
results = _results;
}

/**
* Requests the user for a URI, with intent to write to it.
*
* The URI should point to a file, if the URI is already existent,
* permission is asked for it usage. "current_folder", "current_name" and
* "current_file" can be used to pre-select a file.
*
* A filter can be specified with "current_filter" option, or "filters",
* if more than one filter could be used. Application specific options
* can be added to the dialog with "choices" and a label to the select
* button set with "accept_label".
*
* @param handle
* Object path where the request should be exported
* @param app_id
* Application originating the call (not used)
* @param parent_window
* Transient parent handle, in format "type:handle". if type is "x11",
* handle should be a XID in hexadecimal format, if type is "wayland",
* handle should be a xdg_foreign handle. Other types aren't supported.
* @param title
* title for the file chooser dialog
* @param options
* Dictionary of extra options. valid keys are: "accept_label",
* "choices", "current_file", "current_filter", "current_folder",
* "current_name", "filters" and "modal".
* @param response
* User response, 0 on success, 1 if cancelled, 2 on fail.
* @param results
* Dictionary with the user choices, possible keys are: "uris",
* "choices", and "current_filter".
*/
public async void save_file (
ObjectPath handle,
string app_id,
Expand Down Expand Up @@ -263,6 +327,35 @@ public class Files.FileChooserPortal : Object {
results = _results;
}

/**
* Requests the user for a URI, with the intent to write files inside it.
*
* Filenames are specified via the "files" option, a folder can be
* pre-selected with "current_folder". If a file with the same name as one
* in the "files" list exists, it's replaced.
*
* Application specific options can be added to the dialog with "choices"
* and a label to the select button set with "accept_label".
*
* @param handle
* Object path where the request should be exported
* @param app_id
* Application originating the call (not used)
* @param parent_window
* Transient parent handle, in format "type:handle". if type is "x11",
* handle should be a XID in hexadecimal format. if type is "wayland",
* handle should be a xdg_foreign handle. Other types aren't supported.
* @param title
* title for the file chooser dialog
* @param options
* Dictionary of extra options. valid keys are: "accept_label",
* "choices", "current_folder", "files" and "modal".
* @param response
* User response, 0 on success, 1 if cancelled, 2 on fail.
* @param results
* Dictionary with the user choices, possible keys are: "uris" and
* "choices".
*/
public async void save_files (
ObjectPath handle,
string app_id,
Expand Down Expand Up @@ -348,24 +441,27 @@ public class Files.FileChooserPortal : Object {
}

private Gtk.Dialog create_overwrite_dialog (Gtk.Window parent, GLib.File file) {
unowned var primary = _("Replace “%s”?");
unowned var secondary = _("Replacing this file will overwrite its current contents");
var display_name = file.get_basename ();

string primary, secondary;
if (file.query_file_type (FileQueryInfoFlags.NOFOLLOW_SYMLINKS) == FileType.SYMBOLIC_LINK) {
try {
var info = file.query_info (FileAttribute.STANDARD_SYMLINK_TARGET, FileQueryInfoFlags.NONE);
display_name = info.get_symlink_target ();
primary = _("This file is a link to “%s").printf (info.get_symlink_target ());
} catch (Error e) {
warning ("Could not get info for %s", file.get_uri ());
primary = _("Replace the target of “%s”?");
primary = _("This file is a link.");
}

secondary = _("Replacing the target file for this link will overwrite its current contents.");
secondary = _("Replacing a link will overwrite the target's contents. The link will remain");
} else {
primary = _("Replace “%s”?").printf (file.get_basename ());
secondary = _("Replacing this file will overwrite its current contents");
}

var replace_dialog = new Granite.MessageDialog.with_image_from_icon_name (
primary.printf (display_name), secondary, "dialog-warning", Gtk.ButtonsType.CANCEL
primary,
secondary,
"dialog-warning",
Gtk.ButtonsType.CANCEL
) {
modal = true,
transient_for = parent
Expand Down
35 changes: 19 additions & 16 deletions libcore/Directory.vala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class Files.Directory : Object {
}

public delegate void FileLoadedFunc (Files.File file);
public delegate void DoneLoadingFunc ();

private uint load_timeout_id = 0;
private uint mount_timeout_id = 0;
Expand Down Expand Up @@ -166,7 +167,7 @@ public class Files.Directory : Object {
* to perform filename completion.- Emitting a done_loaded signal in that case would cause
* the premature ending of text entry.
**/
public void init (FileLoadedFunc? file_loaded_func = null) {
public void init (FileLoadedFunc? file_loaded_func = null, DoneLoadingFunc? done_loading_func = null) {
if (state == State.LOADING) {
debug ("Directory Init re-entered - already loading");
return; /* Do not re-enter */
Expand All @@ -180,19 +181,19 @@ public class Files.Directory : Object {

/* If we already have a loaded file cache just list them */
if (previous_state == State.LOADED) {
list_cached_files (file_loaded_func);
list_cached_files (file_loaded_func, done_loading_func);
/* else fully initialise the directory */
} else {
state = State.LOADING;
prepare_directory.begin (file_loaded_func);
prepare_directory.begin (file_loaded_func, done_loading_func);
}
/* done_loaded signal is emitted when ready */
}

/* This is also called when reloading the directory so that another attempt to connect to
* the network is made
*/
private async void prepare_directory (FileLoadedFunc? file_loaded_func) {
private async void prepare_directory (FileLoadedFunc? file_loaded_func, DoneLoadingFunc? done_loading_func) {
debug ("Preparing directory for loading");
/* Force info to be refreshed - the Files.File may have been created already by another part of the program
* that did not ensure the correct info Aync purposes, and retrieved from cache (bug 1511307).
Expand Down Expand Up @@ -228,7 +229,7 @@ public class Files.Directory : Object {
);

/* Only place that should call make_ready function */
yield make_ready (file.is_connected && (is_no_info || success), file_loaded_func);
yield make_ready (is_no_info || success, file_loaded_func, done_loading_func); /* Only place that should call this function */
}

/*** Returns false if should be able to get info but were unable to ***/
Expand Down Expand Up @@ -423,7 +424,7 @@ public class Files.Directory : Object {
}


private async void make_ready (bool ready, FileLoadedFunc? file_loaded_func = null) {
private async void make_ready (bool ready, FileLoadedFunc? file_loaded_func, DoneLoadingFunc? done_loading_func) {
debug ("make ready");
can_load = ready;

Expand All @@ -441,7 +442,7 @@ public class Files.Directory : Object {
file.exists.to_string ());
directory_cache.remove (creation_key);
is_ready = false;
after_loading (file_loaded_func);
after_loading (done_loading_func);
return;
}

Expand Down Expand Up @@ -494,7 +495,7 @@ public class Files.Directory : Object {
connect_volume_monitor_signals ();
}

yield list_directory_async (file_loaded_func);
yield list_directory_async (file_loaded_func, done_loading_func);
}

private void set_confirm_trash () {
Expand Down Expand Up @@ -590,7 +591,7 @@ public class Files.Directory : Object {
}
}

private void list_cached_files (FileLoadedFunc? file_loaded_func = null) {
private void list_cached_files (FileLoadedFunc? file_loaded_func, DoneLoadingFunc? done_loading_func) {
debug ("list cached files");
if (state != State.LOADED) {
critical ("list cached files called in %s state - not expected to happen", state.to_string ());
Expand All @@ -609,10 +610,10 @@ public class Files.Directory : Object {
state = State.LOADED;
loaded_from_cache = true;

after_loading (file_loaded_func);
after_loading (done_loading_func);
}

private async void list_directory_async (FileLoadedFunc? file_loaded_func) {
private async void list_directory_async (FileLoadedFunc? file_loaded_func, DoneLoadingFunc? done_loading_func) {
debug ("list directory async");
/* Should only be called after creation and if reloaded */
if (!is_ready || file_hash.size () > 0) {
Expand Down Expand Up @@ -709,7 +710,7 @@ public class Files.Directory : Object {
} finally {
cancel_timeout (ref load_timeout_id);
loaded_from_cache = false;
after_loading (file_loaded_func);
after_loading (done_loading_func);
}
}

Expand All @@ -725,7 +726,7 @@ public class Files.Directory : Object {
}
}

private void after_loading (FileLoadedFunc? file_loaded_func) {
private void after_loading (DoneLoadingFunc? done_loading_func) {
/* If loading failed reset */
debug ("after loading state is %s", state.to_string ());
if (state == State.LOADING || state == State.TIMED_OUT) {
Expand All @@ -737,8 +738,10 @@ public class Files.Directory : Object {
clear_directory_info ();
}

if (file_loaded_func == null) {
if (done_loading_func == null) {
done_loading ();
} else {
done_loading_func ();
}

if (file.is_directory) { /* Fails for non-existent directories */
Expand Down Expand Up @@ -769,9 +772,9 @@ public class Files.Directory : Object {
return;
}
if (state != State.LOADED) {
list_directory_async.begin (null);
list_directory_async.begin (null, null);
} else {
list_cached_files ();
list_cached_files (null, null);
}
}

Expand Down
34 changes: 11 additions & 23 deletions libcore/FileUtils.vala
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ namespace Files.FileUtils {
parent_path = path;
}

if ((parent_path.has_prefix (Files.MTP_URI) || parent_path.has_prefix (Files.PTP_URI)) &&
!valid_mtp_uri (parent_path)) {

parent_path = path;
}

if (parent_path == Files.SMB_URI) {
parent_path = parent_path + Path.DIR_SEPARATOR_S;
}
Expand Down Expand Up @@ -271,7 +265,15 @@ namespace Files.FileUtils {
rc = rc.replace ("'", "");
}

return Uri.escape_string ((Uri.unescape_string (uri) ?? uri), rc , allow_utf8);
var scheme = Uri.parse_scheme (uri);
var escaped_uri = Uri.escape_string ((Uri.unescape_string (uri) ?? uri), rc , allow_utf8);
if (scheme == "mtp" || scheme == "gphoto2") {
// We want to escape colons except in protocol as some servers have a colon in name
escaped_uri = escaped_uri.replace (":", "%3A");
escaped_uri = escaped_uri.replace ("%3A//", "://");
}

return escaped_uri;
}

/** Produce a valid unescaped path. A current path can be provided and is used to get the scheme and
Expand Down Expand Up @@ -438,11 +440,12 @@ namespace Files.FileUtils {
if (explode_protocol[0] == "mtp" || explode_protocol[0] == "gphoto2" ) {
string[] explode_path = explode_protocol[1].split ("]", 2);
if (explode_path[0] != null && explode_path[0].has_prefix ("[")) {
// Old form of address
protocol = (explode_protocol[0] + "://" + explode_path[0] + "]").replace ("///", "//");
/* If path is being manually edited there may not be "]" so explode_path[1] may be null*/
new_path = explode_path [1] ?? "";
} else {
debug ("Invalid mtp or ptp path %s", path);
// New form of address
protocol = explode_protocol[0] + "://";
new_path = explode_protocol[1] ?? "";
}
Expand All @@ -465,21 +468,6 @@ namespace Files.FileUtils {
}
}

private bool valid_mtp_uri (string uri) {
if (!uri.contains (Files.MTP_URI) && !uri.contains (Files.PTP_URI)) {
return false;
}

string[] explode_protocol = uri.split ("://", 2);
if (explode_protocol.length != 2 ||
!explode_protocol[1].has_prefix ("[") ||
!explode_protocol[1].contains ("]")) {
return false;
}

return true;
}

public string get_smb_share_from_uri (string uri) {
if (!(Uri.parse_scheme (uri) == "smb")) {
return (uri);
Expand Down
Loading

0 comments on commit 1270974

Please sign in to comment.