[gnome-builder] libide/gui: add IdeSession and IdeSessionItem
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide/gui: add IdeSession and IdeSessionItem
- Date: Thu, 15 Sep 2022 00:01:14 +0000 (UTC)
commit 6b02527c1e23de31eebfa8102767dd76bb54302f
Author: Christian Hergert <chergert redhat com>
Date: Wed Sep 14 16:55:23 2022 -0700
libide/gui: add IdeSession and IdeSessionItem
These will be our container objects for state to be preserved across runs
of Builder and specifically with a project.
src/libide/gui/ide-session-item-private.h | 32 +
src/libide/gui/ide-session-item.c | 560 ++++++++++++++++
src/libide/gui/ide-session-item.h | 82 +++
src/libide/gui/ide-session-private.h | 51 --
src/libide/gui/ide-session.c | 1044 +++++------------------------
src/libide/gui/ide-session.h | 67 ++
src/libide/gui/libide-gui.h | 1 -
src/libide/gui/meson.build | 8 +-
8 files changed, 908 insertions(+), 937 deletions(-)
---
diff --git a/src/libide/gui/ide-session-item-private.h b/src/libide/gui/ide-session-item-private.h
new file mode 100644
index 000000000..61683d502
--- /dev/null
+++ b/src/libide/gui/ide-session-item-private.h
@@ -0,0 +1,32 @@
+/* ide-session-item-private.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This file 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 file 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 Lesser 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/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-session-item.h"
+
+G_BEGIN_DECLS
+
+IdeSessionItem *_ide_session_item_new_from_variant (GVariant *variant,
+ GError **error);
+void _ide_session_item_to_variant (IdeSessionItem *self,
+ GVariantBuilder *builder);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-session-item.c b/src/libide/gui/ide-session-item.c
new file mode 100644
index 000000000..773a142c0
--- /dev/null
+++ b/src/libide/gui/ide-session-item.c
@@ -0,0 +1,560 @@
+/* ide-session-item.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This file 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 file 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 Lesser 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/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-session-item"
+
+#include "config.h"
+
+#include "ide-session-item-private.h"
+#include "ide-macros.h"
+
+struct _IdeSessionItem
+{
+ GObject parent_instance;
+ PanelPosition *position;
+ char *id;
+ char *type_hint;
+ GHashTable *metadata;
+};
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_POSITION,
+ PROP_TYPE_HINT,
+ N_PROPS
+};
+
+G_DEFINE_FINAL_TYPE (IdeSessionItem, ide_session_item, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_session_item_dispose (GObject *object)
+{
+ IdeSessionItem *self = (IdeSessionItem *)object;
+
+ g_clear_object (&self->position);
+
+ g_clear_pointer (&self->id, g_free);
+ g_clear_pointer (&self->type_hint, g_free);
+ g_clear_pointer (&self->metadata, g_hash_table_unref);
+
+ G_OBJECT_CLASS (ide_session_item_parent_class)->dispose (object);
+}
+
+static void
+ide_session_item_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeSessionItem *self = IDE_SESSION_ITEM (object);
+
+ switch (prop_id)
+ {
+ case PROP_ID:
+ g_value_set_string (value, ide_session_item_get_id (self));
+ break;
+
+ case PROP_POSITION:
+ g_value_set_object (value, ide_session_item_get_position (self));
+ break;
+
+ case PROP_TYPE_HINT:
+ g_value_set_string (value, ide_session_item_get_type_hint (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_session_item_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeSessionItem *self = IDE_SESSION_ITEM (object);
+
+ switch (prop_id)
+ {
+ case PROP_ID:
+ ide_session_item_set_id (self, g_value_get_string (value));
+ break;
+
+ case PROP_POSITION:
+ ide_session_item_set_position (self, g_value_get_object (value));
+ break;
+
+ case PROP_TYPE_HINT:
+ ide_session_item_set_type_hint (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_session_item_class_init (IdeSessionItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ide_session_item_dispose;
+ object_class->get_property = ide_session_item_get_property;
+ object_class->set_property = ide_session_item_set_property;
+
+ properties [PROP_ID] =
+ g_param_spec_string ("id", NULL, NULL, NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_POSITION] =
+ g_param_spec_object ("position", NULL, NULL,
+ PANEL_TYPE_POSITION,
+ (G_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_TYPE_HINT] =
+ g_param_spec_string ("type-hint", NULL, NULL, NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_session_item_init (IdeSessionItem *self)
+{
+}
+
+/**
+ * ide_session_item_get_id:
+ * @self: a #IdeSessionItem
+ *
+ * Gets the id for the session item, if any.
+ *
+ * Returns: (nullable): a string containing the id; otherwise %NULL
+ */
+const char *
+ide_session_item_get_id (IdeSessionItem *self)
+{
+ g_return_val_if_fail (IDE_IS_SESSION_ITEM (self), NULL);
+
+ return self->id;
+}
+
+/**
+ * ide_session_item_set_id:
+ * @self: a #IdeSessionItem
+ * @id: (nullable): an optional identifier for the item
+ *
+ * Sets the identifier for the item.
+ *
+ * The identifier should generally be global to the session as it would
+ * not be expected to come across multiple items with the same id.
+ */
+void
+ide_session_item_set_id (IdeSessionItem *self,
+ const char *id)
+{
+ g_return_if_fail (IDE_IS_SESSION_ITEM (self));
+
+ if (ide_set_string (&self->id, id))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]);
+}
+
+/**
+ * ide_session_item_get_type_hint:
+ * @self: a #IdeSessionItem
+ *
+ * Gets the type hint for an item.
+ *
+ * Returns: (nullable): a type-hint or %NULL
+ */
+const char *
+ide_session_item_get_type_hint (IdeSessionItem *self)
+{
+ g_return_val_if_fail (IDE_IS_SESSION_ITEM (self), NULL);
+
+ return self->type_hint;
+}
+
+/**
+ * ide_session_item_set_type_hint:
+ * @self: a #IdeSessionItem
+ * @type_hint: (nullable): a type hint string for the item
+ *
+ * Sets the type-hint value for the item.
+ *
+ * This is generally used to help inflate the right version of
+ * an object when loading session items.
+ */
+void
+ide_session_item_set_type_hint (IdeSessionItem *self,
+ const char *type_hint)
+{
+ g_return_if_fail (IDE_IS_SESSION_ITEM (self));
+
+ if (ide_set_string (&self->type_hint, type_hint))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TYPE_HINT]);
+}
+
+/**
+ * ide_session_item_get_position:
+ * @self: a #IdeSessionItem
+ *
+ * Gets the #PanelPosition for the item.
+ *
+ * Returns: (transfer none) (nullable): a #PanelPosition or %NULL
+ */
+PanelPosition *
+ide_session_item_get_position (IdeSessionItem *self)
+{
+ g_return_val_if_fail (IDE_IS_SESSION_ITEM (self), NULL);
+
+ return self->position;
+}
+
+/**
+ * ide_session_item_set_position:
+ * @self: a #IdeSessionItem
+ * @position: (nullable): a #PanelPosition or %NULL
+ *
+ * Sets the position for @self, if any.
+ */
+void
+ide_session_item_set_position (IdeSessionItem *self,
+ PanelPosition *position)
+{
+ g_return_if_fail (IDE_IS_SESSION_ITEM (self));
+ g_return_if_fail (!position || PANEL_IS_POSITION (position));
+
+ if (g_set_object (&self->position, position))
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_POSITION]);
+}
+
+/**
+ * ide_session_item_set_metadata: (skip)
+ * @self: a #IdeSessionItem
+ *
+ * A variadic helper to set metadata.
+ *
+ * The format should be identical to g_variant_new().
+ */
+void
+ide_session_item_set_metadata (IdeSessionItem *self,
+ const char *key,
+ const char *format,
+ ...)
+{
+ GVariant *value;
+ va_list args;
+
+ g_return_if_fail (IDE_IS_SESSION_ITEM (self));
+ g_return_if_fail (key != NULL);
+
+ va_start (args, format);
+ value = g_variant_new_va (format, NULL, &args);
+ va_end (args);
+
+ g_return_if_fail (value != NULL);
+
+ ide_session_item_set_metadata_value (self, key, value);
+}
+
+/**
+ * ide_session_item_has_metadata:
+ * @self: a #IdeSessionItem
+ * @key: the name of the metadata
+ * @value_type: (out) (nullable): a location for a #GVariantType or %NULL
+ *
+ * If the item contains a metadata value for @key.
+ *
+ * Checks if a value exists for a metadata key and retrieves the #GVariantType
+ * for that key.
+ *
+ * Returns: %TRUE if @self contains metadata named @key and @value_type is set
+ * to the value's #GVariantType. Otherwise %FALSE and @value_type is unchanged.
+ */
+gboolean
+ide_session_item_has_metadata (IdeSessionItem *self,
+ const char *key,
+ const GVariantType **value_type)
+{
+ GVariant *value;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (IDE_IS_SESSION_ITEM (self), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+
+ if ((value = ide_session_item_get_metadata_value (self, key, NULL)))
+ {
+ g_assert (!g_variant_is_floating (value));
+
+ if (value_type != NULL)
+ *value_type = g_variant_get_type (value);
+
+ ret = TRUE;
+
+ g_variant_unref (value);
+ }
+
+ return ret;
+}
+
+/**
+ * ide_session_item_has_metadata_with_type:
+ * @self: a #IdeSessionItem
+ * @key: the metadata key
+ * @expected_type: the #GVariantType to check for @key
+ *
+ * Checks if the item contains metadata @key with @expected_type.
+ *
+ * Returns: %TRUE if a value was found for @key matching @expected_typed;
+ * otherwise %FALSE is returned.
+ */
+gboolean
+ide_session_item_has_metadata_with_type (IdeSessionItem *self,
+ const char *key,
+ const GVariantType *expected_type)
+{
+ const GVariantType *value_type = NULL;
+
+ g_return_val_if_fail (IDE_IS_SESSION_ITEM (self), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (expected_type != NULL, FALSE);
+
+ if (ide_session_item_has_metadata (self, key, &value_type))
+ return g_variant_type_equal (value_type, expected_type);
+
+ return FALSE;
+}
+
+/**
+ * ide_session_item_get_metadata: (skip)
+ * @self: a #IdeSessionItem
+ * @key: the key for the metadata value
+ * @format: the format of the value
+ *
+ * Extract a metadata value matching @format.
+ *
+ * It is an error to use this function on untrusted data where you have not
+ * checked the type of the value of @key using ide_session_item_has_metadata()
+ * or ide_session_item_has_metadata_with_type().
+ */
+void
+ide_session_item_get_metadata (IdeSessionItem *self,
+ const char *key,
+ const char *format,
+ ...)
+{
+ GVariant *value;
+ va_list args;
+
+ g_return_if_fail (IDE_IS_SESSION_ITEM (self));
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (format != NULL);
+ g_return_if_fail (g_variant_type_string_is_valid (format));
+ g_return_if_fail (ide_session_item_has_metadata (self, key, NULL));
+
+ value = ide_session_item_get_metadata_value (self, key, NULL);
+
+ g_return_if_fail (value != NULL);
+
+ va_start (args, format);
+ g_variant_get_va (value, format, NULL, &args);
+ va_end (args);
+
+ g_variant_unref (value);
+}
+
+/**
+ * ide_session_item_get_metadata_value:
+ * @self: a #IdeSessionItem
+ * @key: the metadata key
+ * @expected_type: (nullable): a #GVariantType or %NULL
+ *
+ * Retrieves the metadata value for @key.
+ *
+ * If @expected_type is non-%NULL, any non-%NULL value returned from this
+ * function will match @expected_type.
+ *
+ * Returns: (transfer full): a non-floating #GVariant which should be
+ * released with g_variant_unref(); otherwise %NULL.
+ */
+GVariant *
+ide_session_item_get_metadata_value (IdeSessionItem *self,
+ const char *key,
+ const GVariantType *expected_type)
+{
+ GVariant *ret;
+
+ g_return_val_if_fail (IDE_IS_SESSION_ITEM (self), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ if (self->metadata == NULL)
+ return NULL;
+
+ if ((ret = g_hash_table_lookup (self->metadata, key)))
+ {
+ if (expected_type == NULL || g_variant_is_of_type (ret, expected_type))
+ return g_variant_ref (ret);
+ }
+
+ return NULL;
+}
+
+/**
+ * ide_session_item_set_metadata_value:
+ * @self: a #IdeSessionItem
+ * @key: the metadata key
+ * @value: (nullable): the value for @key or %NULL
+ *
+ * Sets the value for metadata @key.
+ *
+ * If @value is %NULL, the metadata key is unset.
+ */
+void
+ide_session_item_set_metadata_value (IdeSessionItem *self,
+ const char *key,
+ GVariant *value)
+{
+ g_return_if_fail (IDE_IS_SESSION_ITEM (self));
+ g_return_if_fail (key != NULL);
+
+ if (value != NULL)
+ {
+ if G_UNLIKELY (self->metadata == NULL)
+ self->metadata = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify)g_variant_unref);
+ g_hash_table_insert (self->metadata,
+ g_strdup (key),
+ g_variant_ref_sink (value));
+ }
+ else
+ {
+ if (self->metadata != NULL)
+ g_hash_table_remove (self->metadata, key);
+ }
+}
+
+void
+_ide_session_item_to_variant (IdeSessionItem *self,
+ GVariantBuilder *builder)
+{
+ g_return_if_fail (IDE_IS_SESSION_ITEM (self));
+ g_return_if_fail (builder != NULL);
+
+ g_variant_builder_open (builder, G_VARIANT_TYPE ("v"));
+ g_variant_builder_open (builder, G_VARIANT_TYPE ("a{sv}"));
+
+ if (self->position != NULL)
+ g_variant_builder_add_parsed (builder,
+ "{'position',<%v>}",
+ panel_position_to_variant (self->position));
+
+ if (self->id != NULL)
+ g_variant_builder_add_parsed (builder, "{'id',<%s>}", self->id);
+
+ if (self->type_hint != NULL)
+ g_variant_builder_add_parsed (builder, "{'type-hint',<%s>}", self->type_hint);
+
+ if (self->metadata != NULL && g_hash_table_size (self->metadata) > 0)
+ {
+ GHashTableIter iter;
+ const char *key;
+ GVariant *value;
+
+ g_variant_builder_open (builder, G_VARIANT_TYPE ("{sv}"));
+ g_variant_builder_add (builder, "s", "metadata");
+ g_variant_builder_open (builder, G_VARIANT_TYPE ("v"));
+ g_variant_builder_open (builder, G_VARIANT_TYPE ("a{sv}"));
+
+ g_hash_table_iter_init (&iter, self->metadata);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ g_variant_builder_add_parsed (builder, "{%s,<%v>}", key, value);
+
+ g_variant_builder_close (builder);
+ g_variant_builder_close (builder);
+ g_variant_builder_close (builder);
+ }
+
+ g_variant_builder_close (builder);
+ g_variant_builder_close (builder);
+}
+
+IdeSessionItem *
+ide_session_item_new (void)
+{
+ return g_object_new (IDE_TYPE_SESSION_ITEM, NULL);
+}
+
+IdeSessionItem *
+_ide_session_item_new_from_variant (GVariant *variant,
+ GError **error)
+{
+ GVariant *positionv = NULL;
+ GVariant *metadatav = NULL;
+ IdeSessionItem *self;
+
+ g_return_val_if_fail (variant != NULL, NULL);
+ g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL);
+
+ self = g_object_new (IDE_TYPE_SESSION_ITEM, NULL);
+
+ g_variant_lookup (variant, "id", "s", &self->id);
+ g_variant_lookup (variant, "type-hint", "s", &self->type_hint);
+
+ if ((positionv = g_variant_lookup_value (variant, "position", NULL)))
+ {
+ GVariant *child = g_variant_get_variant (positionv);
+ self->position = panel_position_new_from_variant (child);
+ g_clear_pointer (&child, g_variant_unref);
+ }
+
+ if ((metadatav = g_variant_lookup_value (variant, "metadata", G_VARIANT_TYPE_VARDICT)))
+ {
+ GVariantIter iter;
+ GVariant *value;
+ char *key;
+
+ g_variant_iter_init (&iter, metadatav);
+
+ while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
+ {
+ GVariant *unwrapped = g_variant_get_variant (value);
+ ide_session_item_set_metadata_value (self, key, unwrapped);
+ g_clear_pointer (&unwrapped, g_variant_unref);
+ }
+ }
+
+ g_clear_pointer (&positionv, g_variant_unref);
+ g_clear_pointer (&metadatav, g_variant_unref);
+
+ return self;
+}
diff --git a/src/libide/gui/ide-session-item.h b/src/libide/gui/ide-session-item.h
new file mode 100644
index 000000000..82c522be9
--- /dev/null
+++ b/src/libide/gui/ide-session-item.h
@@ -0,0 +1,82 @@
+/* ide-session-item.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This file 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 file 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 Lesser 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/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libpanel.h>
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SESSION_ITEM (ide_session_item_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeSessionItem, ide_session_item, IDE, SESSION_ITEM, GObject)
+
+IDE_AVAILABLE_IN_ALL
+IdeSessionItem *ide_session_item_new (void);
+IDE_AVAILABLE_IN_ALL
+PanelPosition *ide_session_item_get_position (IdeSessionItem *self);
+IDE_AVAILABLE_IN_ALL
+void ide_session_item_set_position (IdeSessionItem *self,
+ PanelPosition *position);
+IDE_AVAILABLE_IN_ALL
+const char *ide_session_item_get_id (IdeSessionItem *self);
+IDE_AVAILABLE_IN_ALL
+void ide_session_item_set_id (IdeSessionItem *self,
+ const char *id);
+IDE_AVAILABLE_IN_ALL
+const char *ide_session_item_get_type_hint (IdeSessionItem *self);
+IDE_AVAILABLE_IN_ALL
+void ide_session_item_set_type_hint (IdeSessionItem *self,
+ const char *type_hint);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_session_item_has_metadata (IdeSessionItem *self,
+ const char *key,
+ const GVariantType **value_type);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_session_item_has_metadata_with_type (IdeSessionItem *self,
+ const char *key,
+ const GVariantType *expected_type);
+IDE_AVAILABLE_IN_ALL
+void ide_session_item_get_metadata (IdeSessionItem *self,
+ const char *key,
+ const char *format,
+ ...);
+IDE_AVAILABLE_IN_ALL
+void ide_session_item_set_metadata (IdeSessionItem *self,
+ const char *key,
+ const char *format,
+ ...);
+IDE_AVAILABLE_IN_ALL
+GVariant *ide_session_item_get_metadata_value (IdeSessionItem *self,
+ const char *key,
+ const GVariantType *expected_type);
+IDE_AVAILABLE_IN_ALL
+void ide_session_item_set_metadata_value (IdeSessionItem *self,
+ const char *key,
+ GVariant *value);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-session.c b/src/libide/gui/ide-session.c
index 1f69a27e7..f659fd535 100644
--- a/src/libide/gui/ide-session.c
+++ b/src/libide/gui/ide-session.c
@@ -1,6 +1,6 @@
/* ide-session.c
*
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2022 Christian Hergert <chergert redhat com>
*
* 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
@@ -22,982 +22,264 @@
#include "config.h"
-#include <libpeas/peas.h>
-
-#include <libide-plugins.h>
-#include <libide-threading.h>
-
-#include "ide-frame.h"
-#include "ide-session-addin.h"
-#include "ide-session-private.h"
+#include "ide-session.h"
+#include "ide-session-item-private.h"
struct _IdeSession
{
- IdeObject parent_instance;
- GPtrArray *addins;
+ GObject parent_instance;
+ GPtrArray *items;
};
-typedef struct
-{
- GPtrArray *addins;
- GVariantBuilder pages_state;
- guint active;
- IdeGrid *grid;
-} Save;
-
-typedef struct
-{
- GPtrArray *addins;
- GVariant *state;
- IdeGrid *grid;
- GArray *pages;
- guint active;
-} Restore;
-
-typedef struct
-{
- guint column;
- guint row;
- guint depth;
- IdeSessionAddin *addin;
- GVariant *state;
- IdePage *restored_page;
-} RestoreItem;
-
-G_DEFINE_FINAL_TYPE (IdeSession, ide_session, IDE_TYPE_OBJECT)
-
-static void
-restore_free (Restore *r)
-{
- g_assert (r != NULL);
- g_assert (r->active == 0);
-
- g_clear_pointer (&r->state, g_variant_unref);
- g_clear_pointer (&r->pages, g_array_unref);
-
- g_slice_free (Restore, r);
-}
-
-static void
-save_free (Save *s)
-{
- g_assert (s != NULL);
- g_assert (s->active == 0);
-
- g_slice_free (Save, s);
-}
-
-static void
-restore_item_clear (RestoreItem *item)
-{
- g_assert (item != NULL);
-
- g_clear_pointer (&item->state, g_variant_unref);
-}
-
-static gint
-compare_restore_items (gconstpointer a,
- gconstpointer b)
-{
- const RestoreItem *item_a = a;
- const RestoreItem *item_b = b;
- gint ret;
-
- if (!(ret = item_a->column - item_b->column))
- {
- if (!(ret = item_a->row - item_b->row))
- ret = item_a->depth - item_b->depth;
- }
-
- return ret;
-}
-
-static void
-collect_addins_cb (IdeExtensionSetAdapter *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
-{
- GPtrArray *ar = user_data;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
- g_assert (plugin_info != NULL);
- g_assert (IDE_IS_SESSION_ADDIN (exten));
- g_assert (ar != NULL);
-
- g_ptr_array_add (ar, g_object_ref (exten));
-}
-
-static IdeSessionAddin *
-find_suitable_addin_for_page (IdePage *page,
- GPtrArray *addins)
-{
- for (guint i = 0; i < addins->len; i++)
- {
- IdeSessionAddin *addin = g_ptr_array_index (addins, i);
- if (ide_session_addin_can_save_page (addin, page))
- return addin;
- }
- return NULL;
-}
-
-static void
-on_session_autosaved_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- IdeSession *session = (IdeSession *)object;
- g_autoptr(GError) error = NULL;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_SESSION (session));
- g_assert (G_IS_ASYNC_RESULT (result));
-
- if (!ide_session_save_finish (session, result, &error))
- g_warning ("Couldn't autosave session: %s", error->message);
-}
-
-typedef struct {
- IdeSession *session;
- IdeGrid *grid;
- guint session_autosave_source;
-} AutosaveGrid;
-
-static void
-autosave_grid_free (gpointer data,
- GClosure *closure)
-{
- AutosaveGrid *self = (AutosaveGrid *)data;
-
- if (self->session_autosave_source)
- {
- g_source_remove (self->session_autosave_source);
- self->session_autosave_source = 0;
- }
- g_slice_free (AutosaveGrid, self);
-}
-
-static gboolean
-on_session_autosave_timeout_cb (gpointer user_data)
-{
- AutosaveGrid *autosave_grid = (AutosaveGrid *)user_data;
-
- g_assert (IDE_IS_SESSION (autosave_grid->session));
- g_assert (IDE_IS_GRID (autosave_grid->grid));
-
- ide_session_save_async (autosave_grid->session,
- autosave_grid->grid,
- NULL,
- on_session_autosaved_cb,
- NULL);
-
- autosave_grid->session_autosave_source = 0;
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-schedule_session_autosave_timeout (AutosaveGrid *autosave_grid)
-{
- if (!autosave_grid->session_autosave_source)
- {
- /* We don't want to be saving the state on each (small) change, so introduce a small
- * timeout so changes are grouped when saving.
- */
- autosave_grid->session_autosave_source =
- g_timeout_add_seconds (30,
- on_session_autosave_timeout_cb,
- autosave_grid);
- }
-}
-
-static void
-on_autosave_property_changed_cb (GObject *gobject,
- GParamSpec *pspec,
- gpointer user_data)
-{
- schedule_session_autosave_timeout ((AutosaveGrid *)user_data);
-}
-
-static void
-watch_pages_session_autosave (AutosaveGrid *autosave_grid,
- guint start_pos,
- guint end_pos)
-{
- GListModel *list = (GListModel *)autosave_grid->grid;
- IdeSession *session = (IdeSession *)autosave_grid->session;
-
- g_assert (IDE_IS_SESSION (session));
- g_assert (G_IS_LIST_MODEL (list));
- g_assert (g_type_is_a (g_list_model_get_item_type (list), IDE_TYPE_PAGE));
- g_assert (start_pos <= end_pos);
-
- for (guint i = start_pos; i < end_pos; i++)
- {
- IdePage *page = IDE_PAGE (g_list_model_get_object (list, i));
- IdeSessionAddin *addin;
- g_auto(GStrv) props = NULL;
-
- if ((addin = find_suitable_addin_for_page (page, session->addins)) &&
- (props = ide_session_addin_get_autosave_properties (addin)))
- {
- for (guint j = 0; props[j] != NULL; j++)
- {
- char detailed_signal[256];
- g_snprintf (detailed_signal, sizeof detailed_signal, "notify::%s", props[j]);
-
- g_signal_connect (page, detailed_signal, G_CALLBACK (on_autosave_property_changed_cb),
autosave_grid);
- }
- }
- }
-}
-
-static void
-on_grid_items_changed_cb (GListModel *list,
- guint position,
- guint removed,
- guint added,
- gpointer user_data)
-{
- AutosaveGrid *autosave_grid = (AutosaveGrid *)user_data;
-
- g_assert (G_IS_LIST_MODEL (list));
- g_assert (g_type_is_a (g_list_model_get_item_type (list), IDE_TYPE_PAGE));
-
- /* We've nothing to do when no page were added here as signals are
- * automatically disconnected, so avoid extra work by stopping here early.
- */
- if (added > 0)
- watch_pages_session_autosave (autosave_grid, position, position + added);
-
- /* Handles autosaving both when closing/opening a page and when moving a page in the grid. */
- schedule_session_autosave_timeout (autosave_grid);
-}
-
-static void
-ide_session_destroy (IdeObject *object)
-{
- IdeSession *self = (IdeSession *)object;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_SESSION (self));
-
- g_clear_pointer (&self->addins, g_ptr_array_unref);
-
- IDE_OBJECT_CLASS (ide_session_parent_class)->destroy (object);
-
- IDE_EXIT;
-}
+G_DEFINE_FINAL_TYPE (IdeSession, ide_session, G_TYPE_OBJECT)
static void
-ide_session_parent_set (IdeObject *object,
- IdeObject *parent)
+ide_session_dispose (GObject *object)
{
IdeSession *self = (IdeSession *)object;
- g_autoptr(IdeExtensionSetAdapter) extension_set = NULL;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_SESSION (self));
- g_assert (!parent || IDE_IS_OBJECT (parent));
-
- if (parent == NULL)
- return;
- extension_set = ide_extension_set_adapter_new (IDE_OBJECT (self),
- peas_engine_get_default (),
- IDE_TYPE_SESSION_ADDIN,
- NULL, NULL);
+ g_clear_pointer (&self->items, g_ptr_array_unref);
- self->addins = g_ptr_array_new_with_free_func (g_object_unref);
- ide_extension_set_adapter_foreach (extension_set, collect_addins_cb, self->addins);
+ G_OBJECT_CLASS (ide_session_parent_class)->dispose (object);
}
static void
ide_session_class_init (IdeSessionClass *klass)
{
- IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
- i_object_class->destroy = ide_session_destroy;
- i_object_class->parent_set = ide_session_parent_set;
+ object_class->dispose = ide_session_dispose;
}
static void
ide_session_init (IdeSession *self)
{
+ self->items = g_ptr_array_new_with_free_func (g_object_unref);
}
-static void
-restore_pages_to_grid (GArray *r_items,
- IdeGrid *grid)
-{
- IDE_ENTRY;
- for (guint i = 0; i < r_items->len; i++)
- {
- RestoreItem *item = &g_array_index (r_items, RestoreItem, i);
- PanelGridColumn *column;
- PanelFrame *frame;
-
- /* Ignore pages that couldn't be restored. */
- if (item->restored_page == NULL)
- continue;
-
- /* This relies on the fact that the items are sorted. */
- column = panel_grid_get_column (PANEL_GRID (grid), item->column);
- frame = panel_grid_column_get_row (column, item->row);
-
- panel_frame_add (frame, PANEL_WIDGET (item->restored_page));
- }
- IDE_EXIT;
-}
-
-typedef struct
-{
- IdeTask *task;
- RestoreItem *item;
-} RestorePage;
-
-static void
-on_session_addin_page_restored_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+/**
+ * ide_session_to_variant:
+ * @self: a #IdeSession
+ *
+ * Serializes a #IdeSession as a #GVariant
+ *
+ * The result of this function may be passed to
+ * ide_session_new_from_variant() to recreate a #IdeSession.
+ *
+ * Returns: (transfer full): a #GVariant
+ */
+GVariant *
+ide_session_to_variant (IdeSession *self)
{
- IdeSessionAddin *addin = (IdeSessionAddin *)object;
- RestorePage *r_page = user_data;
- g_autoptr(IdeTask) task = r_page->task;
- g_autoptr(GError) error = NULL;
- RestoreItem *item = r_page->item;
- Restore *r;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SESSION_ADDIN (addin));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_TASK (task));
-
- r = ide_task_get_task_data (task);
-
- g_assert (r != NULL);
- g_assert (r->addins != NULL);
- g_assert (r->active > 0);
- g_assert (r->state != NULL);
+ GVariantBuilder builder;
- if (!(item->restored_page = ide_session_addin_restore_page_finish (addin, result, &error)))
- g_warning ("Couldn't restore page with addin %s: %s", G_OBJECT_TYPE_NAME (addin), error->message);
+ g_return_val_if_fail (IDE_IS_SESSION (self), NULL);
- r->active--;
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add_parsed (&builder, "{'version',<%u>}", 1);
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("{sv}"));
+ g_variant_builder_add (&builder, "s", "items");
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("v"));
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("av"));
+ for (guint i = 0; i < self->items->len; i++)
+ {
+ IdeSessionItem *item = g_ptr_array_index (self->items, i);
- if (r->active == 0)
- {
- restore_pages_to_grid (r->pages, r->grid);
-
- ide_task_return_boolean (task, TRUE);
- }
-
- IDE_EXIT;
+ _ide_session_item_to_variant (item, &builder);
+ }
+ g_variant_builder_close (&builder);
+ g_variant_builder_close (&builder);
+ g_variant_builder_close (&builder);
+ return g_variant_builder_end (&builder);
}
-static IdeSessionAddin *
-get_addin_for_name (GPtrArray *addins,
- const char *addin_name)
-{
- GType addin_type = g_type_from_name (addin_name);
- for (guint i = 0; i < addins->len; i++)
- {
- if (G_OBJECT_TYPE (addins->pdata[i]) == addin_type)
- return addins->pdata[i];
- }
- return NULL;
-}
-
-static void
-load_restore_items (Restore *r,
- GArray *items)
+static gboolean
+ide_session_load_1 (IdeSession *self,
+ GVariant *variant,
+ GError **error)
{
- GVariantIter iter;
- RestoreItem item;
- GVariant *page_state = NULL;
+ GVariant *items = NULL;
+ gboolean ret = FALSE;
- g_assert (r != NULL);
- g_assert (r->state != NULL);
- g_assert (r->addins != NULL);
- g_assert (items != NULL);
+ g_assert (IDE_IS_SESSION (self));
+ g_assert (variant != NULL);
+ g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT));
- g_variant_iter_init (&iter, r->state);
- while ((page_state = g_variant_iter_next_value (&iter)))
+ if ((items = g_variant_lookup_value (variant, "items", G_VARIANT_TYPE ("av"))))
{
- const char *addin_name = NULL;
-
- g_variant_lookup (page_state, "column", "u", &item.column);
- g_variant_lookup (page_state, "row", "u", &item.row);
- g_variant_lookup (page_state, "depth", "u", &item.depth);
- g_variant_lookup (page_state, "addin_name", "&s", &addin_name);
- g_variant_lookup (page_state, "addin_page_state", "v", &item.state);
-
- item.addin = get_addin_for_name (r->addins, addin_name);
- g_array_append_val (items, item);
-
- g_variant_unref (page_state);
- }
-}
+ gsize n_children = g_variant_n_children (items);
-static GVariant *
-migrate_pre_api_rework (GVariant *pages_variant)
-{
- GVariantIter iter;
- const char *uri = NULL;
- int column, row, depth;
- /* Freed in the loop. */
- GVariant *search_variant;
+ for (gsize i = 0; i < n_children; i++)
+ {
+ GVariant *itemv = g_variant_get_child_value (items, i);
+ GVariant *infov = g_variant_get_variant (itemv);
+ IdeSessionItem *item = _ide_session_item_new_from_variant (infov, error);
- GVariantDict version_wrapper_dict;
- GVariantBuilder addins_states;
+ g_clear_pointer (&infov, g_variant_unref);
+ g_clear_pointer (&itemv, g_variant_unref);
- g_variant_dict_init (&version_wrapper_dict, NULL);
- /* Migrate old format to first version of the new format. */
- g_variant_dict_insert (&version_wrapper_dict, "version", "u", (guint32) 1);
+ if (item == NULL)
+ goto cleanup;
- g_variant_builder_init (&addins_states, G_VARIANT_TYPE ("aa{sv}"));
+ g_ptr_array_add (self->items, g_steal_pointer (&item));
+ }
- g_debug ("Handling migration of the project's session.gvariant, from prior to the Session API rework…");
+ ret = TRUE;
- g_variant_iter_init (&iter, pages_variant);
- while (g_variant_iter_next (&iter, "(&siiiv)", &uri, &column, &row, &depth, &search_variant))
- {
- GVariantDict addin_state;
- GVariantDict editor_session_state;
-
- g_variant_dict_init (&addin_state, NULL);
- g_variant_dict_insert (&addin_state, "column", "u", (guint32) column);
- g_variant_dict_insert (&addin_state, "row", "u", (guint32) row);
- g_variant_dict_insert (&addin_state, "depth", "u", (guint32) depth);
- g_variant_dict_insert (&addin_state, "addin_name", "s", "GbpEditorSessionAddin");
-
- /* Since we need to migrate the data for the new API, let's also migrate to a dictionary
- * instead of a tuple, for greater flexibility and extensibility in the future.
- */
- g_variant_dict_init (&editor_session_state, NULL);
- g_variant_dict_insert (&editor_session_state, "uri", "s", uri);
- /* Unbox the search_variant since we don't want to bother with multiple levels of variants,
- * just have an a{sv}
- */
- g_variant_dict_insert_value (&editor_session_state, "search", g_variant_get_variant (search_variant));
- g_variant_dict_insert (&addin_state, "addin_page_state", "v", g_variant_dict_end
(&editor_session_state));
- g_variant_builder_add_value (&addins_states, g_variant_dict_end (&addin_state));
-
- g_variant_unref (search_variant);
+ goto cleanup;
}
- g_variant_dict_insert_value (&version_wrapper_dict, "data", g_variant_builder_end (&addins_states));
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "items missing from variant");
- g_debug ("Successfully migrated old session.gvariant to new format.");
+cleanup:
+ g_clear_pointer (&items, g_variant_unref);
- return g_variant_take_ref (g_variant_dict_end (&version_wrapper_dict));
+ return ret;
}
-static GVariant *
-load_state_with_migrations (GBytes *bytes)
+static gboolean
+ide_session_load (IdeSession *self,
+ GVariant *variant,
+ GError **error)
{
- g_autoptr(GVariant) variant = NULL;
- /* This is the value of the "data" key in the final @variant. */
- g_autoptr(GVariant) migrated_state = NULL;
- g_autoptr(GVariant) old_api_state = NULL;
- gboolean fully_migrated = FALSE;
+ guint version = 0;
- g_assert (bytes != NULL);
-
- variant = g_variant_take_ref (g_variant_new_from_bytes (G_VARIANT_TYPE_VARDICT, bytes, FALSE));
+ g_assert (IDE_IS_SESSION (self));
+ g_assert (variant != NULL);
+ g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT));
- if (!variant)
+ if (g_variant_lookup (variant, "version", "u", &version))
{
- g_warning ("Couldn't load the array of pages' states from session.gvariant!");
- return NULL;
+ if (version == 1)
+ return ide_session_load_1 (self, variant, error);
}
- /* Handle migrations from prior to the Session API rework, where there was only GbpEditorSessionAddin that
used it */
- old_api_state = g_variant_lookup_value (variant, "GbpEditorSessionAddin", G_VARIANT_TYPE ("a(siiiv)"));
- if (old_api_state)
- migrated_state = migrate_pre_api_rework (old_api_state);
- else
- migrated_state = g_steal_pointer (&variant);
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Invalid version number in serialized session");
- while (!fully_migrated)
- {
- guint32 version;
- g_autoptr(GVariant) versioned_data = NULL;
-
- if (!g_variant_lookup (migrated_state, "version", "u", &version))
- {
- g_warning ("session.gvariant isn't using the old format but doesn't have a version field, so
cannot load it!");
- fully_migrated = TRUE;
- migrated_state = NULL;
- break;
- }
-
- if (!(versioned_data = g_variant_lookup_value (migrated_state, "data", G_VARIANT_TYPE ("aa{sv}"))))
- {
- g_warning ("session.gvariant had a version field but the actual versioned data wasn't found, so
cannot load it!");
- fully_migrated = TRUE;
- migrated_state = NULL;
- break;
- }
-
- switch (version)
- {
- /* It's the current format so the rest of the code understands it natively. */
- case 1:
- migrated_state = g_steal_pointer (&migrated_state);
- fully_migrated = TRUE;
- break;
-
- default:
- g_warning ("Version %d of session.gvariant data is not known to Builder!", version);
- migrated_state = NULL;
- fully_migrated = TRUE;
- }
- }
-
- if (migrated_state)
- /* The current format (version 1) is an `aa{sv}` (array of dictionaries) with the dict's keys being:
- * guint32 column, row, depth;
- * char *addin_name;
- * GVariant *addin_page_state;
- */
- return g_variant_lookup_value (migrated_state, "data", NULL);
- else
- return NULL;
+ return FALSE;
}
-static void
-on_session_cache_loaded_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+IdeSession *
+ide_session_new (void)
{
- GFile *file = (GFile *)object;
- g_autoptr(IdeTask) task = user_data;
- g_autoptr(GError) error = NULL;
- g_autoptr(GBytes) bytes = NULL;
- GArray *items = NULL;
- GCancellable *cancellable;
- Restore *r;
-
- IDE_ENTRY;
-
- g_assert (G_IS_FILE (file));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_TASK (task));
-
- r = ide_task_get_task_data (task);
- cancellable = ide_task_get_cancellable (task);
-
- g_assert (r != NULL);
- g_assert (r->addins != NULL);
- g_assert (r->addins->len > 0);
- g_assert (r->state == NULL);
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- if (!(bytes = g_file_load_bytes_finish (file, result, NULL, &error)))
- {
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- ide_task_return_boolean (task, TRUE);
- else
- ide_task_return_error (task, g_steal_pointer (&error));
- IDE_EXIT;
- }
-
- if (g_bytes_get_size (bytes) == 0)
- {
- ide_task_return_boolean (task, TRUE);
- IDE_EXIT;
- }
-
- r->state = load_state_with_migrations (bytes);
-
- if (r->state == NULL)
- {
- ide_task_return_new_error (task,
- G_IO_ERROR,
- G_IO_ERROR_INVALID_DATA,
- "Failed to decode session state");
- IDE_EXIT;
- }
-
- items = g_array_new (FALSE, FALSE, sizeof (RestoreItem));
- g_array_set_clear_func (items, (GDestroyNotify)restore_item_clear);
- load_restore_items (r, items);
- r->pages = items;
- r->active = items->len;
- g_array_sort (items, compare_restore_items);
-
- for (guint i = 0; i < items->len; i++)
- {
- RestoreItem *item = &g_array_index (items, RestoreItem, i);
- RestorePage *r_page = g_slice_new0 (RestorePage);
- r_page->task = g_object_ref (task);
- r_page->item = item;
-
- ide_session_addin_restore_page_async (item->addin,
- item->state,
- cancellable,
- on_session_addin_page_restored_cb,
- r_page);
- }
-
- if (r->active == 0)
- {
- ide_task_return_boolean (task, TRUE);
- IDE_EXIT;
- }
-
- IDE_EXIT;
+ return g_object_new (IDE_TYPE_SESSION, NULL);
}
/**
- * ide_session_restore_async:
- * @self: an #IdeSession
- * @grid: an #IdeGrid
- * @cancellable: (nullable): a #GCancellable or %NULL
- * @callback: the callback to execute upon completion
- * @user_data: user data for callback
+ * ide_session_new_from_variant:
+ * @variant: a #GVariant from ide_session_to_variant()
+ * @error: a location for a #GError, or %NULL
*
- * This function will asynchronously restore the state of the project to
- * the point it was last saved (typically upon shutdown). This includes
- * open documents and editor splits to the degree possible. Adding support
- * for a new page type requires implementing an #IdeSessionAddin.
+ * Creates a new #IdeSession from a #GVariant.
+ *
+ * This creates a new #IdeSession instance from a previous session
+ * which had been serialized to @variant.
+ *
+ * Returns: (transfer full): a #IdeSession
*/
-void
-ide_session_restore_async (IdeSession *self,
- IdeGrid *grid,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+IdeSession *
+ide_session_new_from_variant (GVariant *variant,
+ GError **error)
{
- g_autoptr(IdeTask) task = NULL;
- g_autoptr(GFile) file = NULL;
- g_autoptr(GSettings) settings = NULL;
- IdeContext *context;
- Restore *r;
-
- IDE_ENTRY;
-
- g_return_if_fail (IDE_IS_SESSION (self));
- g_return_if_fail (IDE_IS_GRID (grid));
- g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- task = ide_task_new (self, cancellable, callback, user_data);
- ide_task_set_source_tag (task, ide_session_restore_async);
-
- r = g_slice_new0 (Restore);
- r->addins = self->addins;
- r->grid = grid;
- ide_task_set_task_data (task, r, restore_free);
+ IdeSession *self;
- settings = g_settings_new ("org.gnome.builder");
- if (!g_settings_get_boolean (settings, "restore-previous-files"))
- {
- ide_task_return_boolean (task, TRUE);
- return;
- }
+ g_return_val_if_fail (variant != NULL, NULL);
+ g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL);
- context = ide_object_get_context (IDE_OBJECT (self));
- file = ide_context_cache_file (context, "session.gvariant", NULL);
+ self = g_object_new (IDE_TYPE_SESSION, NULL);
- g_file_load_bytes_async (file,
- cancellable,
- on_session_cache_loaded_cb,
- g_steal_pointer (&task));
+ if (!ide_session_load (self, variant, error))
+ g_clear_object (&self);
- IDE_EXIT;
+ return self;
}
-gboolean
-ide_session_restore_finish (IdeSession *self,
- GAsyncResult *result,
- GError **error)
+guint
+ide_session_get_n_items (IdeSession *self)
{
- gboolean ret;
- Restore *r;
- GListModel *list;
- AutosaveGrid *autosave_grid;
-
- IDE_ENTRY;
-
- g_return_val_if_fail (IDE_IS_SESSION (self), FALSE);
- g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
-
- r = ide_task_get_task_data (IDE_TASK (result));
- g_assert (r != NULL);
- list = G_LIST_MODEL (r->grid);
-
- autosave_grid = g_slice_new0 (AutosaveGrid);
- autosave_grid->grid = r->grid;
- autosave_grid->session = self;
- autosave_grid->session_autosave_source = 0;
-
- watch_pages_session_autosave (autosave_grid,
- 0, g_list_model_get_n_items (list));
- g_signal_connect_data (list,
- "items-changed",
- G_CALLBACK (on_grid_items_changed_cb),
- autosave_grid,
- autosave_grid_free,
- 0);
+ g_return_val_if_fail (IDE_IS_SESSION (self), 0);
- ret = ide_task_propagate_boolean (IDE_TASK (result), error);
-
- IDE_RETURN (ret);
+ return self->items->len;
}
-static void
-on_state_saved_to_cache_file_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+/**
+ * ide_session_get_item:
+ * @self: a #IdeSession
+ * @position: the index of the item
+ *
+ * Gets the item at @position.
+ *
+ * Returns: (transfer none) (nullable): The #IdeSessionItem at @position
+ * or %NULL if there is no item at that position.
+ */
+IdeSessionItem *
+ide_session_get_item (IdeSession *self,
+ guint position)
{
- GFile *file = (GFile *)object;
- g_autoptr(GError) error = NULL;
- g_autoptr(IdeTask) task = user_data;
-
- IDE_ENTRY;
+ g_return_val_if_fail (IDE_IS_SESSION (self), NULL);
- g_assert (G_IS_FILE (file));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_TASK (task));
-
- if (!g_file_replace_contents_finish (file, result, NULL, &error))
- ide_task_return_error (task, g_steal_pointer (&error));
- else
- ide_task_return_boolean (task, TRUE);
+ if (position >= self->items->len)
+ return NULL;
- IDE_EXIT;
+ return g_ptr_array_index (self->items, position);
}
-typedef struct {
- IdeTask *task;
- IdePage *page;
-} SavePage;
-
-static void
-save_state_to_disk (IdeSession *self,
- IdeTask *task,
- GVariantBuilder *pages_state)
+void
+ide_session_remove (IdeSession *self,
+ IdeSessionItem *item)
{
- g_autoptr(GVariant) state = NULL;
- g_autoptr(GBytes) bytes = NULL;
- g_autoptr(GFile) file = NULL;
- GCancellable *cancellable;
- IdeContext *context;
- GVariantDict final_dict;
+ guint position;
- IDE_ENTRY;
+ g_return_if_fail (IDE_IS_SESSION (self));
+ g_return_if_fail (IDE_IS_SESSION_ITEM (item));
- g_assert (IDE_IS_SESSION (self));
- g_assert (IDE_IS_TASK (task));
- g_assert (pages_state != NULL);
-
- cancellable = ide_task_get_cancellable (task);
-
- g_variant_dict_init (&final_dict, NULL);
- g_variant_dict_insert (&final_dict, "version", "u", (guint32) 1);
- g_variant_dict_insert_value (&final_dict, "data", g_variant_builder_end (pages_state));
-
- state = g_variant_ref_sink (g_variant_dict_end (&final_dict));
- bytes = g_variant_get_data_as_bytes (state);
-
-#ifdef IDE_ENABLE_TRACE
- {
- g_autofree char *str = g_variant_print (state, TRUE);
- IDE_TRACE_MSG ("Saving session state to %s", str);
- }
-#endif
-
- context = ide_object_get_context (IDE_OBJECT (self));
- file = ide_context_cache_file (context, "session.gvariant", NULL);
-
- if (!ide_task_return_error_if_cancelled (task))
- g_file_replace_contents_bytes_async (file,
- bytes,
- NULL,
- FALSE,
- G_FILE_CREATE_NONE,
- cancellable,
- on_state_saved_to_cache_file_cb,
- g_object_ref (task));
-
- IDE_EXIT;
+ if (g_ptr_array_find (self->items, item, &position))
+ ide_session_remove_at (self, position);
}
-static void
-on_session_addin_page_saved_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+void
+ide_session_remove_at (IdeSession *self,
+ guint position)
{
- IdeSessionAddin *addin = (IdeSessionAddin *)object;
- g_autoptr(GVariant) page_state = NULL;
- SavePage *save_page = user_data;
- g_autoptr(IdeTask) task = save_page->task;
- IdePage *page = save_page->page;
- g_autoptr(GError) error = NULL;
- IdeSession *self;
- Save *s;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SESSION_ADDIN (addin));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_TASK (task));
-
- self = ide_task_get_source_object (task);
- s = ide_task_get_task_data (task);
-
- g_assert (IDE_IS_SESSION (self));
- g_assert (s != NULL);
- g_assert (s->active > 0);
-
- page_state = ide_session_addin_save_page_finish (addin, result, &error);
-
- if (error != NULL)
- g_warning ("Could not save page with addin %s: %s", G_OBJECT_TYPE_NAME (addin), error->message);
-
- if (page_state != NULL)
- {
- guint frame_column, frame_row, frame_depth;
- GVariantDict state_dict;
-
- g_assert (!g_variant_is_floating (page_state));
-
- ide_grid_get_page_position (s->grid, page, &frame_column, &frame_row, &frame_depth);
-
- g_variant_dict_init (&state_dict, NULL);
- g_variant_dict_insert (&state_dict, "column", "u", frame_column);
- g_variant_dict_insert (&state_dict, "row", "u", frame_row);
- g_variant_dict_insert (&state_dict, "depth", "u", frame_depth);
- g_variant_dict_insert (&state_dict, "addin_name", "s", G_OBJECT_TYPE_NAME (addin));
- g_variant_dict_insert (&state_dict, "addin_page_state", "v", page_state);
-
- g_variant_builder_add_value (&s->pages_state, g_variant_dict_end (&state_dict));
- }
-
- g_slice_free (SavePage, save_page);
-
- s->active--;
-
- if (s->active == 0)
- save_state_to_disk (self, task, &s->pages_state);
+ g_return_if_fail (IDE_IS_SESSION (self));
+ g_return_if_fail (position < self->items->len);
- IDE_EXIT;
+ g_ptr_array_remove_index (self->items, position);
}
-static void
-foreach_page_in_grid_save_cb (IdePage *page,
- gpointer user_data)
+void
+ide_session_append (IdeSession *self,
+ IdeSessionItem *item)
{
- IdeTask *task = user_data;
- IdeSessionAddin *addin;
- SavePage *save_page = NULL;
- Save *s;
-
- g_assert (IDE_IS_PAGE (page));
- g_assert (IDE_IS_TASK (task));
-
- s = ide_task_get_task_data (task);
-
- g_assert (s != NULL);
- g_assert (s->addins != NULL);
-
- if (!(addin = find_suitable_addin_for_page (page, s->addins)))
- {
- /* It's not a saveable page. */
- s->active--;
- return;
- }
-
- save_page = g_slice_new0 (SavePage);
- save_page->task = g_object_ref (task);
- save_page->page = page;
+ g_return_if_fail (IDE_IS_SESSION (self));
+ g_return_if_fail (IDE_IS_SESSION_ITEM (item));
- ide_session_addin_save_page_async (addin,
- page,
- ide_task_get_cancellable (task),
- on_session_addin_page_saved_cb,
- g_steal_pointer (&save_page));
+ g_ptr_array_add (self->items, g_object_ref (item));
}
-/**
- * ide_session_save_async:
- * @self: an #IdeSession
- * @grid: an #IdeGrid
- * @cancellable: (nullable): a #GCancellable, or %NULL
- * @callback: a callback to execute upon completion
- * @user_data: user data for @callback
- *
- * This function will save the position and content of the pages in the @grid,
- * which can then be restored with ide_session_restore_async(), asking the
- * content of the pages to the appropriate #IdeSessionAddin.
- */
void
-ide_session_save_async (IdeSession *self,
- IdeGrid *grid,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ide_session_prepend (IdeSession *self,
+ IdeSessionItem *item)
{
- g_autoptr(IdeTask) task = NULL;
- Save *s;
-
- IDE_ENTRY;
-
g_return_if_fail (IDE_IS_SESSION (self));
- g_return_if_fail (IDE_IS_GRID (grid));
- g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- task = ide_task_new (self, cancellable, callback, user_data);
- ide_task_set_source_tag (task, ide_session_save_async);
+ g_return_if_fail (IDE_IS_SESSION_ITEM (item));
- s = g_slice_new0 (Save);
- s->addins = self->addins;
- s->grid = grid;
- s->active = ide_grid_count_pages (s->grid);
-
- g_variant_builder_init (&s->pages_state, G_VARIANT_TYPE ("aa{sv}"));
- ide_task_set_task_data (task, s, save_free);
-
- ide_grid_foreach_page (s->grid,
- foreach_page_in_grid_save_cb,
- task);
-
- g_assert (s != NULL);
-
- /* Save the empty pages state there too because it wouldn't have
- * been done in foreach_page_in_grid_save_cb() since there's no
- * pages to save.
- */
- if (s->active == 0)
- save_state_to_disk (self, task, &s->pages_state);
-
- IDE_EXIT;
+ g_ptr_array_insert (self->items, 0, g_object_ref (item));
}
-gboolean
-ide_session_save_finish (IdeSession *self,
- GAsyncResult *result,
- GError **error)
+void
+ide_session_insert (IdeSession *self,
+ guint position,
+ IdeSessionItem *item)
{
- gboolean ret;
-
- IDE_ENTRY;
-
- g_return_val_if_fail (IDE_IS_SESSION (self), FALSE);
- g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
-
- ret = ide_task_propagate_boolean (IDE_TASK (result), error);
-
- IDE_RETURN (ret);
-}
+ g_return_if_fail (IDE_IS_SESSION (self));
+ g_return_if_fail (IDE_IS_SESSION_ITEM (item));
-IdeSession *
-ide_session_new (void)
-{
- return g_object_new (IDE_TYPE_SESSION, NULL);
+ g_ptr_array_insert (self->items, position, g_object_ref (item));
}
diff --git a/src/libide/gui/ide-session.h b/src/libide/gui/ide-session.h
new file mode 100644
index 000000000..39433cc84
--- /dev/null
+++ b/src/libide/gui/ide-session.h
@@ -0,0 +1,67 @@
+/* ide-session.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This file 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 file 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 Lesser 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/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-session-item.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SESSION (ide_session_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeSession, ide_session, IDE, SESSION, GObject)
+
+IDE_AVAILABLE_IN_ALL
+IdeSession *ide_session_new (void);
+IDE_AVAILABLE_IN_ALL
+void ide_session_append (IdeSession *self,
+ IdeSessionItem *item);
+IDE_AVAILABLE_IN_ALL
+void ide_session_prepend (IdeSession *self,
+ IdeSessionItem *item);
+IDE_AVAILABLE_IN_ALL
+void ide_session_insert (IdeSession *self,
+ guint position,
+ IdeSessionItem *item);
+IDE_AVAILABLE_IN_ALL
+void ide_session_remove (IdeSession *self,
+ IdeSessionItem *item);
+IDE_AVAILABLE_IN_ALL
+void ide_session_remove_at (IdeSession *self,
+ guint position);
+IDE_AVAILABLE_IN_ALL
+guint ide_session_get_n_items (IdeSession *self);
+IDE_AVAILABLE_IN_ALL
+IdeSessionItem *ide_session_get_item (IdeSession *self,
+ guint position);
+IDE_AVAILABLE_IN_ALL
+IdeSession *ide_session_new_from_variant (GVariant *variant,
+ GError **error);
+IDE_AVAILABLE_IN_ALL
+GVariant *ide_session_to_variant (IdeSession *self);
+
+G_END_DECLS
diff --git a/src/libide/gui/libide-gui.h b/src/libide/gui/libide-gui.h
index 332ecfbd8..94616a71d 100644
--- a/src/libide/gui/libide-gui.h
+++ b/src/libide/gui/libide-gui.h
@@ -49,7 +49,6 @@
# include "ide-primary-workspace.h"
# include "ide-run-button.h"
# include "ide-search-popover.h"
-# include "ide-session-addin.h"
# include "ide-shortcut-provider.h"
# include "ide-workbench.h"
# include "ide-workbench-addin.h"
diff --git a/src/libide/gui/meson.build b/src/libide/gui/meson.build
index 91bfbdfdb..067ddaf12 100644
--- a/src/libide/gui/meson.build
+++ b/src/libide/gui/meson.build
@@ -27,7 +27,8 @@ libide_gui_public_headers = [
'ide-primary-workspace.h',
'ide-run-button.h',
'ide-search-popover.h',
- 'ide-session-addin.h',
+ 'ide-session.h',
+ 'ide-session-item.h',
'ide-shortcut-provider.h',
'ide-workbench.h',
'ide-workbench-addin.h',
@@ -51,7 +52,6 @@ libide_gui_private_headers = [
'ide-page-private.h',
'ide-recoloring-private.h',
'ide-search-popover-private.h',
- 'ide-session-private.h',
'ide-shortcut-bundle-private.h',
'ide-shortcut-manager-private.h',
'ide-shortcut-window-private.h',
@@ -71,7 +71,6 @@ libide_gui_private_sources = [
'ide-notification-view.c',
'ide-recoloring.c',
'ide-search-popover.c',
- 'ide-session.c',
'ide-shortcut-bundle.c',
'ide-shortcut-manager.c',
'ide-shortcut-window.c',
@@ -99,7 +98,8 @@ libide_gui_public_sources = [
'ide-panel-position.c',
'ide-primary-workspace.c',
'ide-run-button.c',
- 'ide-session-addin.c',
+ 'ide-session.c',
+ 'ide-session-item.c',
'ide-shortcut-provider.c',
'ide-workbench.c',
'ide-workbench-addin.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]