#include "gtkshortcutcontrollerprivate.h"
#include "gtkeventcontrollerprivate.h"
+#include "gtkintl.h"
#include "gtkshortcut.h"
+#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
#include <gdk/gdk.h>
GtkEventController parent_instance;
GSList *shortcuts;
+ GtkShortcutScope scope;
guint run_class : 1;
+ guint run_managed : 1;
};
struct _GtkShortcutControllerClass
GtkEventControllerClass parent_class;
};
+enum {
+ PROP_0,
+ PROP_SCOPE,
+
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
G_DEFINE_TYPE (GtkShortcutController, gtk_shortcut_controller,
GTK_TYPE_EVENT_CONTROLLER)
+static gboolean
+gtk_shortcut_controller_is_rooted (GtkShortcutController *self)
+{
+ GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self));
+
+ if (widget == NULL)
+ return FALSE;
+
+ return gtk_widget_get_root (widget) != NULL;
+}
+
+static void
+gtk_shortcut_controller_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (object);
+
+ switch (prop_id)
+ {
+ case PROP_SCOPE:
+ gtk_shortcut_controller_set_scope (self, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_shortcut_controller_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (object);
+
+ switch (prop_id)
+ {
+ case PROP_SCOPE:
+ g_value_set_enum (value, self->scope);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
static void
gtk_shortcut_controller_dispose (GObject *object)
{
}
static gboolean
-gtk_shortcut_controller_handle_event (GtkEventController *controller,
- GdkEvent *event,
- double x,
- double y)
+gtk_shortcut_controller_run_controllers (GtkEventController *controller,
+ GdkEvent *event,
+ double x,
+ double y)
{
GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller);
GtkWidget *widget;
}
}
+ if (self->run_managed)
+ {
+ GtkPropagationPhase current_phase = gtk_event_controller_get_propagation_phase (controller);
+ widget = gtk_event_controller_get_widget (controller);
+
+ for (l = g_object_get_data (G_OBJECT (widget), "gtk-shortcut-controllers"); l; l = l->next)
+ {
+ if (gtk_event_controller_get_propagation_phase (l->data) != current_phase)
+ continue;
+
+ if (gtk_shortcut_controller_run_controllers (l->data, event, x, y))
+ return TRUE;
+ }
+ }
+
return FALSE;
}
+static gboolean
+gtk_shortcut_controller_handle_event (GtkEventController *controller,
+ GdkEvent *event,
+ double x,
+ double y)
+{
+ GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller);
+
+ if (self->scope != GTK_SHORTCUT_SCOPE_LOCAL)
+ return FALSE;
+
+ return gtk_shortcut_controller_run_controllers (controller, event, x, y);
+}
+
+static void
+gtk_shortcut_controller_set_widget (GtkEventController *controller,
+ GtkWidget *widget)
+{
+ GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller);
+
+ GTK_EVENT_CONTROLLER_CLASS (gtk_shortcut_controller_parent_class)->set_widget (controller, widget);
+
+ if (_gtk_widget_get_root (widget))
+ gtk_shortcut_controller_root (self);
+}
+
+static void
+gtk_shortcut_controller_unset_widget (GtkEventController *controller)
+{
+ GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller);
+ GtkWidget *widget = gtk_event_controller_get_widget (controller);
+
+ if (_gtk_widget_get_root (widget))
+ gtk_shortcut_controller_unroot (self);
+
+ GTK_EVENT_CONTROLLER_CLASS (gtk_shortcut_controller_parent_class)->unset_widget (controller);
+}
+
static void
gtk_shortcut_controller_class_init (GtkShortcutControllerClass *klass)
{
- GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
object_class->dispose = gtk_shortcut_controller_dispose;
+ object_class->set_property = gtk_shortcut_controller_set_property;
+ object_class->get_property = gtk_shortcut_controller_get_property;
+
controller_class->handle_event = gtk_shortcut_controller_handle_event;
+ controller_class->set_widget = gtk_shortcut_controller_set_widget;
+ controller_class->unset_widget = gtk_shortcut_controller_unset_widget;
+
+ /**
+ * GtkShortcutController:scope:
+ *
+ * What scope the shortcuts will be handled in.
+ */
+ properties[PROP_SCOPE] =
+ g_param_spec_enum ("scope",
+ P_("Scope"),
+ P_("What scope the shortcuts will be handled in"),
+ GTK_TYPE_SHORTCUT_SCOPE,
+ GTK_SHORTCUT_SCOPE_LOCAL,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
}
static void
{
}
+static void
+complain_if_reached (gpointer should_be_gone)
+{
+ g_critical ("Shortcut controllers failed to clean up.");
+}
+
+void
+gtk_shortcut_controller_root (GtkShortcutController *self)
+{
+ GtkWidget *attach;
+ GSList *controllers;
+
+ switch (self->scope)
+ {
+ case GTK_SHORTCUT_SCOPE_LOCAL:
+ return;
+
+ case GTK_SHORTCUT_SCOPE_MANAGED:
+ case GTK_SHORTCUT_SCOPE_GLOBAL:
+ attach = GTK_WIDGET (gtk_widget_get_root (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self))));
+ break;
+
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+
+ controllers = g_object_steal_data (G_OBJECT (attach), "gtk-shortcut-controllers");
+ controllers = g_slist_prepend (controllers, g_object_ref (self));
+ g_object_set_data_full (G_OBJECT (attach), "gtk-shortcut-controllers", controllers, complain_if_reached);
+}
+
+void
+gtk_shortcut_controller_unroot (GtkShortcutController *self)
+{
+ GtkWidget *attach;
+ GSList *controllers;
+
+ switch (self->scope)
+ {
+ case GTK_SHORTCUT_SCOPE_LOCAL:
+ return;
+
+ case GTK_SHORTCUT_SCOPE_MANAGED:
+ case GTK_SHORTCUT_SCOPE_GLOBAL:
+ attach = GTK_WIDGET (gtk_widget_get_root (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self))));
+ break;
+
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+
+ controllers = g_object_steal_data (G_OBJECT (attach), "gtk-shortcut-controllers");
+ controllers = g_slist_remove (controllers, self);
+ if (controllers)
+ g_object_set_data_full (G_OBJECT (attach), "gtk-shortcut-controllers", controllers, complain_if_reached);
+ g_object_unref (self);
+}
+
GtkEventController *
gtk_shortcut_controller_new (void)
{
controller->run_class = run_class;
}
+void
+gtk_shortcut_controller_set_run_managed (GtkShortcutController *controller,
+ gboolean run_managed)
+{
+ controller->run_managed = run_managed;
+}
+
/**
* gtk_shortcut_controller_add_shortcut:
* @self: the controller
self->shortcuts = g_slist_delete_link (self->shortcuts, l);
g_object_unref (shortcut);
}
+
+/**
+ * gtk_shortcut_controller_set_scope:
+ * @self: a #GtkShortcutController
+ * @scope: the new scope to use
+ *
+ * Sets the controller to have the given @scope.
+ *
+ * The scope allows shortcuts to be activated outside of the normal
+ * event propagation. In particular, it allows installing global
+ * keyboard shortcuts that can be activated even when a widget does
+ * not have focus.
+ **/
+void
+gtk_shortcut_controller_set_scope (GtkShortcutController *self,
+ GtkShortcutScope scope)
+{
+ gboolean rooted;
+
+ g_return_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self));
+
+ if (self->scope == scope)
+ return;
+
+ rooted = gtk_shortcut_controller_is_rooted (self);
+
+ if (rooted)
+ gtk_shortcut_controller_unroot (self);
+
+ self->scope = scope;
+
+ if (rooted)
+ gtk_shortcut_controller_root (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SCOPE]);
+}
+
+/**
+ * gtk_shortcut_controller_get_scope:
+ * @self: a #GtkShortcutController
+ *
+ * Gets the scope for when this controller activates its shortcuts. See
+ * gtk_shortcut_controller_set_scope() for details.
+ *
+ * Returns: the controller's scope
+ **/
+GtkShortcutScope
+gtk_shortcut_controller_get_scope (GtkShortcutController *self)
+{
+ g_return_val_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self), GTK_SHORTCUT_SCOPE_LOCAL);
+
+ return self->scope;
+}
+
static void
gtk_widget_real_root (GtkWidget *widget)
{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+ GList *l;
+
gtk_widget_forall (widget, (GtkCallback) gtk_widget_root, NULL);
+
+ for (l = priv->event_controllers; l; l = l->next)
+ {
+ if (GTK_IS_SHORTCUT_CONTROLLER (l->data))
+ gtk_shortcut_controller_root (GTK_SHORTCUT_CONTROLLER (l->data));
+ }
}
static void
gtk_widget_real_unroot (GtkWidget *widget)
{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+ GList *l;
+
+ for (l = priv->event_controllers; l; l = l->next)
+ {
+ if (GTK_IS_SHORTCUT_CONTROLLER (l->data))
+ gtk_shortcut_controller_unroot (GTK_SHORTCUT_CONTROLLER (l->data));
+ }
+
gtk_widget_forall (widget, (GtkCallback) gtk_widget_unroot, NULL);
}
gtk_css_node_set_name (priv->cssnode, GTK_WIDGET_CLASS (g_class)->priv->css_name);
if (g_type_is_a (G_TYPE_FROM_CLASS (g_class), GTK_TYPE_ROOT))
- priv->root = (GtkRoot *) widget;
+ {
+ priv->root = (GtkRoot *) widget;
+
+ controller = gtk_shortcut_controller_new ();
+ gtk_shortcut_controller_set_run_managed (GTK_SHORTCUT_CONTROLLER (controller), TRUE);
+ gtk_widget_add_controller (widget, controller);
+
+ controller = gtk_shortcut_controller_new ();
+ gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
+ gtk_shortcut_controller_set_run_managed (GTK_SHORTCUT_CONTROLLER (controller), TRUE);
+ gtk_widget_add_controller (widget, controller);
+ }
layout_manager_type = gtk_widget_class_get_layout_manager_type (g_class);
if (layout_manager_type != G_TYPE_INVALID)