From ee23f4233cb51c98db7b74d52f8ba120fcdddd55 Mon Sep 17 00:00:00 2001 From: ItsZariep Date: Sat, 10 Jan 2026 02:30:42 -0600 Subject: [PATCH] migrate export-to-folder and slideshowshuffle to C --- plugins/export-to-folder/export-to-folder.c | 355 ++++++++++++++++++ .../export-to-folder.plugin.desktop.in | 10 +- plugins/export-to-folder/export-to-folder.py | 111 ------ plugins/export-to-folder/meson.build | 21 +- .../export-to-folder/preferences_dialog.ui | 41 -- plugins/slideshowshuffle/meson.build | 18 +- plugins/slideshowshuffle/slideshowshuffle.c | 315 ++++++++++++++++ .../slideshowshuffle.plugin.desktop.in | 9 +- plugins/slideshowshuffle/slideshowshuffle.py | 95 ----- 9 files changed, 707 insertions(+), 268 deletions(-) create mode 100644 plugins/export-to-folder/export-to-folder.c delete mode 100644 plugins/export-to-folder/export-to-folder.py delete mode 100644 plugins/export-to-folder/preferences_dialog.ui create mode 100644 plugins/slideshowshuffle/slideshowshuffle.c delete mode 100644 plugins/slideshowshuffle/slideshowshuffle.py diff --git a/plugins/export-to-folder/export-to-folder.c b/plugins/export-to-folder/export-to-folder.c new file mode 100644 index 0000000..2277660 --- /dev/null +++ b/plugins/export-to-folder/export-to-folder.c @@ -0,0 +1,355 @@ + /* export-to-folder.c -- export plugin for xviewer + * + * Copyright (c) 2012 Jendrik Seipp (jendrikseipp@web.de) + * C port Copyright (c) 2026 ItsZariep + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define BASE_KEY "org.x.viewer.plugins.export-to-folder" + +static const gchar *ui_str = +"" +" " +" " +" " +" " +" " +" " +" " +""; + +/* ExportPlugin type */ +typedef struct _ExportPlugin ExportPlugin; +typedef struct _ExportPluginClass ExportPluginClass; + +struct _ExportPlugin +{ + GObject parent_instance; + + XviewerWindow *window; + GtkActionGroup *action_group; + guint ui_id; + GSettings *settings; +}; + +struct _ExportPluginClass +{ + GObjectClass parent_class; +}; + +GType export_plugin_get_type (void) G_GNUC_CONST; + +#define EXPORT_TYPE_PLUGIN (export_plugin_get_type()) +#define EXPORT_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EXPORT_TYPE_PLUGIN, ExportPlugin)) + +static void xviewer_window_activatable_iface_init (XviewerWindowActivatableInterface *iface); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (ExportPlugin, export_plugin, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE_DYNAMIC (XVIEWER_TYPE_WINDOW_ACTIVATABLE, + xviewer_window_activatable_iface_init)) + +/* Helper functions */ +static gchar * +get_default_export_dir (void) +{ + const gchar *home = g_get_home_dir(); + return g_build_filename(home, "exported-images", NULL); +} + +static gchar * +get_export_dir (ExportPlugin *plugin) +{ + gchar *target_dir = g_settings_get_string(plugin->settings, "target-folder"); + + if (target_dir == NULL || target_dir[0] == '\0') + { + g_free(target_dir); + return get_default_export_dir(); + } + + return target_dir; +} + +static gboolean +create_directory_if_needed (const gchar *path) +{ + if (g_file_test(path, G_FILE_TEST_EXISTS)) + { + return TRUE; + } + + if (g_mkdir_with_parents(path, 0755) != 0) + { + g_warning("Failed to create directory %s: %s", path, g_strerror(errno)); + return FALSE; + } + + return TRUE; +} + +static void +export_cb (GtkAction *action, ExportPlugin *plugin) +{ + XviewerImage *image; + GFile *file; + gchar *src, *name, *export_dir, *dest; + GError *error = NULL; + GtkWidget *dialog; + gint response; + + /* Get path to current image */ + image = xviewer_window_get_image(plugin->window); + if (!image) + { + g_print("No image can be exported\n"); + return; + } + + file = xviewer_image_get_file(image); + if (!file) + { + g_print("No image file\n"); + return; + } + + src = g_file_get_path(file); + if (!src) + { + g_object_unref(file); + return; + } + + name = g_path_get_basename(src); + + /* Create file chooser dialog */ + dialog = gtk_file_chooser_dialog_new(_("Export Image"), + GTK_WINDOW(plugin->window), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Export"), GTK_RESPONSE_ACCEPT, + NULL); + + /* Set default folder from settings */ + export_dir = get_export_dir(plugin); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), export_dir); + g_free(export_dir); + + /* Show dialog and get response */ + response = gtk_dialog_run(GTK_DIALOG(dialog)); + + if (response == GTK_RESPONSE_ACCEPT) + { + export_dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog)); + + if (export_dir) + { + dest = g_build_filename(export_dir, name, NULL); + + /* Create directory if it doesn't exist */ + create_directory_if_needed(export_dir); + + /* Copy file */ + GFile *src_file = g_file_new_for_path(src); + GFile *dest_file = g_file_new_for_path(dest); + + if (g_file_copy(src_file, dest_file, G_FILE_COPY_OVERWRITE, + NULL, NULL, NULL, &error)) + { + g_print("Copied %s into %s\n", name, export_dir); + + /* Update settings with last used folder */ + g_settings_set_string(plugin->settings, "target-folder", export_dir); + } + else + { + g_warning("Failed to copy file: %s", error->message); + g_error_free(error); + } + + g_object_unref(src_file); + g_object_unref(dest_file); + g_free(dest); + g_free(export_dir); + } + } + + gtk_widget_destroy(dialog); + g_object_unref(file); + g_free(src); + g_free(name); +} + +/* ExportPlugin implementation */ +enum +{ + PROP_0, + PROP_WINDOW +}; + +static void +export_plugin_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + ExportPlugin *plugin = EXPORT_PLUGIN(object); + + switch (prop_id) + { + case PROP_WINDOW: + plugin->window = XVIEWER_WINDOW(g_value_dup_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +export_plugin_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + ExportPlugin *plugin = EXPORT_PLUGIN(object); + + switch (prop_id) + { + case PROP_WINDOW: + g_value_set_object(value, plugin->window); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +export_plugin_init (ExportPlugin *plugin) +{ + plugin->settings = g_settings_new(BASE_KEY); +} + +static void +export_plugin_dispose (GObject *object) +{ + ExportPlugin *plugin = EXPORT_PLUGIN(object); + + if (plugin->window) + { + g_object_unref(plugin->window); + plugin->window = NULL; + } + + if (plugin->settings) + { + g_object_unref(plugin->settings); + plugin->settings = NULL; + } + + G_OBJECT_CLASS(export_plugin_parent_class)->dispose(object); +} + +static void +export_plugin_activate (XviewerWindowActivatable *activatable) +{ + ExportPlugin *plugin = EXPORT_PLUGIN(activatable); + GtkUIManager *ui_manager; + GError *error = NULL; + + static const GtkActionEntry action_entries[] = + { + { "Export", NULL, N_("_Export"), "E", NULL, G_CALLBACK(export_cb) } + }; + + ui_manager = xviewer_window_get_ui_manager(plugin->window); + + plugin->action_group = gtk_action_group_new("Export"); + gtk_action_group_set_translation_domain(plugin->action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions(plugin->action_group, action_entries, + G_N_ELEMENTS(action_entries), plugin); + + gtk_ui_manager_insert_action_group(ui_manager, plugin->action_group, 0); + + plugin->ui_id = gtk_ui_manager_add_ui_from_string(ui_manager, ui_str, -1, &error); + if (error) + { + g_warning("Failed to add UI: %s", error->message); + g_error_free(error); + } +} + +static void +export_plugin_deactivate (XviewerWindowActivatable *activatable) +{ + ExportPlugin *plugin = EXPORT_PLUGIN(activatable); + GtkUIManager *ui_manager; + + ui_manager = xviewer_window_get_ui_manager(plugin->window); + + gtk_ui_manager_remove_ui(ui_manager, plugin->ui_id); + gtk_ui_manager_remove_action_group(ui_manager, plugin->action_group); + + g_object_unref(plugin->action_group); + plugin->action_group = NULL; +} + +static void +export_plugin_class_init (ExportPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = export_plugin_dispose; + object_class->set_property = export_plugin_set_property; + object_class->get_property = export_plugin_get_property; + + g_object_class_override_property(object_class, PROP_WINDOW, "window"); +} + +static void +export_plugin_class_finalize (ExportPluginClass *klass) +{ + /* dummy function used by G_DEFINE_DYNAMIC_TYPE */ +} + +static void +xviewer_window_activatable_iface_init (XviewerWindowActivatableInterface *iface) +{ + iface->activate = export_plugin_activate; + iface->deactivate = export_plugin_deactivate; +} + +/* Module registration */ +G_MODULE_EXPORT void +peas_register_types (PeasObjectModule *module) +{ + export_plugin_register_type(G_TYPE_MODULE(module)); + + peas_object_module_register_extension_type(module, + XVIEWER_TYPE_WINDOW_ACTIVATABLE, + EXPORT_TYPE_PLUGIN); +} \ No newline at end of file diff --git a/plugins/export-to-folder/export-to-folder.plugin.desktop.in b/plugins/export-to-folder/export-to-folder.plugin.desktop.in index d57a1c9..cb3330f 100644 --- a/plugins/export-to-folder/export-to-folder.plugin.desktop.in +++ b/plugins/export-to-folder/export-to-folder.plugin.desktop.in @@ -1,10 +1,10 @@ [Plugin] -Loader=python3 +Loader=C Module=export-to-folder -IAge=2 +IAge=3 Name=Export to Folder Icon=xsi-folder-symbolic Description=Export the current image to a separate directory -Authors=Jendrik Seipp -Copyright=Copyright © 2012 Jendrik Seipp -Website=https://bitbucket.org/jendrikseipp/xviewer-export +Authors=Jendrik Seipp , ItsZariep +Copyright=Copyright © 2012 Jendrik Seipp, © 2026 ItsZariep +Website=https://github.com/linuxmint/xviewer-plugins diff --git a/plugins/export-to-folder/export-to-folder.py b/plugins/export-to-folder/export-to-folder.py deleted file mode 100644 index 803119d..0000000 --- a/plugins/export-to-folder/export-to-folder.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- -# -# export.py -- export plugin for xviewer -# -# Copyright (c) 2012 Jendrik Seipp (jendrikseipp@web.de) -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -import os -import shutil - -from gi.repository import GObject, Xviewer, Gio, Gtk, PeasGtk - - -ui_str = """ - - - - - - - - - -""" - -EXPORT_DIR = os.path.join(os.path.expanduser('~'), 'exported-images') -BASE_KEY = 'org.x.viewer.plugins.export-to-folder' - -class ExportPlugin(GObject.Object, Xviewer.WindowActivatable): - window = GObject.property(type=Xviewer.Window) - - def __init__(self): - GObject.Object.__init__(self) - self.settings = Gio.Settings.new(BASE_KEY) - - @property - def export_dir(self): - target_dir = self.settings.get_string('target-folder') - if target_dir == "": - return EXPORT_DIR - - return target_dir - - def do_activate(self): - ui_manager = self.window.get_ui_manager() - self.action_group = Gtk.ActionGroup(name='Export') - self.action_group.add_actions([('Export', None, - _('_Export'), "E", None, self.export_cb)], self.window) - ui_manager.insert_action_group(self.action_group, 0) - self.ui_id = ui_manager.add_ui_from_string(ui_str) - - def do_deactivate(self): - ui_manager = self.window.get_ui_manager().remove_ui(self.ui_id); - - def export_cb(self, action, window): - # Get path to current image. - image = window.get_image() - if not image: - print('No image can be exported') - return - src = image.get_file().get_path() - name = os.path.basename(src) - dest = os.path.join(self.export_dir, name) - # Create directory if it doesn't exist. - try: - os.makedirs(self.export_dir) - except OSError: - pass - shutil.copy2(src, dest) - print('Copied %s into %s' % (name, self.export_dir)) - - -class ExportConfigurable(GObject.Object, PeasGtk.Configurable): - - def __init__(self): - GObject.Object.__init__(self) - self.settings = Gio.Settings.new(BASE_KEY) - - def do_create_configure_widget(self): - # Create preference dialog - signals = {'current_folder_changed_cb': self.current_folder_changed_cb} - builder = Gtk.Builder() - builder.set_translation_domain('xviewer-plugins') - builder.add_from_file(os.path.join(self.plugin_info.get_data_dir(), - 'preferences_dialog.ui')) - builder.connect_signals(signals) - - self.export_dir_button = builder.get_object('export_dir_button') - self.preferences_dialog = builder.get_object('preferences_box') - target_dir = self.settings.get_string('target-folder') - if target_dir == "": - target_dir = EXPORT_DIR; - self.export_dir_button.set_current_folder(target_dir) - - return self.preferences_dialog - - def current_folder_changed_cb(self, button): - self.settings.set_string('target-folder', button.get_current_folder()) diff --git a/plugins/export-to-folder/meson.build b/plugins/export-to-folder/meson.build index 7bd9f8e..318da02 100644 --- a/plugins/export-to-folder/meson.build +++ b/plugins/export-to-folder/meson.build @@ -1,5 +1,6 @@ plugin_name = 'export-to-folder' +# Plugin info i18n.merge_file( input: '@0@.plugin.desktop.in'.format(plugin_name), output: '@0@.plugin'.format(plugin_name), @@ -26,12 +27,16 @@ configure_file( install_dir: gschemas_dir ) -install_data( - 'preferences_dialog.ui', - install_dir: plugin_datadir / plugin_name -) +# Plugin sources +plugin_export_sources = [ + 'export-to-folder.c' +] -install_data( - 'export-to-folder.py', - install_dir: plugin_libdir -) +# Build shared library +shared_library( + plugin_name, + plugin_export_sources, + dependencies: [config_h, glib, gtk, libpeas, xviewer], + install: true, + install_dir: plugin_libdir, +) \ No newline at end of file diff --git a/plugins/export-to-folder/preferences_dialog.ui b/plugins/export-to-folder/preferences_dialog.ui deleted file mode 100644 index a94d71a..0000000 --- a/plugins/export-to-folder/preferences_dialog.ui +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - True - False - start - start - 10 - 10 - 10 - 10 - 10 - - - True - False - Export directory: - - - False - True - 0 - - - - - True - False - select-folder - - - - False - True - 1 - - - - diff --git a/plugins/slideshowshuffle/meson.build b/plugins/slideshowshuffle/meson.build index b65321c..8ee001e 100644 --- a/plugins/slideshowshuffle/meson.build +++ b/plugins/slideshowshuffle/meson.build @@ -1,5 +1,6 @@ plugin_name = 'slideshowshuffle' +# Plugin info i18n.merge_file( input: '@0@.plugin.desktop.in'.format(plugin_name), output: '@0@.plugin'.format(plugin_name), @@ -19,7 +20,16 @@ i18n.merge_file( install_dir: metainfo_dir ) -install_data( - 'slideshowshuffle.py', - install_dir: plugin_libdir -) +# Plugin sources +plugin_slideshow_sources = [ + 'slideshowshuffle.c' +] + +# Build shared library +shared_library( + plugin_name, + plugin_slideshow_sources, + dependencies: [config_h, glib, gtk, libpeas, xviewer], + install: true, + install_dir: plugin_libdir, +) \ No newline at end of file diff --git a/plugins/slideshowshuffle/slideshowshuffle.c b/plugins/slideshowshuffle/slideshowshuffle.c new file mode 100644 index 0000000..2bc3011 --- /dev/null +++ b/plugins/slideshowshuffle/slideshowshuffle.c @@ -0,0 +1,315 @@ + /* Slideshow Shuffle Plugin for xviewer + * + * Copyright (c) 2008 Johannes Marbach + * C port Copyright (c) 2026 ItsZariep + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _SlideshowShufflePlugin SlideshowShufflePlugin; +typedef struct _SlideshowShufflePluginClass SlideshowShufflePluginClass; + +struct _SlideshowShufflePlugin +{ + GObject parent_instance; + XviewerWindow *window; + gulong state_handler_id; + gboolean slideshow; + GHashTable *map; +}; + +struct _SlideshowShufflePluginClass +{ + GObjectClass parent_class; +}; + +GType slideshow_shuffle_plugin_get_type (void) G_GNUC_CONST; + +#define SLIDESHOW_SHUFFLE_TYPE_PLUGIN (slideshow_shuffle_plugin_get_type()) +#define SLIDESHOW_SHUFFLE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SLIDESHOW_SHUFFLE_TYPE_PLUGIN, SlideshowShufflePlugin)) + +static void xviewer_window_activatable_iface_init (XviewerWindowActivatableInterface *iface); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (SlideshowShufflePlugin, slideshow_shuffle_plugin, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE_DYNAMIC (XVIEWER_TYPE_WINDOW_ACTIVATABLE, + xviewer_window_activatable_iface_init)) + +static gint +alphabetic_sort_function (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data) +{ + XviewerImage *img1, *img2; + gtk_tree_model_get(model, a, XVIEWER_LIST_STORE_XVIEWER_IMAGE, &img1, -1); + gtk_tree_model_get(model, b, XVIEWER_LIST_STORE_XVIEWER_IMAGE, &img2, -1); + const char *uri1 = xviewer_image_get_uri_for_display(img1); + const char *uri2 = xviewer_image_get_uri_for_display(img2); + return g_strcmp0(uri1, uri2); +} + +static gint +random_sort_function (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data) +{ + SlideshowShufflePlugin *plugin = SLIDESHOW_SHUFFLE_PLUGIN(data); + XviewerImage *img1, *img2; + gtk_tree_model_get(model, a, XVIEWER_LIST_STORE_XVIEWER_IMAGE, &img1, -1); + gtk_tree_model_get(model, b, XVIEWER_LIST_STORE_XVIEWER_IMAGE, &img2, -1); + const char *uri1 = xviewer_image_get_uri_for_display(img1); + const char *uri2 = xviewer_image_get_uri_for_display(img2); + gint pos1 = GPOINTER_TO_INT(g_hash_table_lookup(plugin->map, uri1)); + gint pos2 = GPOINTER_TO_INT(g_hash_table_lookup(plugin->map, uri2)); + if (pos1 > pos2) return 1; + if (pos1 < pos2) return -1; + return 0; +} + +static void +state_changed_cb (GtkWidget *widget, GdkEventWindowState *event, gpointer user_data) +{ + SlideshowShufflePlugin *plugin = SLIDESHOW_SHUFFLE_PLUGIN(user_data); + XviewerWindow *window = XVIEWER_WINDOW(widget); + XviewerWindowMode mode = xviewer_window_get_mode(window); + + if (mode == XVIEWER_WINDOW_MODE_SLIDESHOW && !plugin->slideshow) + { + /* Slideshow starts */ + plugin->slideshow = TRUE; + XviewerListStore *store = xviewer_window_get_store(window); + if (!store) return; + + XviewerImage *current_image = xviewer_window_get_image(window); + gint pos = 0; + if (current_image) + { + pos = xviewer_list_store_get_pos_by_image(store, current_image); + } + + /* Generate URI list and remove current */ + GList *uri_list = NULL; + const char *current_uri = NULL; + if (current_image) + { + current_uri = xviewer_image_get_uri_for_display(current_image); + } + + GtkTreeIter iter; + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) + { + do + { + XviewerImage *img; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, XVIEWER_LIST_STORE_XVIEWER_IMAGE, &img, -1); + const char *uri = xviewer_image_get_uri_for_display(img); + if (!current_uri || g_strcmp0(uri, current_uri) != 0) + { + uri_list = g_list_append(uri_list, (gpointer)uri); + } + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + } + + plugin->map = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(plugin->map, (gpointer)current_uri, GINT_TO_POINTER(0)); + + /* Generate random positions for remaining URIs */ + GList *supply = NULL; + for (gint i = 1; i <= g_list_length(uri_list); i++) + { + supply = g_list_append(supply, GINT_TO_POINTER(i)); + } + + GList *remaining = g_list_copy(uri_list); + for (GList *l = remaining; l; l = l->next) + { + gint idx = g_random_int_range(0, g_list_length(supply)); + gint rand_pos = GPOINTER_TO_INT(g_list_nth_data(supply, idx)); + supply = g_list_remove(supply, GINT_TO_POINTER(rand_pos)); + g_hash_table_insert(plugin->map, l->data, GINT_TO_POINTER(rand_pos)); + } + g_list_free(uri_list); + g_list_free(remaining); + g_list_free(supply); + + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), random_sort_function, plugin, NULL); + } + else if (mode == XVIEWER_WINDOW_MODE_NORMAL && plugin->slideshow) + { + /* Slideshow ends */ + plugin->slideshow = FALSE; + XviewerListStore *store = xviewer_window_get_store(window); + if (store) + { + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), alphabetic_sort_function, NULL, NULL); + } + if (plugin->map) + { + g_hash_table_destroy(plugin->map); + plugin->map = NULL; + } + } +} + +/* Property handling */ +enum +{ + PROP_0, + PROP_WINDOW +}; + +static void +slideshow_shuffle_plugin_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + SlideshowShufflePlugin *plugin = SLIDESHOW_SHUFFLE_PLUGIN(object); + + switch (prop_id) + { + case PROP_WINDOW: + plugin->window = XVIEWER_WINDOW(g_value_dup_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void +slideshow_shuffle_plugin_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + SlideshowShufflePlugin *plugin = SLIDESHOW_SHUFFLE_PLUGIN(object); + + switch (prop_id) + { + case PROP_WINDOW: + g_value_set_object(value, plugin->window); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void +slideshow_shuffle_plugin_init (SlideshowShufflePlugin *plugin) +{ + plugin->state_handler_id = 0; + plugin->slideshow = FALSE; + plugin->map = NULL; +} + +static void +slideshow_shuffle_plugin_dispose (GObject *object) +{ + SlideshowShufflePlugin *plugin = SLIDESHOW_SHUFFLE_PLUGIN(object); + + if (plugin->state_handler_id != 0 && plugin->window) + { + g_signal_handler_disconnect(plugin->window, plugin->state_handler_id); + plugin->state_handler_id = 0; + } + + if (plugin->map) + { + g_hash_table_destroy(plugin->map); + plugin->map = NULL; + } + + if (plugin->window) + { + g_object_unref(plugin->window); + plugin->window = NULL; + } + + G_OBJECT_CLASS(slideshow_shuffle_plugin_parent_class)->dispose(object); +} + +static void +slideshow_shuffle_plugin_activate (XviewerWindowActivatable *activatable) +{ + SlideshowShufflePlugin *plugin = SLIDESHOW_SHUFFLE_PLUGIN(activatable); + + /* Initialize random seed */ + g_random_set_seed(time(NULL)); + + /* Connect to window state events */ + plugin->state_handler_id = g_signal_connect(plugin->window, + "window-state-event", + G_CALLBACK(state_changed_cb), + plugin); +} + +static void +slideshow_shuffle_plugin_deactivate (XviewerWindowActivatable *activatable) +{ + SlideshowShufflePlugin *plugin = SLIDESHOW_SHUFFLE_PLUGIN(activatable); + + if (plugin->state_handler_id != 0) + { + g_signal_handler_disconnect(plugin->window, plugin->state_handler_id); + plugin->state_handler_id = 0; + } + + if (plugin->map) + { + g_hash_table_destroy(plugin->map); + plugin->map = NULL; + } +} + +static void +slideshow_shuffle_plugin_class_init (SlideshowShufflePluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = slideshow_shuffle_plugin_dispose; + object_class->set_property = slideshow_shuffle_plugin_set_property; + object_class->get_property = slideshow_shuffle_plugin_get_property; + + g_object_class_override_property(object_class, PROP_WINDOW, "window"); +} + +static void +slideshow_shuffle_plugin_class_finalize (SlideshowShufflePluginClass *klass) +{ +} + +static void +xviewer_window_activatable_iface_init (XviewerWindowActivatableInterface *iface) +{ + iface->activate = slideshow_shuffle_plugin_activate; + iface->deactivate = slideshow_shuffle_plugin_deactivate; +} + +G_MODULE_EXPORT void +peas_register_types (PeasObjectModule *module) +{ + slideshow_shuffle_plugin_register_type(G_TYPE_MODULE(module)); + + peas_object_module_register_extension_type(module, + XVIEWER_TYPE_WINDOW_ACTIVATABLE, + SLIDESHOW_SHUFFLE_TYPE_PLUGIN); +} \ No newline at end of file diff --git a/plugins/slideshowshuffle/slideshowshuffle.plugin.desktop.in b/plugins/slideshowshuffle/slideshowshuffle.plugin.desktop.in index e11f291..4ca0970 100644 --- a/plugins/slideshowshuffle/slideshowshuffle.plugin.desktop.in +++ b/plugins/slideshowshuffle/slideshowshuffle.plugin.desktop.in @@ -1,9 +1,10 @@ [Plugin] -Loader=python3 +Loader=C Module=slideshowshuffle -IAge=2 +IAge=3 Name=Slideshow Shuffle Icon=xsi-media-playlist-shuffle-symbolic Description=Shuffles images in slideshow mode -Authors=Johannes Marbach -Copyright=Copyright © 2008 Johannes Marbach +Authors=Johannes Marbach , ItsZariep +Copyright=Copyright © 2008 Johannes Marbach, © 2026 ItsZariep +Website=https://github.com/linuxmint/xviewer-plugins diff --git a/plugins/slideshowshuffle/slideshowshuffle.py b/plugins/slideshowshuffle/slideshowshuffle.py deleted file mode 100644 index 7932db7..0000000 --- a/plugins/slideshowshuffle/slideshowshuffle.py +++ /dev/null @@ -1,95 +0,0 @@ -# Slideshow Shuffle Plugin for xviewer -# Copyright (C) 2008 Johannes Marbach -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 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. - -from gi.repository import GObject, Gtk, Xviewer -import random - -class SlideshowShufflePlugin(GObject.Object, Xviewer.WindowActivatable): - - # Override XviewerWindowActivatable's window property - window = GObject.property(type=Xviewer.Window) - - def __init__(self): - GObject.Object.__init__(self) - - def do_activate(self): - random.seed() - self.slideshow = False - self.state_handler_id = \ - self.window.connect('window-state-event', self.state_changed_cb, self) - - def do_deactivate(self): - self.window.disconnect(self.state_handler_id) - - # The callback functions are done statically to avoid causing additional - # references on the window property causing xviewer to not quit correctly. - @staticmethod - def state_changed_cb(window, event, self): - mode = self.window.get_mode() - - if mode == Xviewer.WindowMode.SLIDESHOW and not self.slideshow: - # Slideshow starts - self.slideshow = True - - # Query position of current image - if not window.get_image(): - pos = 0 - else: - pos = window.get_store().get_pos_by_image(window.get_image()) - - # Generate random map - uri_list = [row[2].get_uri_for_display() \ - for row in window.get_store()] - self.map = {uri_list.pop(pos): 0} - supply = list(range(1, len(uri_list) + 1)) - for uri in uri_list: - self.map[uri] = supply.pop(random.randint(0, len(supply) - 1)) - - # Put random sort function in place - self.window.get_store().\ - set_default_sort_func(self.random_sort_function, self) - elif mode == Xviewer.WindowMode.NORMAL and self.slideshow: - # Slideshow ends - self.slideshow = False - - # Put alphabetic sort function in place - self.window.get_store().\ - set_default_sort_func(self.alphabetic_sort_function) - - @staticmethod - def random_sort_function(store, iter1, iter2, self): - pos1 = self.map[store[iter1][2].get_uri_for_display()] - pos2 = self.map[store[iter2][2].get_uri_for_display()] - - if pos1 > pos2: - return 1 - elif pos1 < pos2: - return -1 - else: - return 0 - - @staticmethod - def alphabetic_sort_function(store, iter1, iter2, data = None): - uri1 = store[iter1][2].get_uri_for_display().lower() - uri2 = store[iter2][2].get_uri_for_display().lower() - - if uri1 > uri2: - return 1 - elif uri1 < uri2: - return -1 - else: - return 0