/* 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);
}