/* LIBGIMP - The GIMP Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * gimpprogress.c * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include "config.h" #include #include "libgimpbase/gimpbase.h" #undef GIMP_DISABLE_DEPRECATED #include "gimpprogress.h" #define GIMP_DISABLE_DEPRECATED #include "gimp.h" typedef struct { gchar *progress_callback; GimpProgressVtable vtable; gpointer data; } GimpProgressData; /* local function prototypes */ static void gimp_progress_data_free (GimpProgressData *data); static void gimp_temp_progress_run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); /* private variables */ static GHashTable * gimp_progress_ht = NULL; static gdouble gimp_progress_current = 0.0; static const gdouble gimp_progress_step = (1.0 / 256.0); /* public functions */ /** * gimp_progress_install: * @start_callback: the function to call when progress starts * @end_callback: the function to call when progress finishes * @text_callback: the function to call to change the text * @value_callback: the function to call to change the value * @user_data: a pointer that is returned when uninstalling the progress * * Note that since GIMP 2.4, @value_callback can be called with * negative values. This is triggered by calls to gimp_progress_pulse(). * The callback should then implement a progress indicating business, * e.g. by calling gtk_progress_bar_pulse(). * * Return value: the name of the temporary procedure that's been installed * * Since: 2.2 **/ const gchar * gimp_progress_install (GimpProgressStartCallback start_callback, GimpProgressEndCallback end_callback, GimpProgressTextCallback text_callback, GimpProgressValueCallback value_callback, gpointer user_data) { GimpProgressVtable vtable = { 0, }; g_return_val_if_fail (start_callback != NULL, NULL); g_return_val_if_fail (end_callback != NULL, NULL); g_return_val_if_fail (text_callback != NULL, NULL); g_return_val_if_fail (value_callback != NULL, NULL); vtable.start = start_callback; vtable.end = end_callback; vtable.set_text = text_callback; vtable.set_value = value_callback; return gimp_progress_install_vtable (&vtable, user_data); } /** * gimp_progress_install_vtable: * @vtable: a pointer to a @GimpProgressVtable. * @user_data: a pointer that is passed as user_data to all vtable functions. * * Return value: the name of the temporary procedure that's been installed * * Since: 2.4 **/ const gchar * gimp_progress_install_vtable (const GimpProgressVtable *vtable, gpointer user_data) { static const GimpParamDef args[] = { { GIMP_PDB_INT32, "command", "" }, { GIMP_PDB_STRING, "text", "" }, { GIMP_PDB_FLOAT, "value", "" } }; static const GimpParamDef values[] = { { GIMP_PDB_FLOAT, "value", "" } }; gchar *progress_callback; g_return_val_if_fail (vtable != NULL, NULL); g_return_val_if_fail (vtable->start != NULL, NULL); g_return_val_if_fail (vtable->end != NULL, NULL); g_return_val_if_fail (vtable->set_text != NULL, NULL); g_return_val_if_fail (vtable->set_value != NULL, NULL); progress_callback = gimp_procedural_db_temp_name (); gimp_install_temp_proc (progress_callback, "Temporary progress callback procedure", "", "", "", "", NULL, "", GIMP_TEMPORARY, G_N_ELEMENTS (args), G_N_ELEMENTS (values), args, values, gimp_temp_progress_run); if (_gimp_progress_install (progress_callback)) { GimpProgressData *progress_data; gimp_extension_enable (); /* Allow callbacks to be watched */ /* Now add to hash table so we can find it again */ if (! gimp_progress_ht) { gimp_progress_ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gimp_progress_data_free); } progress_data = g_slice_new0 (GimpProgressData); progress_data->progress_callback = progress_callback; progress_data->vtable.start = vtable->start; progress_data->vtable.end = vtable->end; progress_data->vtable.set_text = vtable->set_text; progress_data->vtable.set_value = vtable->set_value; progress_data->vtable.pulse = vtable->pulse; progress_data->vtable.get_window = vtable->get_window; progress_data->data = user_data; g_hash_table_insert (gimp_progress_ht, progress_callback, progress_data); return progress_callback; } gimp_uninstall_temp_proc (progress_callback); g_free (progress_callback); return NULL; } /** * gimp_progress_uninstall: * @progress_callback: the name of the temporary procedure to uninstall * * Uninstalls a temporary progress procedure that was installed using * gimp_progress_install(). * * Return value: the @user_data that was passed to gimp_progress_install(). * * Since: 2.2 **/ gpointer gimp_progress_uninstall (const gchar *progress_callback) { GimpProgressData *progress_data; gpointer user_data; g_return_val_if_fail (progress_callback != NULL, NULL); g_return_val_if_fail (gimp_progress_ht != NULL, NULL); progress_data = g_hash_table_lookup (gimp_progress_ht, progress_callback); if (! progress_data) { g_warning ("Can't find internal progress data"); return NULL; } _gimp_progress_uninstall (progress_callback); gimp_uninstall_temp_proc (progress_callback); user_data = progress_data->data; g_hash_table_remove (gimp_progress_ht, progress_callback); return user_data; } /** * gimp_progress_init: * @message: Message to use in the progress dialog. * * Initializes the progress bar for the current plug-in. * * Initializes the progress bar for the current plug-in. It is only * valid to call this procedure from a plug-in. * * Returns: TRUE on success. */ gboolean gimp_progress_init (const gchar *message) { gimp_progress_current = 0.0; return _gimp_progress_init (message, gimp_default_display ()); } /** * gimp_progress_init_printf: * @format: a standard printf() format string * @...: arguments for @format * * Initializes the progress bar for the current plug-in. * * Initializes the progress bar for the current plug-in. It is only * valid to call this procedure from a plug-in. * * Returns: %TRUE on success. * * Since: 2.4 **/ gboolean gimp_progress_init_printf (const gchar *format, ...) { gchar *text; gboolean retval; va_list args; g_return_val_if_fail (format != NULL, FALSE); va_start (args, format); text = g_strdup_vprintf (format, args); va_end (args); retval = gimp_progress_init (text); g_free (text); return retval; } /** * gimp_progress_set_text_printf: * @format: a standard printf() format string * @...: arguments for @format * * Changes the text in the progress bar for the current plug-in. * * This function changes the text in the progress bar for the current * plug-in. Unlike gimp_progress_init() it does not change the * displayed value. * * Returns: %TRUE on success. * * Since: 2.4 **/ gboolean gimp_progress_set_text_printf (const gchar *format, ...) { gchar *text; gboolean retval; va_list args; g_return_val_if_fail (format != NULL, FALSE); va_start (args, format); text = g_strdup_vprintf (format, args); va_end (args); retval = gimp_progress_set_text (text); g_free (text); return retval; } /** * gimp_progress_update: * @percentage: Percentage of progress completed (in the range from 0.0 to 1.0). * * Updates the progress bar for the current plug-in. * * Returns: TRUE on success. */ gboolean gimp_progress_update (gdouble percentage) { gboolean changed; if (percentage <= 0.0) { changed = (gimp_progress_current != 0.0); percentage = 0.0; } else if (percentage >= 1.0) { changed = (gimp_progress_current != 1.0); percentage = 1.0; } else { changed = (fabs (gimp_progress_current - percentage) > gimp_progress_step); #ifdef GIMP_UNSTABLE if (! changed) { static gboolean warned = FALSE; static gint count = 0; count++; if (count > 3 && ! warned) { g_printerr ("%s is updating the progress too often\n", g_get_prgname ()); warned = TRUE; } } #endif } /* Suppress the update if the change was only marginal. */ if (! changed) return TRUE; gimp_progress_current = percentage; return _gimp_progress_update (gimp_progress_current); } /* private functions */ static void gimp_progress_data_free (GimpProgressData *data) { g_slice_free (GimpProgressData, data); } static void gimp_temp_progress_run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[2]; GimpProgressData *progress_data; *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = GIMP_PDB_SUCCESS; progress_data = g_hash_table_lookup (gimp_progress_ht, name); if (! progress_data) { g_warning ("Can't find internal progress data"); values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR; } else { GimpProgressCommand command = param[0].data.d_int32; switch (command) { case GIMP_PROGRESS_COMMAND_START: progress_data->vtable.start (param[1].data.d_string, param[2].data.d_float != 0.0, progress_data->data); break; case GIMP_PROGRESS_COMMAND_END: progress_data->vtable.end (progress_data->data); break; case GIMP_PROGRESS_COMMAND_SET_TEXT: progress_data->vtable.set_text (param[1].data.d_string, progress_data->data); break; case GIMP_PROGRESS_COMMAND_SET_VALUE: progress_data->vtable.set_value (param[2].data.d_float, progress_data->data); break; case GIMP_PROGRESS_COMMAND_PULSE: if (progress_data->vtable.pulse) progress_data->vtable.pulse (progress_data->data); else progress_data->vtable.set_value (-1, progress_data->data); break; case GIMP_PROGRESS_COMMAND_GET_WINDOW: *nreturn_vals = 2; values[1].type = GIMP_PDB_FLOAT; if (progress_data->vtable.get_window) values[1].data.d_float = (gdouble) progress_data->vtable.get_window (progress_data->data); else values[1].data.d_float = 0; break; default: values[0].data.d_status = GIMP_PDB_CALLING_ERROR; break; } } }