summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjell Ahlstedt <kjell.ahlstedt@bredband.net>2016-04-27 09:26:33 +0200
committerKjell Ahlstedt <kjell.ahlstedt@bredband.net>2016-04-27 09:26:33 +0200
commit84e597dd50d513ee24235bdfe0ffc79d1a26ea67 (patch)
tree7389a6508f1ad3574da7bb92df2abdf979e186a6
parenta2a5d46d23e3a63262acdcc8c8ba0d23184d62a7 (diff)
downloadglibmm-84e597dd50d513ee24235bdfe0ffc79d1a26ea67.tar.gz
Add Gio::ListModel, ListStoreBase and ListStore<>
Based on work by Murray Cumming <murrayc@murrayc.com> and Marcin Kolny <marcin.kolny@gmail.com> Bug #755307
-rw-r--r--gio/giomm.h2
-rw-r--r--gio/src/filelist.am2
-rw-r--r--gio/src/gio_vfuncs.defs19
-rw-r--r--gio/src/listmodel.ccg23
-rw-r--r--gio/src/listmodel.hg102
-rw-r--r--gio/src/liststore.ccg53
-rw-r--r--gio/src/liststore.hg323
7 files changed, 524 insertions, 0 deletions
diff --git a/gio/giomm.h b/gio/giomm.h
index 6e64e8ce..ad17170f 100644
--- a/gio/giomm.h
+++ b/gio/giomm.h
@@ -89,6 +89,8 @@
#include <giomm/initable.h>
#include <giomm/inputstream.h>
#include <giomm/iostream.h>
+#include <giomm/listmodel.h>
+#include <giomm/liststore.h>
#include <giomm/loadableicon.h>
#include <giomm/memoryinputstream.h>
#include <giomm/memoryoutputstream.h>
diff --git a/gio/src/filelist.am b/gio/src/filelist.am
index 0f494e48..0095128e 100644
--- a/gio/src/filelist.am
+++ b/gio/src/filelist.am
@@ -74,6 +74,8 @@ giomm_files_any_hg = \
initable.hg \
inputstream.hg \
iostream.hg \
+ listmodel.hg \
+ liststore.hg \
loadableicon.hg \
memoryinputstream.hg \
memoryoutputstream.hg \
diff --git a/gio/src/gio_vfuncs.defs b/gio/src/gio_vfuncs.defs
index e9792a97..80261738 100644
--- a/gio/src/gio_vfuncs.defs
+++ b/gio/src/gio_vfuncs.defs
@@ -1184,3 +1184,22 @@
'("GError**" "error")
)
)
+
+; GListModel
+(define-vfunc get_item_type
+ (of-object "GListModel")
+ (return-type "GType")
+)
+
+(define-vfunc get_n_items
+ (of-object "GListModel")
+ (return-type "guint")
+)
+
+(define-vfunc get_item
+ (of-object "GListModel")
+ (return-type "gpointer")
+ (parameters
+ '("guint" "position")
+ )
+)
diff --git a/gio/src/listmodel.ccg b/gio/src/listmodel.ccg
new file mode 100644
index 00000000..b6004fab
--- /dev/null
+++ b/gio/src/listmodel.ccg
@@ -0,0 +1,23 @@
+/* Copyright (C) 2016 The giomm Development Team
+ *
+ * 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 2.1 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
+ * Lesser 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, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gio/gio.h>
+
+namespace Gio
+{
+
+} // namespace Gio
diff --git a/gio/src/listmodel.hg b/gio/src/listmodel.hg
new file mode 100644
index 00000000..3f8cec01
--- /dev/null
+++ b/gio/src/listmodel.hg
@@ -0,0 +1,102 @@
+/* Copyright (C) 2016 The giomm Development Team
+ *
+ * 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 2.1 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
+ * Lesser 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, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glibmm/interface.h>
+#include <gio/gio.h>
+
+_DEFS(giomm,gio)
+_PINCLUDE(glibmm/private/interface_p.h)
+_PINCLUDE(gio/gio.h)
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+typedef struct _GListModelInterface GListModelInterface;
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+
+namespace Gio
+{
+
+/** A dynamic list of objects.
+ *
+ * A ListModel represents a mutable list of
+ * Glib::Objects. Its main intention is as a model for various widgets in
+ * user interfaces, such as list views, but it can also be used as a
+ * convenient method of returning lists of data, with support for
+ * updates.
+ *
+ * Each object in the list may also report changes in itself via some
+ * mechanism (normally the Glib::PropertyProxy<>::signal_changed() signal
+ * of one or more of the object's properties). Taken together
+ * with the signal_items_changed() signal, this provides for a list
+ * that can change its membership, and in which the members can change
+ * their individual properties.
+ *
+ * A good example would be the list of visible wireless network access
+ * points, where each access point can report dynamic properties such as
+ * signal strength.
+ *
+ * It is important to note that the ListModel itself does not report
+ * changes to the individual items. It only reports changes to the list
+ * membership. If you want to observe changes to the objects themselves
+ * then you need to connect signals to the objects that you are
+ * interested in.
+ *
+ * All items in a ListModel are of (or derived from) the same type.
+ * get_item_type() returns that type. The type may be an
+ * interface, in which case all objects in the list must implement it.
+ *
+ * The semantics are close to that of an array:
+ * get_n_items() returns the number of items in the list and
+ * get_object() returns an item at a (0-based) position. In
+ * order to allow implementations to calculate the list length lazily,
+ * you can also iterate over items: starting from 0, repeatedly call
+ * get_object() until it returns nullptr.
+ *
+ * This interface is intended only to be used from a single thread. The
+ * thread in which it is appropriate to use it depends on the particular
+ * implementation, but typically it will be from the thread that owns
+ * the thread-default main context
+ * in effect at the time that the model was created.
+ *
+ * @newin{2,50}
+ */
+class ListModel : public Glib::Interface
+{
+ _CLASS_INTERFACE(ListModel, GListModel, G_LIST_MODEL, GListModelInterface)
+
+protected:
+ _WRAP_METHOD(void items_changed(guint position, guint removed, guint added), g_list_model_items_changed, newin "2,50")
+
+public:
+ _WRAP_METHOD(GType get_item_type() const, g_list_model_get_item_type, newin "2,50")
+ _WRAP_METHOD(guint get_n_items() const, g_list_model_get_n_items, newin "2,50")
+
+ //g_list_model_get_item is useless as long as we have g_list_model_get_object().
+ //It doesn't do anything differently.
+ _IGNORE(g_list_model_get_item)
+
+ _WRAP_METHOD(Glib::RefPtr<Glib::ObjectBase> get_object(guint position), g_list_model_get_object, newin "2,50")
+ _WRAP_METHOD(Glib::RefPtr<const Glib::ObjectBase> get_object(guint position) const, g_list_model_get_object, constversion, newin "2,50")
+
+ _WRAP_SIGNAL(void items_changed(guint position, guint removed, guint added), "items-changed", no_default_handler, newin "2,50")
+
+ _WRAP_VFUNC(GType get_item_type(), "get_item_type")
+ _WRAP_VFUNC(guint get_n_items(), "get_n_items")
+ _WRAP_VFUNC(gpointer get_item(guint position), "get_item")
+};
+
+} // namespace Gio
diff --git a/gio/src/liststore.ccg b/gio/src/liststore.ccg
new file mode 100644
index 00000000..4580c5e1
--- /dev/null
+++ b/gio/src/liststore.ccg
@@ -0,0 +1,53 @@
+/* Copyright (C) 2016 The giomm Development Team
+ *
+ * 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 2.1 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
+ * Lesser 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, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gio/gio.h>
+#include <memory>
+
+namespace
+{
+extern "C"
+{
+int ListStoreBase_CompareDataFunc(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ auto slot = static_cast<Gio::ListStoreBase::SlotCompare*>(user_data);
+
+ const Glib::RefPtr<const Glib::ObjectBase> item_a =
+ Glib::wrap(static_cast<Glib::Object::BaseObjectType*>(const_cast<gpointer>(a)), true);
+ const Glib::RefPtr<const Glib::ObjectBase> item_b =
+ Glib::wrap(static_cast<Glib::Object::BaseObjectType*>(const_cast<gpointer>(b)), true);
+
+ return (*slot)(item_a, item_b);
+}
+}
+} // anonymous namespace
+
+namespace Gio
+{
+void ListStoreBase::splice(guint position, guint n_removals,
+ const std::vector<Glib::RefPtr<Glib::ObjectBase>>& additions)
+{
+ const std::size_t n_additions = additions.size();
+ std::unique_ptr<gpointer[]> g_additions{new gpointer[n_additions]};
+ for (std::size_t i = 0; i < n_additions; i++)
+ {
+ g_additions[i] = additions[i]->gobj();
+ }
+ g_list_store_splice(gobj(), position, n_removals, g_additions.get(), n_additions);
+}
+
+} // namespace Gio
diff --git a/gio/src/liststore.hg b/gio/src/liststore.hg
new file mode 100644
index 00000000..840d7397
--- /dev/null
+++ b/gio/src/liststore.hg
@@ -0,0 +1,323 @@
+/* Copyright (C) 2016 The giomm Development Team
+ *
+ * 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 2.1 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
+ * Lesser 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, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glibmm/object.h>
+#include <giomm/listmodel.h>
+#include <vector>
+#include <type_traits>
+
+_DEFS(giomm,gio)
+_PINCLUDE(glibmm/private/object_p.h)
+
+namespace Gio
+{
+
+/** A simple implementation of Gio::ListModel that stores all items in memory.
+ *
+ * The templated subclass ListStore<> provides better compile-time type safety.
+ *
+ * It provides insertions, deletions, and lookups in logarithmic time
+ * with a fast path for the common case of iterating the list linearly.
+ *
+ * @newin{2,50}
+ */
+class ListStoreBase
+: public Glib::Object,
+ public ListModel
+{
+ _CLASS_GOBJECT(ListStoreBase, GListStore, G_LIST_STORE, Glib::Object, GObject)
+ _IMPLEMENTS_INTERFACE(ListModel)
+ _STRUCT_NOT_HIDDEN
+
+protected:
+ _WRAP_CTOR(ListStoreBase(GType item_type), g_list_store_new)
+
+public:
+ _WRAP_CREATE(GType item_type)
+
+#m4 _CONVERSION(`const Glib::RefPtr<Glib::ObjectBase>&',`gpointer',`($3)->gobj()')
+
+ _WRAP_METHOD(void insert(guint position, const Glib::RefPtr<Glib::ObjectBase>& item), g_list_store_insert, newin "2,50")
+
+ /** A slot that will be called to compare two items.
+ * The slot should return a negative integer if the first item comes before the second,
+ * 0 if they are equal, or a positive integer if the first value comes after the second.
+ * For instance,
+ * @code
+ * int on_compare_item(const Glib::RefPtr<const Glib::ObjectBase>& item1, const Glib::RefPtr<const Glib::ObjectBase>& item2);
+ * @endcode
+ *
+ * @newin{2,50}
+ */
+ using SlotCompare = sigc::slot<int, const Glib::RefPtr<const Glib::ObjectBase>&, const Glib::RefPtr<const Glib::ObjectBase>&>;
+
+ _WRAP_METHOD(guint insert_sorted(const Glib::RefPtr<Glib::ObjectBase>& item,
+ const SlotCompare& slot{compare_func}), g_list_store_insert_sorted,
+ slot_name slot, slot_callback ListStoreBase_CompareDataFunc, no_slot_copy, newin "2,50")
+
+ _WRAP_METHOD(void sort(const SlotCompare& slot{compare_func}), g_list_store_sort,
+ slot_name slot, slot_callback ListStoreBase_CompareDataFunc, no_slot_copy, newin "2,50")
+
+ _WRAP_METHOD(void append(const Glib::RefPtr<Glib::ObjectBase>& item), g_list_store_append, newin "2,50")
+ _WRAP_METHOD(void remove(guint position), g_list_store_remove, newin "2,50")
+ _WRAP_METHOD(void remove_all(), g_list_store_remove_all, newin "2,50")
+
+ /** Removes @a n_removals items and adds @a additions.size() items.
+ * @a additions must contain items of type property_item_type() or derived from it.
+ * Empty RefPtr is not permitted.
+ *
+ * This function is more efficient than insert() and remove(), because it only emits
+ * ListModel::signal_items_changed() once for the change.
+ *
+ * The parameters @a position and @a n_removals must be correct (i.e.
+ * @a position + @a n_removals must be less than or equal to the length of
+ * the list at the time this function is called).
+ *
+ * @newin{2,50}
+ *
+ * @param position The position at which to make the change.
+ * @param n_removals The number of items to remove.
+ * @param additions The items to add.
+ */
+ void splice(guint position, guint n_removals,
+ const std::vector<Glib::RefPtr<Glib::ObjectBase>>& additions);
+ _IGNORE(g_list_store_splice)
+
+ _WRAP_PROPERTY("item-type", GType, newin "2,50")
+
+}; // end class ListStoreBase
+
+/** A simple implementation of Gio::ListModel that stores all items in memory.
+ *
+ * It provides insertions, deletions, and lookups in logarithmic time
+ * with a fast path for the common case of iterating the list linearly.
+ *
+ * @newin{2,50}
+ *
+ * @tparam T_item Base class of the items in the ListStore. All items must
+ * be of type T_item or a type derived from T_item.
+ * T_item must be Glib::Object or a type derived from Glib::Object.
+ */
+template <typename T_item>
+class ListStore : public ListStoreBase
+{
+ static_assert(std::is_base_of<Glib::Object, T_item>::value,
+ "T_item must be Glib::Object or derived from Glib::Object.");
+
+protected:
+ ListStore();
+
+public:
+ static Glib::RefPtr<ListStore> create();
+
+ /** Get the item at @a position.
+ * If @a position is greater than or equal to the number of
+ * items in @a list, an empty Glib::RefPtr is returned.
+ *
+ * An empty Glib::RefPtr is never returned for an index that is less than the length
+ * of the list. See ListModel::get_n_items().
+ *
+ * @newin{2,50}
+ *
+ * @param position The position of the item to fetch.
+ * @return The object at @a position.
+ */
+ Glib::RefPtr<T_item> get_item(guint position);
+
+ /** Get the item at @a position.
+ * If @a position is greater than or equal to the number of
+ * items in @a list, an empty Glib::RefPtr is returned.
+ *
+ * An empty Glib::RefPtr is never returned for an index that is less than the length
+ * of the list. See ListModel::get_n_items().
+ *
+ * @newin{2,50}
+ *
+ * @param position The position of the item to fetch.
+ * @return The object at @a position.
+ */
+ Glib::RefPtr<const T_item> get_item(guint position) const;
+
+ /** Inserts @a item at @a position.
+ * @a item must be of type ListStoreBase::property_item_type() or derived from it.
+ * @a position must be smaller than the length of the list, or equal to it to append.
+ *
+ * Use splice() to insert multiple items at the same time efficiently.
+ *
+ * @newin{2,50}
+ *
+ * @param position The position at which to insert the new item.
+ * @param item The new item.
+ */
+ void insert(guint position, const Glib::RefPtr<T_item>& item);
+
+ /** A slot that will be called to compare two items.
+ * The slot should return a negative integer if the first item comes before the second,
+ * 0 if they are equal, or a positive integer if the first value comes after the second.
+ * For instance,
+ * @code
+ * int on_compare_item(const Glib::RefPtr<const T_item>& item1, const Glib::RefPtr<const T_item>& item2);
+ * @endcode
+ *
+ * @newin{2,50}
+ */
+ using SlotCompare = sigc::slot<int, const Glib::RefPtr<const T_item>&, const Glib::RefPtr<const T_item>&>;
+
+ /** Inserts @a item at a position to be determined by the @a slot.
+ *
+ * The list must already be sorted before calling this function or the
+ * result is undefined. Usually you would approach this by only ever
+ * inserting items by way of this function.
+ *
+ * @newin{2,50}
+ *
+ * @param item The new item.
+ * @param slot Pairwise comparison function for sorting.
+ * @return The position at which @a item was inserted.
+ */
+ guint insert_sorted(const Glib::RefPtr<T_item>& item, const SlotCompare& slot);
+
+ /** Sorts the items according to @a slot.
+ *
+ * @newin{2,50}
+ *
+ * @param slot Pairwise comparison function for sorting.
+ */
+ void sort(const SlotCompare& slot);
+
+ /** Appends @a item.
+ * @a item must be of type ListStoreBase::property_item_type() or derived from it.
+ *
+ * Use splice() to append multiple items at the same time efficiently.
+ *
+ * @newin{2,50}
+ *
+ * @param item The new item.
+ */
+ void append(const Glib::RefPtr<T_item>& item);
+
+ /** Removes @a n_removals items and adds @a additions.size() items.
+ * @a additions must contain items of type ListStoreBase::property_item_type()
+ * or derived from it. Empty RefPtr is not permitted.
+ *
+ * This function is more efficient than insert() and remove(), because it only emits
+ * ListModel::signal_items_changed() once for the change.
+ *
+ * The parameters @a position and @a n_removals must be correct (i.e.
+ * @a position + @a n_removals must be less than or equal to the length of
+ * the list at the time this function is called).
+ *
+ * @newin{2,50}
+ *
+ * @param position The position at which to make the change.
+ * @param n_removals The number of items to remove.
+ * @param additions The items to add.
+ */
+ void splice(guint position, guint n_removals,
+ const std::vector<Glib::RefPtr<T_item>>& additions);
+
+private:
+ static int compare_data_func(gconstpointer a, gconstpointer b, gpointer user_data);
+}; // end class ListStore
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+template <typename T_item>
+ListStore<T_item>::ListStore()
+: ListStoreBase(T_item::get_base_type())
+{ }
+
+template <typename T_item>
+Glib::RefPtr<ListStore<T_item>> ListStore<T_item>::create()
+{
+ return Glib::RefPtr<ListStore<T_item>>(new ListStore<T_item>());
+}
+
+template <typename T_item>
+Glib::RefPtr<T_item> ListStore<T_item>::get_item(guint position)
+{
+ return Glib::RefPtr<T_item>::cast_dynamic(ListModel::get_object(position));
+}
+
+template <typename T_item>
+Glib::RefPtr<const T_item> ListStore<T_item>::get_item(guint position) const
+{
+ return const_cast<ListStore<T_item>*>(this)->get_item(position);
+}
+
+template <typename T_item>
+void ListStore<T_item>::insert(guint position, const Glib::RefPtr<T_item>& item)
+{
+ ListStoreBase::insert(position, item);
+}
+
+template <typename T_item>
+guint ListStore<T_item>::insert_sorted(
+ const Glib::RefPtr<T_item>& item, const SlotCompare& slot)
+{
+ // Use the original slot (not a copy).
+ auto slot_copy = const_cast<SlotCompare*>(&slot);
+
+ return g_list_store_insert_sorted(gobj(), item->gobj(), &compare_data_func, slot_copy);
+}
+
+template <typename T_item>
+void ListStore<T_item>::sort(const SlotCompare& slot)
+{
+ // Use the original slot (not a copy).
+ auto slot_copy = const_cast<SlotCompare*>(&slot);
+
+ g_list_store_sort(gobj(), &compare_data_func, slot_copy);
+}
+
+template <typename T_item>
+void ListStore<T_item>::append(const Glib::RefPtr<T_item>& item)
+{
+ ListStoreBase::append(item);
+}
+
+template <typename T_item>
+void ListStore<T_item>::splice(guint position, guint n_removals,
+ const std::vector<Glib::RefPtr<T_item>>& additions)
+{
+ const std::size_t n_additions = additions.size();
+ std::unique_ptr<gpointer[]> g_additions{new gpointer[n_additions]};
+ for (std::size_t i = 0; i < n_additions; i++)
+ {
+ g_additions[i] = additions[i]->gobj();
+ }
+ g_list_store_splice(gobj(), position, n_removals, g_additions.get(), n_additions);
+}
+
+template <typename T_item>
+int ListStore<T_item>::compare_data_func(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ auto slot = static_cast<SlotCompare*>(user_data);
+
+ // cast_dynamic is necessary if T_item is a user-derived class, such as
+ // class MyObject : public Glib::Object
+ const Glib::RefPtr<const T_item> item_a = Glib::RefPtr<T_item>::cast_dynamic(
+ Glib::wrap(static_cast<typename T_item::BaseObjectType*>(const_cast<gpointer>(a)), true));
+ const Glib::RefPtr<const T_item> item_b = Glib::RefPtr<T_item>::cast_dynamic(
+ Glib::wrap(static_cast<typename T_item::BaseObjectType*>(const_cast<gpointer>(b)), true));
+
+ return (*slot)(item_a, item_b);
+}
+
+#endif // DOXYGEN_SHOULD_SKIP_THIS
+
+} // namespace Gio