407 lines
11 KiB
C
407 lines
11 KiB
C
|
/* 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
|
||
|
* <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <gio/gio.h>
|
||
|
|
||
|
#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,
|
||
|
const PikaValueArray *args,
|
||
|
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 = vtable->get_window;
|
||
|
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,
|
||
|
const PikaValueArray *args,
|
||
|
gpointer run_data)
|
||
|
{
|
||
|
PikaProgressData *progress_data = run_data;
|
||
|
PikaProgressCommand command;
|
||
|
const gchar *text;
|
||
|
gdouble value;
|
||
|
|
||
|
command = PIKA_VALUES_GET_ENUM (args, 0);
|
||
|
text = PIKA_VALUES_GET_STRING (args, 1);
|
||
|
value = PIKA_VALUES_GET_DOUBLE (args, 2);
|
||
|
|
||
|
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;
|
||
|
guint64 window_id = 0;
|
||
|
|
||
|
if (progress_data->vtable.get_window)
|
||
|
window_id = progress_data->vtable.get_window (progress_data->data);
|
||
|
|
||
|
return_vals = pika_procedure_new_return_values (procedure,
|
||
|
PIKA_PDB_SUCCESS,
|
||
|
NULL);
|
||
|
PIKA_VALUES_SET_DOUBLE (return_vals, 1, window_id);
|
||
|
|
||
|
return return_vals;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return pika_procedure_new_return_values (procedure,
|
||
|
PIKA_PDB_CALLING_ERROR,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
|
||
|
}
|