/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * pikaprogress.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 "libpikabase/pikabase.h" #include "pikaprogress.h" #include "pika.h" typedef struct { gchar *progress_callback; PikaProgressVtable vtable; gpointer data; GDestroyNotify data_destroy; } PikaProgressData; /* local function prototypes */ static void pika_progress_data_free (PikaProgressData *data); static PikaValueArray * pika_temp_progress_run (PikaProcedure *procedure, PikaProcedureConfig *config, gpointer run_data); /* private variables */ static gdouble pika_progress_current = 0.0; static const gdouble pika_progress_step = (1.0 / 256.0); static const gint64 pika_progress_delay = 50000; /* 50 millisecond */ /* public functions */ /** * pika_progress_install_vtable: * @vtable: a pointer to a @PikaProgressVtable. * @user_data: a pointer that is passed as user_data to all vtable * functions. * @user_data_destroy: (nullable): destroy function for @user_data, or %NULL. * * Returns: the name of the temporary procedure that's been installed * * Since: 2.4 **/ const gchar * pika_progress_install_vtable (const PikaProgressVtable *vtable, gpointer user_data, GDestroyNotify user_data_destroy) { PikaPlugIn *plug_in; PikaProcedure *procedure; gchar *progress_callback; PikaProgressData *progress_data; 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); plug_in = pika_get_plug_in (); progress_callback = pika_pdb_temp_procedure_name (pika_get_pdb ()); progress_data = g_slice_new0 (PikaProgressData); 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_handle = vtable->get_window_handle; progress_data->data = user_data; progress_data->data_destroy = user_data_destroy; procedure = pika_procedure_new (plug_in, progress_callback, PIKA_PDB_PROC_TYPE_TEMPORARY, pika_temp_progress_run, progress_data, (GDestroyNotify) pika_progress_data_free); PIKA_PROC_ARG_ENUM (procedure, "command", "Command", "The progress command", PIKA_TYPE_PROGRESS_COMMAND, PIKA_PROGRESS_COMMAND_START, G_PARAM_READWRITE); PIKA_PROC_ARG_STRING (procedure, "text", "Text", "The progress text", NULL, G_PARAM_READWRITE); PIKA_PROC_ARG_DOUBLE (procedure, "value", "Value", "The progress value", 0.0, 1.0, 0.0, G_PARAM_READWRITE); PIKA_PROC_VAL_DOUBLE (procedure, "value", "Value", "The progress value", 0.0, 1.0, 0.0, G_PARAM_READWRITE); pika_plug_in_add_temp_procedure (plug_in, procedure); g_object_unref (procedure); if (_pika_progress_install (progress_callback)) { /* Allow callbacks to be watched */ pika_plug_in_extension_enable (plug_in); return progress_callback; } pika_plug_in_remove_temp_procedure (plug_in, progress_callback); return NULL; } /** * pika_progress_uninstall: * @progress_callback: the name of the temporary procedure to uninstall * * Uninstalls a temporary progress procedure that was installed using * pika_progress_install(). * * Since: 2.2 **/ void pika_progress_uninstall (const gchar *progress_callback) { PikaPlugIn *plug_in = pika_get_plug_in (); g_return_if_fail (progress_callback != NULL); pika_plug_in_remove_temp_procedure (plug_in, progress_callback); } /** * pika_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 pika_progress_init (const gchar *message) { PikaDisplay *display = pika_default_display (); gboolean success; pika_progress_current = 0.0; success = _pika_progress_init (message, display); return success; } /** * pika_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 pika_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 = pika_progress_init (text); g_free (text); return retval; } /** * pika_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 pika_progress_init() it does not change the * displayed value. * * Returns: %TRUE on success. * * Since: 2.4 **/ gboolean pika_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 = pika_progress_set_text (text); g_free (text); return retval; } /** * pika_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. * * The library will handle over-updating by possibly dropping silently * some updates when they happen too close next to each other (either * time-wise or step-wise). * The caller does not have to take care of this aspect of progression * and can focus on computing relevant progression steps. * * Returns: TRUE on success. */ gboolean pika_progress_update (gdouble percentage) { static gint64 last_update = 0; gboolean changed; if (percentage <= 0.0) { changed = (pika_progress_current != 0.0); percentage = 0.0; } else if (percentage >= 1.0) { changed = (pika_progress_current != 1.0); percentage = 1.0; } else { if (last_update == 0 || g_get_monotonic_time () - last_update >= pika_progress_delay) { /* If the progression step is too small, better not show it. */ changed = (fabs (pika_progress_current - percentage) > pika_progress_step); } else { /* Too many changes in a short time interval. */ changed = FALSE; } } /* Suppress the update if the change was only marginal or progression * update happens too often. This is not an error, it is just * unneeded to overload the GUI with constant updates. */ if (! changed) return TRUE; pika_progress_current = percentage; last_update = g_get_monotonic_time (); return _pika_progress_update (pika_progress_current); } /* private functions */ static void pika_progress_data_free (PikaProgressData *data) { _pika_progress_uninstall (data->progress_callback); g_free (data->progress_callback); if (data->data_destroy) data->data_destroy (data->data); g_slice_free (PikaProgressData, data); } static PikaValueArray * pika_temp_progress_run (PikaProcedure *procedure, PikaProcedureConfig *config, gpointer run_data) { PikaProgressData *progress_data = run_data; PikaProgressCommand command; gchar *text; gdouble value; g_object_get (config, "command", &command, "text", &text, "value", &value, NULL); switch (command) { case PIKA_PROGRESS_COMMAND_START: progress_data->vtable.start (text, value != 0.0, progress_data->data); break; case PIKA_PROGRESS_COMMAND_END: progress_data->vtable.end (progress_data->data); break; case PIKA_PROGRESS_COMMAND_SET_TEXT: progress_data->vtable.set_text (text, progress_data->data); break; case PIKA_PROGRESS_COMMAND_SET_VALUE: progress_data->vtable.set_value (value, progress_data->data); break; case PIKA_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 PIKA_PROGRESS_COMMAND_GET_WINDOW: { PikaValueArray *return_vals; GBytes *window_handle = NULL; if (progress_data->vtable.get_window_handle) window_handle = progress_data->vtable.get_window_handle (progress_data->data); return_vals = pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL); PIKA_VALUES_SET_BYTES (return_vals, 1, window_handle); g_bytes_unref (window_handle); g_free (text); return return_vals; } break; default: g_free (text); return pika_procedure_new_return_values (procedure, PIKA_PDB_CALLING_ERROR, NULL); } g_free (text); return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL); }