diff --git a/plug-ins/common/.gitignore b/plug-ins/common/.gitignore index 99a4684ee5..eb8b65f141 100644 --- a/plug-ins/common/.gitignore +++ b/plug-ins/common/.gitignore @@ -14,6 +14,8 @@ /blur.exe /border-average /border-average.exe +/busy-dialog +/busy-dialog.exe /cartoon /cartoon.exe /checkerboard diff --git a/plug-ins/common/Makefile.am b/plug-ins/common/Makefile.am index eccf0e9c35..2e1927c2eb 100644 --- a/plug-ins/common/Makefile.am +++ b/plug-ins/common/Makefile.am @@ -52,6 +52,7 @@ animation_optimize_libexecdir = $(gimpplugindir)/plug-ins/animation-optimize blinds_libexecdir = $(gimpplugindir)/plug-ins/blinds blur_libexecdir = $(gimpplugindir)/plug-ins/blur border_average_libexecdir = $(gimpplugindir)/plug-ins/border-average +busy_dialog_libexecdir = $(gimpplugindir)/plug-ins/busy-dialog cartoon_libexecdir = $(gimpplugindir)/plug-ins/cartoon checkerboard_libexecdir = $(gimpplugindir)/plug-ins/checkerboard cml_explorer_libexecdir = $(gimpplugindir)/plug-ins/cml-explorer @@ -146,6 +147,7 @@ animation_optimize_libexec_PROGRAMS = animation-optimize blinds_libexec_PROGRAMS = blinds blur_libexec_PROGRAMS = blur border_average_libexec_PROGRAMS = border-average +busy_dialog_libexec_PROGRAMS = busy-dialog cartoon_libexec_PROGRAMS = cartoon checkerboard_libexec_PROGRAMS = checkerboard cml_explorer_libexec_PROGRAMS = cml-explorer @@ -359,6 +361,24 @@ border_average_LDADD = \ $(INTLLIBS) \ $(border_average_RC) +busy_dialog_SOURCES = \ + busy-dialog.c + +busy_dialog_LDADD = \ + $(libgimpui) \ + $(libgimpwidgets) \ + $(libgimpmodule) \ + $(libgimp) \ + $(libgimpmath) \ + $(libgimpconfig) \ + $(libgimpcolor) \ + $(libgimpbase) \ + $(GTK_LIBS) \ + $(GEGL_LIBS) \ + $(RT_LIBS) \ + $(INTLLIBS) \ + $(bust_dialog_RC) + cartoon_SOURCES = \ cartoon.c diff --git a/plug-ins/common/busy-dialog.c b/plug-ins/common/busy-dialog.c new file mode 100644 index 0000000000..0e0d58dd26 --- /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 . + */ + +#include "config.h" + +#include +#include + +#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; + } +} diff --git a/plug-ins/common/gimprc.common b/plug-ins/common/gimprc.common index b6aa42284d..ba79e2d92f 100644 --- a/plug-ins/common/gimprc.common +++ b/plug-ins/common/gimprc.common @@ -4,6 +4,7 @@ animation_play_RC = animation-play.rc.o blinds_RC = blinds.rc.o blur_RC = blur.rc.o border_average_RC = border-average.rc.o +busy_dialog_RC = bust-dialog.rc.o cartoon_RC = cartoon.rc.o checkerboard_RC = checkerboard.rc.o cml_explorer_RC = cml-explorer.rc.o diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in index 624660efd4..c497d3368e 100644 --- a/po-plug-ins/POTFILES.in +++ b/po-plug-ins/POTFILES.in @@ -9,6 +9,7 @@ plug-ins/common/animation-play.c plug-ins/common/blinds.c plug-ins/common/blur.c plug-ins/common/border-average.c +plug-ins/common/busy-dialog.c plug-ins/common/cartoon.c plug-ins/common/checkerboard.c plug-ins/common/cml-explorer.c