From 87ba373882f475f02a524a120b59ea9fcccf14e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 20 Nov 2023 18:25:30 +0100 Subject: New upstream version 0.32.3 --- src/AppWindow.vala | 52 ++++++++++++++++++++++++---------------- src/BatchImport.vala | 6 +++++ src/MediaDataRepresentation.vala | 2 +- src/Photo.vala | 3 ++- src/PhotoPage.vala | 13 +++++++--- src/Properties.vala | 10 ++++---- src/Thumbnail.vala | 7 ++++++ src/ThumbnailCache.vala | 17 +++++++++++-- src/camera/GPhoto.vala | 14 +---------- src/camera/ImportPage.vala | 5 +--- src/db/TagTable.vala | 2 +- src/direct/DirectPhotoPage.vala | 11 ++++++++- src/direct/DirectWindow.vala | 3 --- src/folders/FoldersBranch.vala | 10 +++++--- src/util/string.vala | 6 ----- 15 files changed, 98 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/AppWindow.vala b/src/AppWindow.vala index 438806c..1fb0515 100644 --- a/src/AppWindow.vala +++ b/src/AppWindow.vala @@ -23,7 +23,17 @@ public class FullscreenWindow : PageWindow { { "LeaveFullscreen", on_close } }; - public FullscreenWindow(Page page) { + public struct Monitor { + Gdk.Screen screen; + Gdk.Monitor? monitor; + int monitor_nr; + + public Gdk.Rectangle get_geometry() { + return monitor.get_geometry(); + } + } + + public FullscreenWindow(Page page, Monitor monitor) { base (); set_current_page(page); @@ -32,12 +42,7 @@ public class FullscreenWindow : PageWindow { const string[] accels = { "F11", null }; Application.set_accels_for_action ("win.LeaveFullscreen", accels); - set_screen(AppWindow.get_instance().get_screen()); - - // Needed so fullscreen will occur on correct monitor in multi-monitor setups - Gdk.Rectangle monitor = get_monitor_geometry(); - move(monitor.x, monitor.y); - + set_screen(monitor.screen); set_border_width(0); // restore pin state @@ -80,10 +85,11 @@ public class FullscreenWindow : PageWindow { // call to set_default_size() saves one repaint caused by changing // size from default to full screen. In slideshow mode, this change // also causes pixbuf cache updates, so it really saves some work. - set_default_size(monitor.width, monitor.height); + Gdk.Rectangle monitor_geometry = monitor.get_geometry(); + set_default_size(monitor_geometry.width, monitor_geometry.height); // need to create a Gdk.Window to set masks - fullscreen(); + fullscreen_on_monitor(monitor.screen, monitor.monitor_nr); show_all(); // capture motion events to show the toolbar @@ -113,11 +119,6 @@ public class FullscreenWindow : PageWindow { public void update_toolbar_dismissal() { is_toolbar_dismissal_enabled = !pin_button.get_active(); } - - private Gdk.Rectangle get_monitor_geometry() { - var monitor = get_display().get_monitor_at_window(AppWindow.get_instance().get_window()); - return monitor.get_geometry(); - } public override bool configure_event(Gdk.EventConfigure event) { bool result = base.configure_event(event); @@ -200,6 +201,11 @@ public class FullscreenWindow : PageWindow { int py; seat.get_pointer().get_position(null, null, out py); + + // If we are on a completely different screen, ignore it + if (seat.get_display() != toolbar.get_display()) { + return false; + } int wy; toolbar.get_window().get_geometry(null, out wy, null, null); @@ -402,8 +408,6 @@ public abstract class AppWindow : PageWindow { // added to is the one that claims its accelerators protected bool maximized = false; protected Dimensions dimensions; - protected int pos_x = 0; - protected int pos_y = 0; protected AppWindow() { base(); @@ -659,10 +663,20 @@ public abstract class AppWindow : PageWindow { return; } - get_position(out pos_x, out pos_y); + // Need to call this before hide() otherwise we will always get + // the left-most monitor + FullscreenWindow.Monitor monitor= {get_screen(), null, 0}; + var display = get_display(); + for (var i = 0; i < display.get_n_monitors(); i++) { + if (display.get_monitor(i) == display.get_monitor_at_window(get_window())) { + monitor.monitor = display.get_monitor(i); + monitor.monitor_nr = i; + break; + } + } hide(); - FullscreenWindow fsw = new FullscreenWindow(page); + FullscreenWindow fsw = new FullscreenWindow(page, monitor); if (get_current_page() != null) get_current_page().switching_to_fullscreen(fsw); @@ -675,8 +689,6 @@ public abstract class AppWindow : PageWindow { if (fullscreen_window == null) return; - move(pos_x, pos_y); - show_all(); if (get_current_page() != null) diff --git a/src/BatchImport.vala b/src/BatchImport.vala index 90ccba8..ae4f573 100644 --- a/src/BatchImport.vala +++ b/src/BatchImport.vala @@ -1508,7 +1508,13 @@ private class WorkSniffer : BackgroundImportJob { FileToPrepare file_a = (FileToPrepare) a; FileToPrepare file_b = (FileToPrepare) b; string sa = file_a.get_path(); + if (!sa.validate()) { + sa = Uri.escape_string(sa, Uri.RESERVED_CHARS_ALLOWED_IN_PATH, true); + } string sb = file_b.get_path(); + if (!sb.validate()) { + sb = Uri.escape_string(sa, Uri.RESERVED_CHARS_ALLOWED_IN_PATH, true); + } return utf8_cs_compare(sa, sb); }); diff --git a/src/MediaDataRepresentation.vala b/src/MediaDataRepresentation.vala index 3400577..e0e6b7f 100644 --- a/src/MediaDataRepresentation.vala +++ b/src/MediaDataRepresentation.vala @@ -596,7 +596,7 @@ public abstract class MediaSourceCollection : DatabaseSourceCollection { string[] components = source_id.split("-"); assert(components.length == 2); - return fetch_by_numeric_id(parse_int64(components[1], 16)); + return fetch_by_numeric_id(int64.parse(components[1], 16)); } public abstract Gee.Collection get_event_source_ids(EventID event_id); diff --git a/src/Photo.vala b/src/Photo.vala index 34cfedf..f636a32 100644 --- a/src/Photo.vala +++ b/src/Photo.vala @@ -3808,6 +3808,7 @@ public abstract class Photo : PhotoSource, Dateable, Positionable { if (ext == null || !file_format.get_properties().is_recognized_extension(ext)) ext = file_format.get_properties().get_default_extension(); + // TRANSLATORS: "modified" here is part of a file name that was changed with another image tool outside of Shotwell. Note that there are potential issues with UTF-8 characters string editable_basename = "%s_%s.%s".printf(name, _("modified"), ext); bool collision; @@ -4837,7 +4838,7 @@ public class LibraryPhotoSourceCollection : MediaSourceCollection { assert(source_id.has_prefix(Photo.TYPENAME)); string numeric_only = source_id.substring(Photo.TYPENAME.length, -1); - return fetch_by_numeric_id(parse_int64(numeric_only, 16)); + return fetch_by_numeric_id(int64.parse(numeric_only, 16)); } public override Gee.Collection get_event_source_ids(EventID event_id){ diff --git a/src/PhotoPage.vala b/src/PhotoPage.vala index 10ebb10..a279d89 100644 --- a/src/PhotoPage.vala +++ b/src/PhotoPage.vala @@ -78,17 +78,24 @@ public class ZoomBuffer : Object { private TransformationJob? demand_transform_job = null; // only 1 demand transform job can be // active at a time private Workers workers = null; - private SinglePhotoPage parent_page; + private unowned SinglePhotoPage parent_page; private bool is_interactive_redraw_in_progress = false; public ZoomBuffer(SinglePhotoPage parent_page, Photo backing_photo, Gdk.Pixbuf preview_image) { this.parent_page = parent_page; + this.parent_page.add_weak_pointer(&this.parent_page); this.preview_image = preview_image; this.backing_photo = backing_photo; this.workers = new Workers(2, false); } + ~ZoomBuffer() { + if (this.parent_page != null) { + this.parent_page.remove_weak_pointer(&this.parent_page); + } + } + private void on_iso_source_fetch_complete(BackgroundJob job) { IsoSourceFetchJob fetch_job = (IsoSourceFetchJob) job; if (fetch_job.fetched == null) { @@ -103,7 +110,7 @@ public class ZoomBuffer : Object { } object_state = ObjectState.SOURCE_NOT_TRANSFORMED; - if (!is_interactive_redraw_in_progress) + if (!is_interactive_redraw_in_progress && parent_page != null) parent_page.repaint(); BackgroundJob transformation_job = new TransformationJob(this, iso_source_image, @@ -140,7 +147,7 @@ public class ZoomBuffer : Object { demand_transform_cached_pixbuf = transform_job.transformed; demand_transform_job = null; - parent_page.repaint(); + if (parent_page != null) parent_page.repaint(); } // passing a 'reduced_pixbuf' that has one-quarter the number of pixels as the 'iso_pixbuf' is diff --git a/src/Properties.vala b/src/Properties.vala index c0cf2fd..b8c3e0d 100644 --- a/src/Properties.vala +++ b/src/Properties.vala @@ -46,13 +46,13 @@ private abstract class Properties : Gtk.Box { } else { Gtk.Label info_label = new Gtk.Label(""); if (!is_string_empty(info_text)) { - info_label.set_tooltip_markup(info_text); + info_label.set_tooltip_text(info_text); } if (href == null) { - info_label.set_markup(is_string_empty(info_text) ? "" : info_text); + info_label.set_text(is_string_empty(info_text) ? "" : info_text); } else { - info_label.set_markup("%s".printf(href, info_text)); + info_label.set_markup("%s".printf(href, Markup.escape_text(info_text))); } info_label.set_ellipsize(Pango.EllipsizeMode.END); info_label.halign = Gtk.Align.START; @@ -534,8 +534,8 @@ private class ExtendedProperties : Properties { if (source is MediaSource) { MediaSource media = (MediaSource) source; - file_path = media.get_master_file().get_path(); - development_path = media.get_file().get_path(); + file_path = Filename.display_name(media.get_master_file().get_path()); + development_path = Filename.display_name(media.get_file().get_path()); filesize = media.get_master_filesize(); // as of right now, all extended properties other than filesize, filepath & comment aren't diff --git a/src/Thumbnail.vala b/src/Thumbnail.vala index 51d2612..54fe361 100644 --- a/src/Thumbnail.vala +++ b/src/Thumbnail.vala @@ -194,6 +194,13 @@ public class Thumbnail : MediaSourceItem { public static int64 filename_ascending_comparator(void *a, void *b) { string path_a = ((Thumbnail *) a)->media.get_file().get_basename().down(); string path_b = ((Thumbnail *) b)->media.get_file().get_basename().down(); + if (!path_a.validate()) { + path_a = Uri.escape_string(path_a, Uri.RESERVED_CHARS_ALLOWED_IN_PATH, true); + } + + if (!path_b.validate()) { + path_b = Uri.escape_string(path_b, Uri.RESERVED_CHARS_ALLOWED_IN_PATH, true); + } int64 result = strcmp(path_a.collate_key_for_filename(), path_b.collate_key_for_filename()); return (result != 0) ? result : photo_id_ascending_comparator(a, b); diff --git a/src/ThumbnailCache.vala b/src/ThumbnailCache.vala index 5585708..2664cf3 100644 --- a/src/ThumbnailCache.vala +++ b/src/ThumbnailCache.vala @@ -509,13 +509,26 @@ public class ThumbnailCache : Object { File src_file = get_source_cached_file(src_source); File dest_file = get_cached_file(dest_source.get_representative_id(), src_source.get_preferred_thumbnail_format()); + bool success = false; try { src_file.copy(dest_file, FileCopyFlags.ALL_METADATA | FileCopyFlags.OVERWRITE, null, null); + success = true; } catch (Error err) { - AppWindow.panic("%s".printf(err.message)); + debug("Failed to duplicate thumbnail: %s", err.message); } - + + if (success) { + return; + } + + try { + _import_from_source(dest_source, true); + _import_from_source(src_source, false); + } catch (Error err) { + debug("Failed to duplicate thumbnail: %s", err.message); + } + // Do NOT store in memory cache, for similar reasons as stated in _import(). } diff --git a/src/camera/GPhoto.vala b/src/camera/GPhoto.vala index 702f307..5c0b9a8 100644 --- a/src/camera/GPhoto.vala +++ b/src/camera/GPhoto.vala @@ -92,21 +92,9 @@ namespace GPhoto { } } - // For CameraFileInfoFile, CameraFileInfoPreview, and CameraStorageInformation. See: - // https://bugzilla.gnome.org/show_bug.cgi?id=716252 - // https://bugzilla.redhat.com/show_bug.cgi?id=585676 - // https://sourceforge.net/tracker/?func=detail&aid=3000198&group_id=8874&atid=108874 - public const int MAX_FILENAME_LENGTH = 63; - public const int MAX_BASEDIR_LENGTH = 255; - public bool get_info(Context context, Camera camera, string folder, string filename, out CameraFileInfo info) throws Error { - if (folder.length > MAX_BASEDIR_LENGTH || filename.length > MAX_FILENAME_LENGTH) { - info = {}; - - return false; - } - + Result res = camera.get_file_info(folder, filename, out info, context); if (res != Result.OK) throw new GPhotoError.LIBRARY("[%d] Error retrieving file information for %s/%s: %s", diff --git a/src/camera/ImportPage.vala b/src/camera/ImportPage.vala index a5d3b4e..463317b 100644 --- a/src/camera/ImportPage.vala +++ b/src/camera/ImportPage.vala @@ -1346,10 +1346,7 @@ public class ImportPage : CheckerboardPage { } } - public static string? get_fulldir(GPhoto.Camera camera, string camera_name, int fsid, string folder) { - if (folder.length > GPhoto.MAX_BASEDIR_LENGTH) - return null; - + public static string? get_fulldir(GPhoto.Camera camera, string camera_name, int fsid, string folder) { string basedir = get_fs_basedir(camera, fsid); if (basedir == null) { debug("Unable to find base directory for %s fsid %d", camera_name, fsid); diff --git a/src/db/TagTable.vala b/src/db/TagTable.vala index ce191c1..e8bb701 100644 --- a/src/db/TagTable.vala +++ b/src/db/TagTable.vala @@ -235,7 +235,7 @@ public class TagTable : DatabaseTable { // a typename followed by an identifying number (e.g., "video-022354"). if (token[0].isdigit()) { // this is a legacy entry - result.add(PhotoID.upgrade_photo_id_to_source_id(PhotoID(parse_int64(token, 10)))); + result.add(PhotoID.upgrade_photo_id_to_source_id(PhotoID(int64.parse(token, 10)))); } else if (token[0].isalpha()) { // this is a modern entry result.add(token); diff --git a/src/direct/DirectPhotoPage.vala b/src/direct/DirectPhotoPage.vala index cc7186c..50321e9 100644 --- a/src/direct/DirectPhotoPage.vala +++ b/src/direct/DirectPhotoPage.vala @@ -9,6 +9,7 @@ public class DirectPhotoPage : EditingHostPage { private DirectViewCollection? view_controller = null; private File current_save_dir; private bool drop_if_dirty = false; + private bool in_shutdown = false; public DirectPhotoPage(File file) { base (DirectPhoto.global, file.get_basename()); @@ -319,7 +320,10 @@ public class DirectPhotoPage : EditingHostPage { return true; } + // Check if we can write the target format bool is_writeable = get_photo().get_file_format().can_write(); + + // TODO: Check if we can actually write to the file string save_option = is_writeable ? _("_Save") : _("_Save a Copy"); Gtk.ResponseType response = AppWindow.negate_affirm_cancel_question( @@ -336,6 +340,7 @@ public class DirectPhotoPage : EditingHostPage { on_save_as(); } else if ((response == Gtk.ResponseType.CANCEL) || (response == Gtk.ResponseType.DELETE_EVENT) || (response == Gtk.ResponseType.CLOSE)) { + in_shutdown = false; return false; } @@ -343,6 +348,7 @@ public class DirectPhotoPage : EditingHostPage { } public bool check_quit() { + in_shutdown = true; return check_ok_to_close_photo(get_photo(), false); } @@ -352,8 +358,9 @@ public class DirectPhotoPage : EditingHostPage { private void save(File dest, int scale, ScaleConstraint constraint, Jpeg.Quality quality, PhotoFileFormat format, bool copy_unmodified = false, bool save_metadata = true) { + Scaling scaling = Scaling.for_constraint(constraint, scale, false); - + try { get_photo().export(dest, scaling, quality, format, copy_unmodified, save_metadata); } catch (Error err) { @@ -363,6 +370,8 @@ public class DirectPhotoPage : EditingHostPage { return; } + if (in_shutdown) return; + // Fetch the DirectPhoto and reimport. DirectPhoto photo; DirectPhoto.global.fetch(dest, out photo, true); diff --git a/src/direct/DirectWindow.vala b/src/direct/DirectWindow.vala index 9eec5b1..baf6124 100644 --- a/src/direct/DirectWindow.vala +++ b/src/direct/DirectWindow.vala @@ -66,9 +66,6 @@ public class DirectWindow : AppWindow { } protected override void on_quit() { - if (!get_direct_page().check_quit()) - return; - Config.Facade.get_instance().set_direct_window_state(maximized, dimensions); base.on_quit(); diff --git a/src/folders/FoldersBranch.vala b/src/folders/FoldersBranch.vala index 5de7082..49b2d97 100644 --- a/src/folders/FoldersBranch.vala +++ b/src/folders/FoldersBranch.vala @@ -168,11 +168,15 @@ public class Folders.SidebarEntry : Sidebar.SimplePageEntry, Sidebar.ExpandableE public SidebarEntry(File dir) { this.dir = dir; - collation = dir.get_path().collate_key_for_filename(); + collation = to_string().collate_key_for_filename(); } public override string get_sidebar_name() { - return dir.get_basename(); + try { + return dir.query_info(FileAttribute.STANDARD_DISPLAY_NAME, FileQueryInfoFlags.NONE, null).get_display_name(); + } catch (Error err) { + return this.to_string(); + } } public override string? get_sidebar_icon() { @@ -180,7 +184,7 @@ public class Folders.SidebarEntry : Sidebar.SimplePageEntry, Sidebar.ExpandableE } public override string to_string() { - return dir.get_path(); + return Filename.display_name(dir.get_path()); } public bool expand_on_select() { diff --git a/src/util/string.vala b/src/util/string.vala index 89424d0..976f8ee 100644 --- a/src/util/string.vala +++ b/src/util/string.vala @@ -4,8 +4,6 @@ * (version 2.1 or later). See the COPYING file in this distribution. */ -extern int64 g_ascii_strtoll(string str, out char *endptr, uint num_base); - public const int DEFAULT_USER_TEXT_INPUT_LENGTH = 1024; public inline bool is_string_empty(string? s) { @@ -173,10 +171,6 @@ public string? prepare_input_text(string? text, PrepareInputTextOptions options, return prepped; } -public int64 parse_int64(string str, int num_base) { - return g_ascii_strtoll(str, null, num_base); -} - namespace String { public inline bool contains_char(string haystack, unichar needle) { -- cgit v1.2.3