app: add GimpAsyncSet

GimpAsyncSet represents a dynamic set of running GimpAsync objects.
The objects are automatically removed from the set once they're
synced.

GimpAsyncSet implements the GimpWaitable and GimpCancelable
interfaces, allowing the entire set to be waited-on or canceled.

Additionally, GimpAsyncSet provides an "empty" property, which
indicates whether the set is empty or not.  This allows responding
to the completion of all the GimpAsync objects through the set's
"notify::empty" signal, or drive UI changes through property
bindings.
This commit is contained in:
Ell 2018-05-29 09:16:48 -04:00
parent e24ce8861d
commit 80de723022
5 changed files with 425 additions and 0 deletions

View File

@ -129,6 +129,7 @@ AM_LDFLAGS = \
-Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \
-Wl,-u,$(SYMPREFIX)gimp_layer_mode_is_legacy \
-Wl,-u,$(SYMPREFIX)gimp_parallel_init \
-Wl,-u,$(SYMPREFIX)gimp_async_set_new \
-Wl,-u,$(SYMPREFIX)gimp_uncancelable_waitable_new \
-Wl,-u,$(SYMPREFIX)gimp_tool_cursors_get_resource

View File

@ -87,6 +87,8 @@ libappcore_a_sources = \
gimp-utils.h \
gimpasync.c \
gimpasync.h \
gimpasyncset.c \
gimpasyncset.h \
gimpbezierdesc.h \
gimpbezierdesc.c \
gimpboundary.c \

View File

@ -178,6 +178,7 @@ typedef struct _GimpMandala GimpMandala;
/* misc objects */
typedef struct _GimpAsync GimpAsync;
typedef struct _GimpAsyncSet GimpAsyncSet;
typedef struct _GimpBuffer GimpBuffer;
typedef struct _GimpDrawableFilter GimpDrawableFilter;
typedef struct _GimpEnvironTable GimpEnvironTable;

360
app/core/gimpasyncset.c Normal file
View File

@ -0,0 +1,360 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpasyncset.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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "core-types.h"
#include "gimpasync.h"
#include "gimpasyncset.h"
#include "gimpcancelable.h"
#include "gimpwaitable.h"
enum
{
PROP_0,
PROP_EMPTY
};
typedef struct _GimpAsyncSetCallbackInfo GimpAsyncSetCallbackInfo;
struct _GimpAsyncSetPrivate
{
GHashTable *asyncs;
};
/* local function prototypes */
static void gimp_async_set_waitable_iface_init (GimpWaitableInterface *iface);
static void gimp_async_set_cancelable_iface_init (GimpCancelableInterface *iface);
static void gimp_async_set_dispose (GObject *object);
static void gimp_async_set_finalize (GObject *object);
static void gimp_async_set_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_async_set_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_async_set_wait (GimpWaitable *waitable);
static gboolean gimp_async_set_try_wait (GimpWaitable *waitable);
static gboolean gimp_async_set_wait_until (GimpWaitable *waitable,
gint64 end_time);
static void gimp_async_set_cancel (GimpCancelable *cancelable);
static void gimp_async_set_async_callback (GimpAsync *async,
GimpAsyncSet *async_set);
static void gimp_async_set_clear_internal (GimpAsyncSet *async_set,
gboolean wait);
G_DEFINE_TYPE_WITH_CODE (GimpAsyncSet, gimp_async_set, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GIMP_TYPE_WAITABLE,
gimp_async_set_waitable_iface_init)
G_IMPLEMENT_INTERFACE (GIMP_TYPE_CANCELABLE,
gimp_async_set_cancelable_iface_init))
#define parent_class gimp_async_set_parent_class
/* private functions */
static void
gimp_async_set_class_init (GimpAsyncSetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gimp_async_set_dispose;
object_class->finalize = gimp_async_set_finalize;
object_class->set_property = gimp_async_set_set_property;
object_class->get_property = gimp_async_set_get_property;
g_object_class_install_property (object_class, PROP_EMPTY,
g_param_spec_boolean ("empty",
NULL, NULL,
FALSE,
GIMP_PARAM_READABLE));
g_type_class_add_private (klass, sizeof (GimpAsyncSetPrivate));
}
static void
gimp_async_set_waitable_iface_init (GimpWaitableInterface *iface)
{
iface->wait = gimp_async_set_wait;
iface->try_wait = gimp_async_set_try_wait;
iface->wait_until = gimp_async_set_wait_until;
}
static void
gimp_async_set_cancelable_iface_init (GimpCancelableInterface *iface)
{
iface->cancel = gimp_async_set_cancel;
}
static void
gimp_async_set_init (GimpAsyncSet *async_set)
{
async_set->priv = G_TYPE_INSTANCE_GET_PRIVATE (async_set,
GIMP_TYPE_ASYNC_SET,
GimpAsyncSetPrivate);
async_set->priv->asyncs = g_hash_table_new (NULL, NULL);
#ifdef TIME_ASYNC_SET_OPS
async_set->priv->start_time = g_get_monotonic_time ();
#endif
}
static void
gimp_async_set_dispose (GObject *object)
{
GimpAsyncSet *async_set = GIMP_ASYNC_SET (object);
gimp_async_set_clear (async_set);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gimp_async_set_finalize (GObject *object)
{
GimpAsyncSet *async_set = GIMP_ASYNC_SET (object);
g_hash_table_unref (async_set->priv->asyncs);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_async_set_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_async_set_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpAsyncSet *async_set = GIMP_ASYNC_SET (object);
switch (property_id)
{
case PROP_EMPTY:
g_value_set_boolean (value, gimp_async_set_is_empty (async_set));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_async_set_wait (GimpWaitable *waitable)
{
GimpAsyncSet *async_set = GIMP_ASYNC_SET (waitable);
gimp_async_set_clear_internal (async_set, TRUE);
}
static gboolean
gimp_async_set_try_wait (GimpWaitable *waitable)
{
GimpAsyncSet *async_set = GIMP_ASYNC_SET (waitable);
while (! gimp_async_set_is_empty (async_set))
{
GimpAsync *async;
GHashTableIter iter;
g_hash_table_iter_init (&iter, async_set->priv->asyncs);
g_hash_table_iter_next (&iter, (gpointer *) &async, NULL);
if (! gimp_waitable_try_wait (GIMP_WAITABLE (async)))
return FALSE;
}
return TRUE;
}
static gboolean
gimp_async_set_wait_until (GimpWaitable *waitable,
gint64 end_time)
{
GimpAsyncSet *async_set = GIMP_ASYNC_SET (waitable);
while (! gimp_async_set_is_empty (async_set))
{
GimpAsync *async;
GHashTableIter iter;
g_hash_table_iter_init (&iter, async_set->priv->asyncs);
g_hash_table_iter_next (&iter, (gpointer *) &async, NULL);
if (! gimp_waitable_wait_until (GIMP_WAITABLE (async), end_time))
return FALSE;
}
return TRUE;
}
static void
gimp_async_set_cancel (GimpCancelable *cancelable)
{
GimpAsyncSet *async_set = GIMP_ASYNC_SET (cancelable);
GimpAsync *async;
GHashTableIter iter;
g_hash_table_iter_init (&iter, async_set->priv->asyncs);
while (g_hash_table_iter_next (&iter, (gpointer *) &async, NULL))
gimp_cancelable_cancel (GIMP_CANCELABLE (async));
}
static void
gimp_async_set_async_callback (GimpAsync *async,
GimpAsyncSet *async_set)
{
g_hash_table_remove (async_set->priv->asyncs, async);
if (gimp_async_set_is_empty (async_set))
g_object_notify (G_OBJECT (async_set), "empty");
}
static void
gimp_async_set_clear_internal (GimpAsyncSet *async_set,
gboolean wait)
{
GimpAsync *async;
GHashTableIter iter;
if (gimp_async_set_is_empty (async_set))
return;
g_hash_table_iter_init (&iter, async_set->priv->asyncs);
while (g_hash_table_iter_next (&iter, (gpointer *) &async, NULL))
{
gimp_async_remove_callback (
async,
(GimpAsyncCallback) gimp_async_set_async_callback,
async_set);
if (wait)
gimp_waitable_wait (GIMP_WAITABLE (async));
}
g_hash_table_remove_all (async_set->priv->asyncs);
g_object_notify (G_OBJECT (async_set), "empty");
}
/* public functions */
GimpAsyncSet *
gimp_async_set_new (void)
{
return g_object_new (GIMP_TYPE_ASYNC_SET,
NULL);
}
void
gimp_async_set_add (GimpAsyncSet *async_set,
GimpAsync *async)
{
g_return_if_fail (GIMP_IS_ASYNC_SET (async_set));
g_return_if_fail (GIMP_IS_ASYNC (async));
if (g_hash_table_add (async_set->priv->asyncs, async))
{
if (g_hash_table_size (async_set->priv->asyncs) == 1)
g_object_notify (G_OBJECT (async_set), "empty");
gimp_async_add_callback (
async,
(GimpAsyncCallback) gimp_async_set_async_callback,
async_set);
}
}
void
gimp_async_set_remove (GimpAsyncSet *async_set,
GimpAsync *async)
{
g_return_if_fail (GIMP_IS_ASYNC_SET (async_set));
g_return_if_fail (GIMP_IS_ASYNC (async));
if (g_hash_table_remove (async_set->priv->asyncs, async))
{
gimp_async_remove_callback (
async,
(GimpAsyncCallback) gimp_async_set_async_callback,
async_set);
if (g_hash_table_size (async_set->priv->asyncs) == 0)
g_object_notify (G_OBJECT (async_set), "empty");
}
}
void
gimp_async_set_clear (GimpAsyncSet *async_set)
{
g_return_if_fail (GIMP_IS_ASYNC_SET (async_set));
gimp_async_set_clear_internal (async_set, FALSE);
}
gboolean
gimp_async_set_is_empty (GimpAsyncSet *async_set)
{
g_return_val_if_fail (GIMP_IS_ASYNC_SET (async_set), FALSE);
return g_hash_table_size (async_set->priv->asyncs) == 0;
}

61
app/core/gimpasyncset.h Normal file
View File

@ -0,0 +1,61 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpasyncset.h
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __GIMP_ASYNC_SET_H__
#define __GIMP_ASYNC_SET_H__
#define GIMP_TYPE_ASYNC_SET (gimp_async_set_get_type ())
#define GIMP_ASYNC_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ASYNC_SET, GimpAsyncSet))
#define GIMP_ASYNC_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ASYNC_SET, GimpAsyncSetClass))
#define GIMP_IS_ASYNC_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ASYNC_SET))
#define GIMP_IS_ASYNC_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ASYNC_SET))
#define GIMP_ASYNC_SET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ASYNC_SET, GimpAsyncSetClass))
typedef struct _GimpAsyncSetPrivate GimpAsyncSetPrivate;
typedef struct _GimpAsyncSetClass GimpAsyncSetClass;
struct _GimpAsyncSet
{
GObject parent_instance;
GimpAsyncSetPrivate *priv;
};
struct _GimpAsyncSetClass
{
GObjectClass parent_class;
};
GType gimp_async_set_get_type (void) G_GNUC_CONST;
GimpAsyncSet * gimp_async_set_new (void);
void gimp_async_set_add (GimpAsyncSet *async_set,
GimpAsync *async);
void gimp_async_set_remove (GimpAsyncSet *async_set,
GimpAsync *async);
void gimp_async_set_clear (GimpAsyncSet *async_set);
gboolean gimp_async_set_is_empty (GimpAsyncSet *async_set);
#endif /* __GIMP_ASYNC_SET_H__ */