diff options
Diffstat (limited to 'plug-ins/common/busy-dialog.c')
-rw-r--r-- | plug-ins/common/busy-dialog.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/plug-ins/common/busy-dialog.c b/plug-ins/common/busy-dialog.c new file mode 100644 index 0000000..ac16792 --- /dev/null +++ b/plug-ins/common/busy-dialog.c @@ -0,0 +1,309 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * busy-dialog.c + * Copyright (C) 2018 Ell + * + * 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 3 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, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include "libgimp/stdplugins-intl.h" + + +#define PLUG_IN_PROC "plug-in-busy-dialog" +#define PLUG_IN_BINARY "busy-dialog" +#define PLUG_IN_ROLE "gimp-busy-dialog" + + +typedef struct +{ + GIOChannel *read_channel; + GIOChannel *write_channel; +} Context; + + +static void query (void); +static void run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals); + +static GimpPDBStatusType busy_dialog (gint read_fd, + gint write_fd, + const gchar *message, + gboolean cancelable); + +static gboolean busy_dialog_read_channel_notify (GIOChannel *source, + GIOCondition condition, + Context *context); + +static gboolean busy_dialog_delete_event (GtkDialog *dialog, + GdkEvent *event, + Context *context); +static void busy_dialog_response (GtkDialog *dialog, + gint response_id, + Context *context); + + +const GimpPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + + +MAIN () + + +static void +query (void) +{ + static const GimpParamDef args [] = + { + { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" }, + { GIMP_PDB_INT32, "read-fd", "The read file descriptor" }, + { GIMP_PDB_INT32, "write-fd", "The write file descriptor" }, + { GIMP_PDB_STRING, "message", "The message" }, + { GIMP_PDB_INT32, "cancelable", "Whether the dialog is cancelable (TRUE or FALSE)" } + }; + + gimp_install_procedure (PLUG_IN_PROC, + "Show a dialog while waiting for an operation to finish", + "Used by GIMP to display a dialog, containing a " + "spinner and a custom message, while waiting for an " + "ongoing operation to finish. Optionally, the dialog " + "may provide a \"Cancel\" button, which can be used " + "to cancel the operation.", + "Ell", + "Ell", + "2018", + NULL, + "", + GIMP_PLUGIN, + G_N_ELEMENTS (args), 0, + args, NULL); +} + +static void +run (const gchar *name, + gint n_params, + const GimpParam *params, + gint *n_return_vals, + GimpParam **return_vals) +{ + GimpRunMode run_mode = params[0].data.d_int32; + GimpPDBStatusType status = GIMP_PDB_SUCCESS; + + static GimpParam values[1]; + + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = status; + + *n_return_vals = 1; + *return_vals = values; + + INIT_I18N (); + + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + case GIMP_RUN_NONINTERACTIVE: + case GIMP_RUN_WITH_LAST_VALS: + if (n_params != 5) + { + status = GIMP_PDB_CALLING_ERROR; + } + else + { + status = busy_dialog (params[1].data.d_int32, + params[2].data.d_int32, + params[3].data.d_string, + params[4].data.d_int32); + } + break; + + default: + status = GIMP_PDB_CALLING_ERROR; + break; + } + + values[0].data.d_status = status; +} + +static GimpPDBStatusType +busy_dialog (gint read_fd, + gint write_fd, + const gchar *message, + gboolean cancelable) +{ + Context context; + GtkWidget *window; + GtkWidget *content_area; + GtkWidget *vbox; + GtkWidget *label; + GtkWidget *box; + +#ifdef G_OS_WIN32 + context.read_channel = g_io_channel_win32_new_fd (read_fd); + context.write_channel = g_io_channel_win32_new_fd (write_fd); +#else + context.read_channel = g_io_channel_unix_new (read_fd); + context.write_channel = g_io_channel_unix_new (write_fd); +#endif + + g_io_channel_set_close_on_unref (context.read_channel, TRUE); + g_io_channel_set_close_on_unref (context.write_channel, TRUE); + + /* triggered when the operation is finished in the main app, and we should + * quit. + */ + g_io_add_watch (context.read_channel, G_IO_IN | G_IO_ERR | G_IO_HUP, + (GIOFunc) busy_dialog_read_channel_notify, + &context); + + /* call gtk_init() before gimp_ui_init(), to avoid DESKTOP_STARTUP_ID from + * taking effect -- we want the dialog to be prominently displayed above + * other plug-in windows. + */ + gtk_init (NULL, NULL); + + gimp_ui_init (PLUG_IN_BINARY, FALSE); + + /* the main window */ + if (! cancelable) + { + window = g_object_new (GTK_TYPE_WINDOW, + "title", _("Please Wait"), + "skip-taskbar-hint", TRUE, + "deletable", FALSE, + "resizable", FALSE, + "role", "gimp-busy-dialog", + "type-hint", GDK_WINDOW_TYPE_HINT_DIALOG, + "window-position", GTK_WIN_POS_CENTER, + NULL); + + g_signal_connect (window, "delete-event", G_CALLBACK (gtk_true), NULL); + + content_area = window; + } + else + { + window = g_object_new (GTK_TYPE_DIALOG, + "title", _("Please Wait"), + "skip-taskbar-hint", TRUE, + "resizable", FALSE, + "role", "gimp-busy-dialog", + "window-position", GTK_WIN_POS_CENTER, + NULL); + + gtk_dialog_add_button (GTK_DIALOG (window), + _("_Cancel"), GTK_RESPONSE_CANCEL); + + g_signal_connect (window, "delete-event", + G_CALLBACK (busy_dialog_delete_event), + &context); + + g_signal_connect (window, "response", + G_CALLBACK (busy_dialog_response), + &context); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (window)); + } + + /* the main vbox */ + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 16); + gtk_container_add (GTK_CONTAINER (content_area), vbox); + gtk_widget_show (vbox); + + /* the title label */ + label = gtk_label_new (_("Please wait for the operation to complete")); + gimp_label_set_attributes (GTK_LABEL (label), + PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, + -1); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + /* the busy box */ + box = gimp_busy_box_new (message); + gtk_container_set_border_width (GTK_CONTAINER (box), 8); + gtk_box_pack_start (GTK_BOX (vbox), box, TRUE, TRUE, 0); + gtk_widget_show (box); + + gtk_window_present (GTK_WINDOW (window)); + + gtk_main (); + + gtk_widget_destroy (window); + + g_clear_pointer (&context.read_channel, g_io_channel_unref); + g_clear_pointer (&context.write_channel, g_io_channel_unref); + + return GIMP_PDB_SUCCESS; +} + +static gboolean +busy_dialog_read_channel_notify (GIOChannel *source, + GIOCondition condition, + Context *context) +{ + gtk_main_quit (); + + return FALSE; +} + +static gboolean +busy_dialog_delete_event (GtkDialog *dialog, + GdkEvent *event, + Context *context) +{ + gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL); + + return TRUE; +} + +static void +busy_dialog_response (GtkDialog *dialog, + gint response_id, + Context *context) +{ + switch (response_id) + { + case GTK_RESPONSE_CANCEL: + { + GtkWidget *button; + + gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_CANCEL, FALSE); + + button = gtk_dialog_get_widget_for_response (dialog, + GTK_RESPONSE_CANCEL); + gtk_button_set_label (GTK_BUTTON (button), _("Canceling...")); + + /* signal the cancellation request to the main app */ + g_clear_pointer (&context->write_channel, g_io_channel_unref); + } + break; + + default: + break; + } +} |