[gtkmm: 1/2] Add Gtk::Expression




commit d6594f78bb7cb5d9184d0ff37f6c31e889d4cbb4
Author: Andreas Persson <andreasp56 outlook com>
Date:   Sun Sep 20 10:18:37 2020 +0200

    Add Gtk::Expression
    
    Add binding of gtkexpression. It is implemented as a template with the
    value type as a template parameter. Creating a closure expression from a
    slot, with other expressions as parameters, is possible, and the types
    are checked at compile time.

 .gitignore                     |   4 +
 gtk/gtkmm/meson.build          |   2 +
 gtk/src/dropdown.hg            |   6 +-
 gtk/src/expression.ccg         |  37 +++
 gtk/src/expression.hg          | 633 +++++++++++++++++++++++++++++++++++++++++
 gtk/src/expressionwatch.ccg    |  17 ++
 gtk/src/expressionwatch.hg     |  82 ++++++
 gtk/src/filelist.am            |   2 +
 gtk/src/gtk_docs_override.xml  |  32 +++
 gtk/src/gtk_extra_objects.defs |   6 +
 gtk/src/stringfilter.hg        |  12 +-
 gtk/src/stringsorter.hg        |  14 +-
 tools/m4/convert_gtk.m4        |   3 +
 13 files changed, 836 insertions(+), 14 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 1dd182d3..f31b4327 100644
--- a/.gitignore
+++ b/.gitignore
@@ -295,6 +295,10 @@ gtk/gtkmm/eventcontrollerscroll.cc
 gtk/gtkmm/eventcontrollerscroll.h
 gtk/gtkmm/expander.cc
 gtk/gtkmm/expander.h
+gtk/gtkmm/expression.cc
+gtk/gtkmm/expression.h
+gtk/gtkmm/expressionwatch.cc
+gtk/gtkmm/expressionwatch.h
 gtk/gtkmm/filechooser.cc
 gtk/gtkmm/filechooser.h
 gtk/gtkmm/filechooserbutton.cc
diff --git a/gtk/gtkmm/meson.build b/gtk/gtkmm/meson.build
index 2bade12e..f6519ec0 100644
--- a/gtk/gtkmm/meson.build
+++ b/gtk/gtkmm/meson.build
@@ -104,6 +104,8 @@ gtkmm_any_hg_ccg_basenames = [
   'eventcontrollermotion',
   'eventcontrollerscroll',
   'expander',
+  'expression',
+  'expressionwatch',
   'filechooser',
   'filechooserbutton',
   'filechooserdialog',
diff --git a/gtk/src/dropdown.hg b/gtk/src/dropdown.hg
index 3bf80d95..9a26314b 100644
--- a/gtk/src/dropdown.hg
+++ b/gtk/src/dropdown.hg
@@ -16,6 +16,7 @@
 
 #include <giomm/listmodel.h>
 #include <gtkmm/listitemfactory.h>
+#include <gtkmm/expression.h>
 #include <gtkmm/widget.h>
 
 _DEFS(gtkmm,gtk)
@@ -58,7 +59,7 @@ class GTKMM_API DropDown : public Widget
   _STRUCT_NOT_HIDDEN
 
 public:
-  _CTOR_DEFAULT
+  _WRAP_CTOR(DropDown(const Glib::RefPtr<Gio::ListModel>& model = {}, const 
Glib::RefPtr<Expression<Glib::ustring>>& expression = {}), gtk_drop_down_new)
 
   /** Creates a new %DropDown that is populated with the strings in @a strings.
    *
@@ -84,6 +85,8 @@ public:
   _WRAP_METHOD(void set_list_factory(const Glib::RefPtr<ListItemFactory>& factory), 
gtk_drop_down_set_list_factory)
   _WRAP_METHOD(Glib::RefPtr<ListItemFactory> get_list_factory(), gtk_drop_down_get_list_factory, refreturn)
   _WRAP_METHOD(Glib::RefPtr<const ListItemFactory> get_list_factory() const, gtk_drop_down_get_list_factory, 
refreturn, constversion)
+  _WRAP_METHOD(void set_expression(const Glib::RefPtr<Expression<Glib::ustring>>& expression), 
gtk_drop_down_set_expression)
+  _WRAP_METHOD(Glib::RefPtr<Expression<Glib::ustring>> get_expression() const, gtk_drop_down_get_expression)
 
   _WRAP_METHOD(void set_enable_search(bool enable_search = true), gtk_drop_down_set_enable_search)
   _WRAP_METHOD(bool get_enable_search() const, gtk_drop_down_get_enable_search)
@@ -94,6 +97,7 @@ public:
   _WRAP_PROPERTY("selected", guint)
   _WRAP_PROPERTY("selected-item", Glib::RefPtr<Glib::ObjectBase>)
   _WRAP_PROPERTY("enable-search", bool)
+  _WRAP_PROPERTY("expression", Glib::RefPtr<Expression<Glib::ustring>>)
 };
 
 } // namespace Gtk
diff --git a/gtk/src/expression.ccg b/gtk/src/expression.ccg
new file mode 100644
index 00000000..005e6e46
--- /dev/null
+++ b/gtk/src/expression.ccg
@@ -0,0 +1,37 @@
+/* Copyright (C) 2020 The gtkmm 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/expression.h>
+
+
+namespace Gtk
+{
+
+namespace Expression_Private
+{
+  
+void watch_callback(gpointer data)
+{
+  auto slot = static_cast<sigc::slot<void()>*>(data);
+  (*slot)();
+}
+
+void closure_callback_func()
+{}
+
+} // namespace Expression_Private
+
+} // namespace Gtk
diff --git a/gtk/src/expression.hg b/gtk/src/expression.hg
new file mode 100644
index 00000000..2815f62e
--- /dev/null
+++ b/gtk/src/expression.hg
@@ -0,0 +1,633 @@
+/* Copyright (C) 2020 The gtkmm 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/expressionwatch.h>
+
+_DEFS(gtkmm,gtk)
+
+namespace Gtk
+{
+
+/** Base class for Gtk::Expression.
+ *
+ * @see Gtk::Expression
+ *
+ * @newin{3,98}
+ */
+class GTKMM_API ExpressionBase
+{
+  _CLASS_OPAQUE_REFCOUNTED(ExpressionBase, GtkExpression, NONE, gtk_expression_ref, gtk_expression_unref, 
GTKMM_API)
+  _IGNORE(gtk_expression_ref, gtk_expression_unref, gtk_expression_bind, gtk_expression_evaluate, 
gtk_expression_watch)
+
+public:
+
+  /** For instance: void on_notify();
+   *
+   * Callback called by Gtk::Expression::watch() when the expression
+   * value changes.
+   */
+  using SlotNotify = sigc::slot<void()>;
+
+  _WRAP_METHOD(GType get_value_type() const, gtk_expression_get_value_type)
+  _WRAP_METHOD(bool is_static() const, gtk_expression_is_static)
+};
+
+/** Expressions to values.
+ *
+ * %Gtk::Expression provides a way to describe references to values.
+ *
+ * An important aspect of expressions is that the value can be obtained
+ * from a source that is several steps away. For example, an expression
+ * may describe ‘the value of property A of @a object1, which is itself the
+ * value of a property of @a object2’. And @a object1 may not even exist yet
+ * at the time that the expression is created. This is contrast to
+ * Glib::Binding, which can only create direct connections between
+ * the properties of two objects that must both exist for the duration
+ * of the binding.
+ *
+ * An expression needs to be "evaluated" to obtain the value that it currently
+ * refers to. An evaluation always happens in the context of a current object
+ * called `this` (it mirrors the behavior of object-oriented languages),
+ * which may or may not influence the result of the evaluation. Use
+ * evaluate() for evaluating an expression.
+ *
+ * Various methods for defining expressions exist, from simple constants via
+ * Gtk::ConstantExpression() to looking up properties in an object (even
+ * recursively) via Gtk::PropertyExpression() or providing custom functions
+ * to transform and combine expressions via Gtk::ClosureExpression().
+ *
+ * Here is an example of a complex expression:
+ * ~~~
+ *   color_expr = Gtk::PropertyExpression<Glib::RefPtr<Glib::ObjectBase>>::create(
+ *       Gtk::ListItem::get_type(), "item");
+ *   expression = Gtk::PropertyExpression<Glib::ustring>::create(
+ *       GTK_TYPE_COLOR, color_expr, "name");
+ * ~~~
+ * when evaluated with `this` being a Gtk::ListItem, it will obtain the
+ * "item" property from the Gtk::ListItem, and then obtain the "name" property
+ * from the resulting object (which is assumed to be of type GTK_TYPE_COLOR).
+ *
+ * A more concise way to describe this would be
+ * ~~~
+ *   this->item->name
+ * ~~~
+ *
+ * The most likely place where you will encounter expressions is in the context
+ * of list models and list widgets using them. For example, Gtk::DropDown is
+ * evaluating a %Gtk::Expression to obtain strings from the items in its model
+ * that it can then use to match against the contents of its search entry.
+ * Gtk::StringFilter is using a %Gtk::Expression for similar reasons.
+ *
+ * By default, expressions are not paying attention to changes and evaluation is
+ * just a snapshot of the current state at a given time. To get informed about
+ * changes, an expression needs to be "watched" via a Gtk::ExpressionWatch, which
+ * will cause a callback to be called whenever the value of the expression may
+ * have changed. watch() starts watching an expression, and
+ * Gtk::ExpressionWatch::unwatch() stops.
+ *
+ * Watches can be created for automatically updating the propery of an object,
+ * similar to the Glib::Binding mechanism, by using bind().
+ *
+ * @newin{3,98}
+ */
+template<class T>
+class Expression : public ExpressionBase
+{
+public:
+  using ValueType = T;
+
+  /** Evaluates the given expression and on success returns the result.
+   *
+   * It is possible that expressions cannot be evaluated - for example
+   * when the expression references objects that have been destroyed or
+   * set to <tt>nullptr</tt>. In that case the returned std::optional
+   * will not contain a value.
+   *
+   * @param this_ The this argument for the evaluation.
+   * @return The optional result of the evaluation.
+   */
+  std::optional<T> evaluate(const Glib::RefPtr<Glib::ObjectBase>& this_);
+
+  /** Installs a watch for the expression that calls the @a notify function
+   * whenever the evaluation of the expression may have changed.
+   *
+   * GTK cannot guarantee that the evaluation did indeed change when the @a notify
+   * gets invoked, but it guarantees the opposite: When it did in fact change,
+   * the @a notify will be invoked.
+   *
+   * @param this_ The `this` argument to watch.
+   * @param notify Callback to invoke when the expression changes.
+   * @return The newly installed watch.
+   */
+  Glib::RefPtr<ExpressionWatch<T>> watch(const Glib::RefPtr<Glib::ObjectBase>& this_,
+                                         const SlotNotify& notify);
+
+  /** Bind a target's @a property to the expression.
+   *
+   * The value that the expression evaluates to is set on the target.
+   * This is repeated whenever the expression changes to ensure that
+   * the object's property stays synchronized with the expression.
+   *
+   * If the expression's evaluation fails, target's @a property is not updated.
+   * You can ensure that this doesn't happen by using a fallback
+   * expression.
+   *
+   * @param property Property on the target to bind to.
+   * @param this_ The this argument for the evaluation of the expression.
+   * @return A Gtk::ExpressionWatch.
+   */
+  template<class T2>
+  Glib::RefPtr<ExpressionWatch<T>> bind(const Glib::PropertyProxy<T2>& property,
+                                        const Glib::RefPtr<Glib::ObjectBase>& this_ = nullptr);
+
+  /** @copydoc bind(const Glib::PropertyProxy<T2>&,const Glib::RefPtr<Glib::ObjectBase>&)
+   */
+  template<class T2>
+  Glib::RefPtr<ExpressionWatch<T>> bind(const Glib::PropertyProxy_WriteOnly<T2>& property,
+                                        const Glib::RefPtr<Glib::ObjectBase>& this_ = nullptr);
+};
+
+template<class T>
+class PropertyExpression final : public Expression<T>
+{
+public:
+  /** Get the GType for this class, for use with the underlying GObject type system.
+   */
+  static GType get_type() G_GNUC_CONST;
+
+  /** Creates an expression that looks up a property via the
+   * `this` argument.
+   *
+   * If the resulting object conforms to @a this_type, its property
+   * named @a property_name will be queried.
+   * Otherwise, this expression's evaluation will fail.
+   *
+   * The given @a this_type must have a property with @a property_name.
+   *
+   * @param this_type The type to expect for the this type.
+   * @param property_name Name of the property.
+   * @return A new Gtk::Expression.
+   */
+  static Glib::RefPtr<PropertyExpression> create(GType this_type,
+                                                 const Glib::ustring& property_name);
+
+  /** Creates an expression that looks up a property via the
+   * given @a expression.
+   *
+   * If the resulting object conforms to @a OT, its property
+   * named @a property_name will be queried.
+   * Otherwise, this expression's evaluation will fail.
+   *
+   * The value type of the given @a expression must have a property with
+   * @a property_name.
+   *
+   * @param expression Expression to evaluate to get the object to query.
+   * @param property_name Name of the property.
+   * @return A new Gtk::Expression.
+   */
+  template<class OT>
+  static Glib::RefPtr<PropertyExpression> create(const Glib::RefPtr<OT>& expression,
+                                                 const Glib::ustring& property_name);
+
+  /** Creates an expression that looks up a property via the
+   * given @a expression.
+   *
+   * If the resulting object conforms to @a this_type, its property
+   * named @a property_name will be queried.
+   * Otherwise, this expression's evaluation will fail.
+   *
+   * The given @a this_type must have a property with @a property_name.
+   *
+   * @param this_type The type to expect for the this type.
+   * @param expression Expression to evaluate to get the object to query.
+   * @param property_name Name of the property.
+   * @return A new Gtk::Expression.
+   */
+  template<class OT>
+  static Glib::RefPtr<PropertyExpression> create(GType this_type,
+                                                 const Glib::RefPtr<OT>& expression,
+                                                 const Glib::ustring& property_name);
+};
+
+template<class T>
+class ConstantExpression final : public Expression<T>
+{
+public:
+  /** Get the GType for this class, for use with the underlying GObject type system.
+   */
+  static GType get_type() G_GNUC_CONST;
+
+  /** Creates a Gtk::Expression that evaluates to the object given by
+   * the arguments.
+   *
+   * @param args Arguments to create the object from.
+   * @return A new Gtk::Expression.
+   */
+  template<class... T_Args>
+  static Glib::RefPtr<ConstantExpression<T>> create(T_Args&&... args);
+
+  /** Creates an expression that always evaluates to the given @a value.
+   *
+   * @param value A Value.
+   * @return A new Gtk::Expression.
+   */
+  static Glib::RefPtr<ConstantExpression<T>> create_for_value(const Glib::Value<T>& value);
+};
+
+template<class T>
+class ObjectExpression final : public Expression<T>
+{
+public:
+  /** Get the GType for this class, for use with the underlying GObject type system.
+   */
+  static GType get_type() G_GNUC_CONST;
+
+  /** Creates an expression evaluating to the given @a object with a weak reference.
+   * Once the @a object is disposed, it will fail to evaluate.
+   * This expression is meant to break reference cycles.
+   *
+   * If you want to keep a reference to @a object, use Gtk::ConstantExpression::create().
+   *
+   * @param object %Object to watch.
+   * @return A new Gtk::Expression.
+   */
+  static Glib::RefPtr<ObjectExpression<T>> create(const T& object);
+};
+
+template<class T>
+class ClosureExpression final : public Expression<T>
+{
+public:
+  /** Get the GType for this class, for use with the underlying GObject type system.
+   */
+  static GType get_type() G_GNUC_CONST;
+
+  /** Creates a Gtk::Expression that calls @a slot when it is evaluated.
+   * @a slot is called with the @a this object and the results of evaluating
+   * the @a params expressions.
+   *
+   * Example:
+   * ~~~
+   * Glib::ustring get_string(Glib::RefPtr<Glib::ObjectBase> this_, double a, int b)
+   * {
+   *   return Glib::ustring::sprintf("a is %f, b is %d", a, b);
+   * }
+   *
+   * Glib::RefPtr<Gtk::Expression<double>> expr1 = ...
+   * Glib::RefPtr<Gtk::Expression<int>> expr2 = ...
+   *
+   * Glib::RefPtr<Gtk::Expression<Glib::ustring>> expr3 =
+   *   Gtk::ClosureExpression<Glib::ustring>::create(
+   *     sigc::ptr_fun(get_string), expr1, expr2);
+   * ~~~
+   *
+   * @param slot Slot to call when evaluating this expression.
+   * @param params Expressions for each parameter.
+   * @return A new Gtk::Expression.
+   */
+  template<class S, class... ATs>
+  static Glib::RefPtr<ClosureExpression<T>> create(S slot, const Glib::RefPtr<ATs>&... params);
+
+private:
+  template<class A1, class... ATs>
+  static void fill_params(GtkExpression** gparams, const Glib::RefPtr<A1>& a1,
+                          const Glib::RefPtr<ATs>&... eas);
+
+  static void fill_params(GtkExpression** gparams);
+};
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+namespace Expression_Private
+{
+
+void watch_callback(gpointer data);
+
+template<class T, class... ATs>
+class Invoker
+{
+public:
+  explicit Invoker(const sigc::slot<T(ATs...)>& slot);
+  void invoke(const GValue* param_values, GValue* return_value);
+
+private:
+  sigc::slot<T(ATs...)> the_slot;
+
+  template<class PT>
+  PT eval_param(const GValue* gv);
+
+  template<std::size_t... I>
+  void invoke(const GValue* param_values, GValue* return_value,
+              std::index_sequence<I...>);
+};
+
+
+template<class TI>
+void closure_marshal(GClosure* closure,
+                     GValue* return_value,
+                     guint n_param_values,
+                     const GValue* param_values,
+                     gpointer invocation_hint,
+                     gpointer marshal_data);
+
+void closure_callback_func();
+
+template<class TI>
+void closure_destroy(gpointer data, GClosure* closure);
+
+} // namespace Expression_Private
+
+template<class T>
+std::optional<T> Expression<T>::evaluate(const Glib::RefPtr<Glib::ObjectBase>& this_)
+{
+  Glib::Value<T> value;
+  bool result = gtk_expression_evaluate(gobj(), this_ ? this_->gobj() : nullptr, value.gobj());
+  if (!result)
+    return {};
+
+  if (!G_VALUE_HOLDS(value.gobj(), Glib::Value<T>::value_type()))
+  {
+    g_warning("%s: The evaluated expression has type '%s', expected '%s'", G_STRLOC,
+      g_type_name(G_VALUE_TYPE(value.gobj())), g_type_name(Glib::Value<T>::value_type()));
+    return {};
+  }
+
+  return value.get();
+}
+
+template<class T>
+Glib::RefPtr<ExpressionWatch<T>> Expression<T>::watch(const Glib::RefPtr<Glib::ObjectBase>& this_,
+                                                      const SlotNotify& notify)
+{
+  auto slot_copy = new SlotNotify(notify);
+  return Glib::wrap<T>(gtk_expression_watch(gobj(), this_ ? this_->gobj() : nullptr,
+                                            &Expression_Private::watch_callback, slot_copy,
+                                            &Glib::destroy_notify_delete<SlotNotify>), true);
+}
+
+template<class T>
+template<class T2>
+Glib::RefPtr<ExpressionWatch<T>> Expression<T>::bind(const Glib::PropertyProxy<T2>& property,
+                                                     const Glib::RefPtr<Glib::ObjectBase>& this_)
+{
+  return Glib::wrap<T>(gtk_expression_bind(gobj_copy(), property.get_object()->gobj(),
+                                           property.get_name(),
+                                           this_ ? this_->gobj() : nullptr), true);
+}
+
+template<class T>
+template<class T2>
+Glib::RefPtr<ExpressionWatch<T>> Expression<T>::bind(
+  const Glib::PropertyProxy_WriteOnly<T2>& property,
+  const Glib::RefPtr<Glib::ObjectBase>& this_)
+{
+  return Glib::wrap<T>(gtk_expression_bind(gobj_copy(), property.get_object()->gobj(),
+                                           property.get_name(),
+                                           this_ ? this_->gobj() : nullptr), true);
+}
+
+template<class T>
+GType PropertyExpression<T>::get_type()
+{
+  return gtk_property_expression_get_type();
+}
+
+template<class T>
+Glib::RefPtr<PropertyExpression<T>> PropertyExpression<T>::create(
+  GType this_type, const Glib::ustring& property_name)
+{
+  return Glib::make_refptr_for_instance<Gtk::PropertyExpression<T>>(
+    reinterpret_cast<Gtk::PropertyExpression<T>*>(
+      gtk_property_expression_new(this_type,
+                                  nullptr,
+                                  property_name.c_str())));
+}
+
+template<class T>
+template<class OT>
+Glib::RefPtr<PropertyExpression<T>> PropertyExpression<T>::create(
+  const Glib::RefPtr<OT>& expression, const Glib::ustring& property_name)
+{
+  return Glib::make_refptr_for_instance<Gtk::PropertyExpression<T>>(
+    reinterpret_cast<Gtk::PropertyExpression<T>*>(
+      gtk_property_expression_new(Glib::Value<typename OT::ValueType>::value_type(),
+                                  expression->gobj_copy(),
+                                  property_name.c_str())));
+}
+
+template<class T>
+template<class OT>
+Glib::RefPtr<PropertyExpression<T>> PropertyExpression<T>::create(
+  GType this_type, const Glib::RefPtr<OT>& expression, const Glib::ustring& property_name)
+{
+  return Glib::make_refptr_for_instance<Gtk::PropertyExpression<T>>(
+    reinterpret_cast<Gtk::PropertyExpression<T>*>(
+      gtk_property_expression_new(this_type,
+                                  expression->gobj_copy(),
+                                  property_name.c_str())));
+}
+
+template<class T>
+GType ConstantExpression<T>::get_type()
+{
+  return gtk_constant_expression_get_type();
+}
+
+template<class T>
+template<class... T_Args>
+Glib::RefPtr<ConstantExpression<T>> ConstantExpression<T>::create(T_Args&&... args)
+{
+  Glib::Value<T> value;
+  value.init(Glib::Value<T>::value_type());
+  value.set(T(std::forward<T_Args>(args)...));
+  return create_for_value(value);
+}
+
+template<class T>
+Glib::RefPtr<ConstantExpression<T>> ConstantExpression<T>::create_for_value(
+  const Glib::Value<T>& value)
+{
+  return Glib::make_refptr_for_instance<Gtk::ConstantExpression<T>>(
+    reinterpret_cast<Gtk::ConstantExpression<T>*>(
+      gtk_constant_expression_new_for_value(value.gobj())));
+}
+
+template<class T>
+GType ObjectExpression<T>::get_type()
+{
+  return gtk_object_expression_get_type();
+}
+
+template<class T>
+Glib::RefPtr<ObjectExpression<T>> ObjectExpression<T>::create(const T& object)
+{
+  return Glib::make_refptr_for_instance<Gtk::ObjectExpression<T>>(
+    reinterpret_cast<Gtk::ObjectExpression<T>*>(
+      gtk_object_expression_new(G_OBJECT(object->gobj()))));
+}
+
+template<class T>
+GType ClosureExpression<T>::get_type()
+{
+  return gtk_cclosure_expression_get_type();
+}
+
+template<class T>
+template<class S, class... ATs>
+Glib::RefPtr<ClosureExpression<T>> ClosureExpression<T>::create(S slot,
+                                                                const Glib::RefPtr<ATs>&... params)
+{
+  using TI = Expression_Private::Invoker<T, Glib::RefPtr<Glib::ObjectBase>,
+                                         typename ATs::ValueType...>;
+  auto invoker = new TI(slot);
+  guint n_params = sizeof...(params);
+  auto gparams = new GtkExpression*[n_params];
+  fill_params(gparams, params...);
+
+  auto object = gtk_cclosure_expression_new(
+    Glib::Value<T>::value_type(),
+    Expression_Private::closure_marshal<TI>,
+    n_params,
+    gparams,
+    Expression_Private::closure_callback_func,
+    invoker,
+    Expression_Private::closure_destroy<TI>);
+  delete[] gparams;
+
+  return Glib::make_refptr_for_instance<Gtk::ClosureExpression<T>>(
+    reinterpret_cast<Gtk::ClosureExpression<T>*>(object));
+}
+
+template<class T>
+template<class A1, class... ATs>
+void ClosureExpression<T>::fill_params(GtkExpression** gparams, const Glib::RefPtr<A1>& a1,
+                                       const Glib::RefPtr<ATs>&... eas)
+{
+  gparams[0] = a1->gobj_copy();
+  fill_params(gparams + 1, eas...);
+}
+
+template<class T>
+void ClosureExpression<T>::fill_params(GtkExpression**)
+{}
+
+
+namespace Expression_Private
+{
+
+template<class T, class... ATs>
+Invoker<T, ATs...>::Invoker(const sigc::slot<T(ATs...)>& slot)
+:
+  the_slot(slot)
+{}
+
+template<class T, class... ATs>
+template<class PT>
+PT Invoker<T, ATs...>::eval_param(const GValue* gv)
+{
+  Glib::Value<PT> v;
+  v.init(gv);
+  return v.get();
+}
+
+template<class T, class... ATs>
+template<std::size_t... I>
+void Invoker<T, ATs...>::invoke(const GValue* param_values, GValue* return_value,
+                                std::index_sequence<I...>)
+{
+  T res = the_slot(eval_param<ATs>(&param_values[I])...);
+  Glib::Value<T> rv;
+  rv.init(Glib::Value<T>::value_type());
+  rv.set(res);
+  g_value_copy(rv.gobj(), return_value);
+}
+
+template<class T, class... ATs>
+void Invoker<T, ATs...>::invoke(const GValue* param_values, GValue* return_value)
+{
+  invoke(param_values, return_value, std::make_index_sequence<sizeof...(ATs)>());
+}
+
+
+template<class TI>
+void closure_marshal(GClosure* closure,
+                     GValue* return_value,
+                     guint,
+                     const GValue* param_values,
+                     gpointer,
+                     gpointer)
+{
+  static_cast<TI*>(closure->data)->invoke(param_values, return_value);
+}
+
+
+template<class TI>
+void closure_destroy(gpointer, GClosure* closure)
+{
+  delete static_cast<TI*>(closure->data);
+}
+
+} // namespace Expression_Private
+
+#endif //DOXYGEN_SHOULD_SKIP_THIS
+
+} // namespace Gtk
+
+
+namespace Glib
+{
+
+template<class T>
+RefPtr<Gtk::Expression<T>> wrap(GtkExpression* object, bool take_copy = false)
+{
+  if (take_copy && object)
+    gtk_expression_ref(object);
+
+  return Glib::make_refptr_for_instance<Gtk::Expression<T>>(
+    reinterpret_cast<Gtk::Expression<T>*>(object));
+}
+
+template<class T>
+class Value<RefPtr<Gtk::Expression<T>>> : public ValueBase
+{
+public:
+  using CppType = RefPtr<Gtk::Expression<T>>;
+
+  static GType value_type();
+
+  void set(const CppType& data);
+  CppType get() const;
+};
+
+template<class T>
+GType Value<RefPtr<Gtk::Expression<T>>>::value_type()
+{
+  return gtk_expression_get_type();
+}
+
+template<class T>
+void Value<RefPtr<Gtk::Expression<T>>>::set(const CppType& data)
+{
+  gtk_value_set_expression(&gobject_, const_cast<GtkExpression*>(data->gobj()));
+}
+
+template<class T>
+typename Value<RefPtr<Gtk::Expression<T>>>::CppType Value<RefPtr<Gtk::Expression<T>>>::get() const
+{
+  return wrap<T>(gtk_value_get_expression(&gobject_), true);
+}
+
+} // namespace Glib
diff --git a/gtk/src/expressionwatch.ccg b/gtk/src/expressionwatch.ccg
new file mode 100644
index 00000000..57b87fdc
--- /dev/null
+++ b/gtk/src/expressionwatch.ccg
@@ -0,0 +1,17 @@
+/* Copyright (C) 2020 The gtkmm 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtk/gtk.h>
diff --git a/gtk/src/expressionwatch.hg b/gtk/src/expressionwatch.hg
new file mode 100644
index 00000000..78308d22
--- /dev/null
+++ b/gtk/src/expressionwatch.hg
@@ -0,0 +1,82 @@
+/* Copyright (C) 2020 The gtkmm 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, see <http://www.gnu.org/licenses/>.
+ */
+
+_CONFIGINCLUDE(gtkmmconfig.h)
+
+#include <glibmm/object.h>
+#include <gtk/gtk.h>
+
+_DEFS(gtkmm,gtk)
+
+namespace Gtk
+{
+class GTKMM_API ExpressionWatchBase
+{
+  _CLASS_OPAQUE_REFCOUNTED(ExpressionWatchBase, GtkExpressionWatch, NONE, gtk_expression_watch_ref, 
gtk_expression_watch_unref, GTKMM_API)
+  _IGNORE(gtk_expression_watch_ref, gtk_expression_watch_unref, gtk_expression_watch_evaluate)
+
+public:
+  _WRAP_METHOD(void unwatch(), gtk_expression_watch_unwatch)
+};
+
+template<class T>
+class GTKMM_API ExpressionWatch final : public ExpressionWatchBase
+{
+public:
+  /** Evaluates the watched expression and on success returns the result.
+   *
+   * This is equivalent to calling Gtk::Expression::evaluate() with the
+   * expression and this pointer originally used to create the watch.
+   *
+   * @return The optional result of the evaluation.
+   */
+  std::optional<T> evaluate();
+};
+
+template<class T>
+std::optional<T> ExpressionWatch<T>::evaluate()
+{
+  Glib::Value<T> value;
+  bool result = gtk_expression_watch_evaluate(gobj(), value.gobj());
+  if (!result)
+    return {};
+
+  if (!G_VALUE_HOLDS(value.gobj(), Glib::Value<T>::value_type()))
+  {
+    g_warning("%s: The evaluated expression has type '%s', expected '%s'", G_STRLOC,
+      g_type_name(G_VALUE_TYPE(value.gobj())), g_type_name(Glib::Value<T>::value_type()));
+    return {};
+  }
+
+  return value.get();
+}
+
+} // namespace Gtk
+
+namespace Glib
+{
+
+template<class T>
+RefPtr<Gtk::ExpressionWatch<T>> wrap(GtkExpressionWatch* object, bool take_copy = false)
+{
+  if (take_copy && object)
+    gtk_expression_watch_ref(object);
+
+  return Glib::make_refptr_for_instance<Gtk::ExpressionWatch<T>>(
+    reinterpret_cast<Gtk::ExpressionWatch<T>*>(object));
+}
+
+} // namespace Glib
diff --git a/gtk/src/filelist.am b/gtk/src/filelist.am
index 816a091c..400b3647 100644
--- a/gtk/src/filelist.am
+++ b/gtk/src/filelist.am
@@ -89,6 +89,8 @@ gtkmm_files_any_hg =          \
        eventcontrollermotion.hg                \
        eventcontrollerscroll.hg                \
        expander.hg             \
+       expression.hg \
+       expressionwatch.hg \
        filechooser.hg          \
        filechooserbutton.hg    \
        filechooserdialog.hg    \
diff --git a/gtk/src/gtk_docs_override.xml b/gtk/src/gtk_docs_override.xml
index e5b7fe53..e41a606c 100644
--- a/gtk/src/gtk_docs_override.xml
+++ b/gtk/src/gtk_docs_override.xml
@@ -1414,6 +1414,38 @@ constructed by the Gtk::Builder instance.
 </return>
 </function>
 
+<function name="gtk_expression_get_value_type">
+<return>The type returned from Gtk::Expression::evaluate()
+</return>
+</function>
+
+<function name="gtk_expression_is_static">
+<description>Checks if the expression is static.
+
+A static expression will never change its result when 
+Gtk::Expression::evaluate() is called on it with the same arguments.
+
+That means a call to Gtk::Expression::watch() is not necessary because
+it will never trigger a notify.
+</description>
+</function>
+
+<function name="gtk_expression_watch_unwatch">
+<description>
+Stops watching an expression that was established via Gtk::Expression::watch().
+</description>
+</function>
+
+<function name="gtk_expression_watch_evaluate">
+<description>
+Evaluates the watched expression and on success stores the result
+in @value.
+
+This is equivalent to calling Gtk::Expression::evaluate() with the
+expression and this pointer originally used to create @watch.
+</description>
+</function>
+
 <!-- TODO: Remove this signal description when gtk's description contains -->
 <!-- the correct number of parameters (no @keyval). -->
 <signal name="GtkEventControllerKey::modifiers">
diff --git a/gtk/src/gtk_extra_objects.defs b/gtk/src/gtk_extra_objects.defs
index 4ac9dc95..05151ad2 100644
--- a/gtk/src/gtk_extra_objects.defs
+++ b/gtk/src/gtk_extra_objects.defs
@@ -138,6 +138,12 @@
   (gtype-id "GTK_TYPE_DRAG_SOURCE")
 )
 
+(define-object DropDown
+  (in-module "Gtk")
+  (c-name "GtkDropDown")
+  (gtype-id "GTK_TYPE_DROP_DOWN")
+)
+
 (define-object DropTarget
   (in-module "Gtk")
   (c-name "GtkDropTarget")
diff --git a/gtk/src/stringfilter.hg b/gtk/src/stringfilter.hg
index 53493bd8..90dc5700 100644
--- a/gtk/src/stringfilter.hg
+++ b/gtk/src/stringfilter.hg
@@ -15,6 +15,7 @@
  */
 
 #include <gtkmm/filter.h>
+#include <gtkmm/expression.h>
 
 _DEFS(gtkmm,gtk)
 _PINCLUDE(gtkmm/private/filter_p.h)
@@ -39,24 +40,23 @@ class GTKMM_API StringFilter : public Filter
   _STRUCT_NOT_HIDDEN
 
 protected:
-  //TODO: change this when GtkExpression has been wrapped
-  _WRAP_CTOR(StringFilter(GtkExpression* expression), gtk_string_filter_new)
+  _WRAP_CTOR(StringFilter(const Glib::RefPtr<Expression<Glib::ustring>>& expression), gtk_string_filter_new)
 
 public:
   _WRAP_ENUM(MatchMode, GtkStringFilterMatchMode, decl_prefix GTKMM_API)
 
-  _WRAP_CREATE(GtkExpression* expression)
+  _WRAP_CREATE(const Glib::RefPtr<Expression<Glib::ustring>>& expression)
 
   _WRAP_METHOD(Glib::ustring get_search() const, gtk_string_filter_get_search)
   _WRAP_METHOD(void set_search(const Glib::ustring& search), gtk_string_filter_set_search)
-  _WRAP_METHOD(GtkExpression* get_expression() const, gtk_string_filter_get_expression)
-  _WRAP_METHOD(void set_expression(GtkExpression* expression), gtk_string_filter_set_expression)
+  _WRAP_METHOD(Glib::RefPtr<Expression<Glib::ustring>> get_expression() const, 
gtk_string_filter_get_expression)
+  _WRAP_METHOD(void set_expression(const Glib::RefPtr<Expression<Glib::ustring>>& expression), 
gtk_string_filter_set_expression)
   _WRAP_METHOD(bool get_ignore_case() const, gtk_string_filter_get_ignore_case)
   _WRAP_METHOD(void set_ignore_case(bool ignore_case = true), gtk_string_filter_set_ignore_case)
   _WRAP_METHOD(MatchMode get_match_mode() const, gtk_string_filter_get_match_mode)
   _WRAP_METHOD(void set_match_mode(MatchMode mode), gtk_string_filter_set_match_mode)
 
-  _WRAP_PROPERTY("expression", GtkExpression*)
+  _WRAP_PROPERTY("expression", Glib::RefPtr<Expression<Glib::ustring>>)
   _WRAP_PROPERTY("ignore-case", bool)
   _WRAP_PROPERTY("match-mode", MatchMode)
   _WRAP_PROPERTY("search", Glib::ustring)
diff --git a/gtk/src/stringsorter.hg b/gtk/src/stringsorter.hg
index 13254d0b..c3e5ee9c 100644
--- a/gtk/src/stringsorter.hg
+++ b/gtk/src/stringsorter.hg
@@ -15,6 +15,7 @@
  */
 
 #include <gtkmm/sorter.h>
+#include <gtkmm/expression.h>
 
 _DEFS(gtkmm,gtk)
 _PINCLUDE(gtkmm/private/sorter_p.h)
@@ -22,7 +23,7 @@ _PINCLUDE(gtkmm/private/sorter_p.h)
 namespace Gtk
 {
 
-/** Sort by comparing strings
+/** Sort by comparing strings.
  *
  * %Gtk::StringSorter is a Gtk::Sorter that compares strings. It does the
  * comparison in a linguistically correct way using the current locale by
@@ -40,18 +41,17 @@ class GTKMM_API StringSorter : public Sorter
   _STRUCT_NOT_HIDDEN
 
 protected:
-  //TODO: change this when GtkExpression has been wrapped
-  _WRAP_CTOR(StringSorter(GtkExpression* expression), gtk_string_sorter_new)
+  _WRAP_CTOR(StringSorter(const Glib::RefPtr<Expression<Glib::ustring>>& expression), gtk_string_sorter_new)
 
 public:
-  _WRAP_CREATE(GtkExpression* expression)
+  _WRAP_CREATE(const Glib::RefPtr<Expression<Glib::ustring>>& expression)
 
-  _WRAP_METHOD(GtkExpression* get_expression() const, gtk_string_sorter_get_expression)
-  _WRAP_METHOD(void set_expression(GtkExpression* expression), gtk_string_sorter_set_expression)
+  _WRAP_METHOD(Glib::RefPtr<Expression<Glib::ustring>> get_expression() const, 
gtk_string_sorter_get_expression)
+  _WRAP_METHOD(void set_expression(const Glib::RefPtr<Expression<Glib::ustring>>& expression), 
gtk_string_sorter_set_expression)
   _WRAP_METHOD(bool get_ignore_case() const, gtk_string_sorter_get_ignore_case)
   _WRAP_METHOD(void set_ignore_case(bool ignore_case = true), gtk_string_sorter_set_ignore_case)
 
-  _WRAP_PROPERTY("expression", GtkExpression*)
+  _WRAP_PROPERTY("expression", Glib::RefPtr<Expression<Glib::ustring>>)
   _WRAP_PROPERTY("ignore-case", bool)
 };
 
diff --git a/tools/m4/convert_gtk.m4 b/tools/m4/convert_gtk.m4
index b1e9a4e8..7dccd88c 100644
--- a/tools/m4/convert_gtk.m4
+++ b/tools/m4/convert_gtk.m4
@@ -604,3 +604,6 @@ _CONVERSION(`GtkSorter*',`Glib::RefPtr<Sorter>',`Glib::wrap($3)')
 _CONVERSION(`GtkTreeListRow*',`Glib::RefPtr<TreeListRow>',`Glib::wrap($3)')
 _CONVERSION(`const Glib::RefPtr<Filter>&', `GtkFilter*', __CONVERT_REFPTR_TO_P)
 _CONVERSION(`GtkFilter*',`Glib::RefPtr<Filter>',`Glib::wrap($3)')
+
+_CONVERSION(`const Glib::RefPtr<Expression<Glib::ustring>>&',`GtkExpression*',`(($3) ? ($3)->gobj() : 
nullptr)')
+_CONVERSION(`GtkExpression*',`Glib::RefPtr<Expression<Glib::ustring>>',`Glib::wrap<Glib::ustring>($3)')


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]