2 * This file is part of GtkHotkey.
3 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
5 * GtkHotkey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GtkHotkey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with GtkHotkey. If not, see <http://www.gnu.org/licenses/>.
19 #include "gtk-hotkey-info.h"
20 #include "gtk-hotkey-error.h"
21 #include "gtk-hotkey-listener.h"
23 struct _GtkHotkeyInfoPrivate {
29 GtkHotkeyListener *listener;
31 #define GTK_HOTKEY_INFO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_HOTKEY_TYPE_INFO, GtkHotkeyInfoPrivate))
34 GTK_HOTKEY_INFO_DUMMY_PROPERTY,
35 GTK_HOTKEY_INFO_BOUND,
36 GTK_HOTKEY_INFO_APPLICATION_ID,
37 GTK_HOTKEY_INFO_KEY_ID,
38 GTK_HOTKEY_INFO_APP_INFO,
39 GTK_HOTKEY_INFO_SIGNATURE,
40 GTK_HOTKEY_INFO_DESCRIPTION,
49 guint info_signals[LAST_SIGNAL] = { 0 };
51 static gpointer gtk_hotkey_info_parent_class = NULL;
53 static void gtk_hotkey_info_finalize (GObject * obj);
56 * SECTION:gtk-hotkey-info
57 * @short_description: Primary representation of a hotkey
58 * @see_also: #GtkHotkeyRegistry
60 * #GtkHotkeyInfo is the primary object around which the GtkHotkey library
63 * Hotkeys are stored and managed via a #GtkHotkeyRegistry, while the actual
64 * binding and listening for key-presses is done by a #GtkHotkeyListener.
69 * gtk_hotkey_info_bind:
70 * @self: The hotkey to bind
71 * @error: Place to return a #GError, or %NULL to ignore
73 * Register the hotkey with the system. The #GtkHotkeyInfo::activated signal
74 * will now be emitted when the user presses the keyboard combination
75 * matching the hotkey's signature.
77 * Returns: %TRUE on success, and %FALSE on error in which case @error
81 gtk_hotkey_info_bind (GtkHotkeyInfo* self, GError **error)
85 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), FALSE);
87 if (gtk_hotkey_info_is_bound(self)) {
88 g_set_error (error, GTK_HOTKEY_LISTENER_ERROR,
89 GTK_HOTKEY_LISTENER_ERROR_BIND,
90 "Can not bind hotkey '%s' with signature '%s'. "
91 "It is already bound",
92 gtk_hotkey_info_get_key_id(self),
93 gtk_hotkey_info_get_signature(self));
97 if (!self->priv->listener)
98 self->priv->listener = gtk_hotkey_listener_get_default ();
100 g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(self->priv->listener), FALSE);
102 result = gtk_hotkey_listener_bind_hotkey (self->priv->listener, self, error);
104 g_object_unref (self->priv->listener);
105 self->priv->listener = NULL;
109 g_object_notify (G_OBJECT(self), "bound");
115 * gtk_hotkey_info_unbind
116 * @self: The hotkey to unbind
117 * @error: Place to return a #GError, or %NULL to ignore
118 * @returns: %TRUE on success, and %FALSE on error in which case @error
121 * Remove the hotkey binding from the system. The #GtkHotkeyInfo::activated
122 * signal will no longer be emitted when the hotkey is pressed.
125 gtk_hotkey_info_unbind (GtkHotkeyInfo* self, GError **error)
129 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), FALSE);
131 if (!gtk_hotkey_info_is_bound(self)) {
132 g_set_error (error, GTK_HOTKEY_LISTENER_ERROR,
133 GTK_HOTKEY_LISTENER_ERROR_UNBIND,
134 "Can not unbind hotkey '%s' with signature '%s'."
136 gtk_hotkey_info_get_key_id(self),
137 gtk_hotkey_info_get_signature(self));
141 g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(self->priv->listener), FALSE);
143 result = gtk_hotkey_listener_unbind_hotkey (self->priv->listener, self,
146 g_object_unref (self->priv->listener);
147 self->priv->listener = NULL;
150 g_object_notify (G_OBJECT(self), "bound");
156 * gtk_hotkey_info_is_bound
157 * @self: The hotkey to inspect
158 * @returns: %TRUE if gtk_hotkey_info_bind() has been called and returned %TRUE
161 * Check whether the hotkey has been successfully bound to a #GtkHotkeyListener.
164 gtk_hotkey_info_is_bound (GtkHotkeyInfo* self)
166 return (self->priv->listener != NULL);
170 * gtk_hotkey_info_get_application_id
173 * Get the unique system identifier for the hotkey. See
174 * #GtkHotkeyInfo:application-id for details.
177 gtk_hotkey_info_get_application_id (GtkHotkeyInfo* self)
179 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
180 return self->priv->app_id;
184 * gtk_hotkey_info_get_key_id
187 * Get the identifier the owning application use to identify this hotkey.
188 * See #GtkHotkeyInfo:key-id for details.
191 gtk_hotkey_info_get_key_id (GtkHotkeyInfo* self)
193 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
194 return self->priv->key_id;
198 * gtk_hotkey_info_get_app_info
201 * Not to be confused with the value of the #GtkHotkeyInfo:application-id
202 * property. The hotkey can be associated with an installed desktop application
203 * via a #GAppInfo. This is not mandatory and this method returns %NULL
204 * if no desktop application has been associated with this hotkey.
206 * See the #GtkHotkeyInfo:app-info property for details.
209 gtk_hotkey_info_get_app_info (GtkHotkeyInfo* self)
211 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
212 return self->priv->app_info;
216 * gtk_hotkey_info_get_signature
219 * Get the keyboard signature of the hotkey. This could for example be
220 * '<Alt>F3' or '<Control><Shift>G'.
223 gtk_hotkey_info_get_signature (GtkHotkeyInfo* self)
225 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
226 return self->priv->signature;
230 * gtk_hotkey_info_get_description
232 * @returns: The description of the hotkey or %NULL if none is set
234 * Get the free form description of the hotkey. The description is not guaranteed
235 * to be set and may be %NULL.
237 * FIXME: Do we need to take i18n into account here?
240 gtk_hotkey_info_get_description (GtkHotkeyInfo* self)
242 g_return_val_if_fail (GTK_HOTKEY_IS_INFO(self), NULL);
243 return self->priv->description;
247 * gtk_hotkey_info_set_description
250 * Set a description for the hotkey. See also gtk_hotkey_info_get_description().
253 gtk_hotkey_info_set_description (GtkHotkeyInfo* self, const gchar *description)
255 g_return_if_fail (GTK_HOTKEY_IS_INFO(self));
256 g_object_set (self, "description", description, NULL);
260 * gtk_hotkey_info_equals
261 * @hotkey1: The first hotkey to compare
262 * @hotkey2: Second hotkey to compare to
263 * @sloppy_equals: If %TRUE sloppy equality will be used. This ignores
264 * the #GtkHotkeyInfo:description and #GtkHotkeyInfo:app-info
265 * properties of the objects.
266 * @returns: %TRUE if all the properties of the hotkeys match. Two %NULL hotkeys
267 * are also considered equal.
269 * Compare two #GtkHotkeyInfo<!-- -->s to see if they are equal. This method
270 * allows an optional 'sloppy equality' which ignores #GtkHotkeyInfo:description
271 * and #GtkHotkeyInfo:app-info.
274 gtk_hotkey_info_equals (GtkHotkeyInfo *hotkey1,
275 GtkHotkeyInfo *hotkey2,
276 gboolean sloppy_equals)
278 if (hotkey1 == hotkey2) return TRUE;
280 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (hotkey1), FALSE);
281 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (hotkey2), FALSE);
283 if (!g_str_equal (gtk_hotkey_info_get_application_id (hotkey1),
284 gtk_hotkey_info_get_application_id (hotkey2)))
287 if (!g_str_equal (gtk_hotkey_info_get_key_id (hotkey1),
288 gtk_hotkey_info_get_key_id (hotkey2)))
291 if (!g_str_equal (gtk_hotkey_info_get_signature (hotkey1),
292 gtk_hotkey_info_get_signature (hotkey2)))
295 /* For sloppy equality this is good enough */
299 const gchar *d1, *d2;
300 d1 = gtk_hotkey_info_get_description (hotkey1);
301 d2 = gtk_hotkey_info_get_description (hotkey2);
302 if (d1 != NULL && d2 != NULL) {
303 if (!g_str_equal (gtk_hotkey_info_get_description (hotkey1),
304 gtk_hotkey_info_get_description (hotkey2)))
308 /* The case d1 == d2 == NULL will pass through the above */
310 GAppInfo *app1, *app2;
311 app1 = gtk_hotkey_info_get_app_info (hotkey1);
312 app2 = gtk_hotkey_info_get_app_info (hotkey2);
313 if (app1 != NULL && app2 != NULL) {
314 if (!g_app_info_equal (app1, app2))
316 } else if (app1 != app2)
318 /* As above, if app1 == app2 == NULL we count equality */
324 * gtk_hotkey_info_activated
325 * @self: #GtkHotkeyInfo to emit the #GtkHotkeyInfo::activated signal
326 * @event_time: The system time the event happened on. This is useful for
327 * applications to pass through focus stealing prevention when
330 * Emit the #GtkHotkeyInfo::activated signal on a hotkey. Mainly called
331 * by #GtkHotkeyListener implementations. This method should not normally be
332 * used by applications.
335 gtk_hotkey_info_activated (GtkHotkeyInfo* self, guint event_time)
337 g_return_if_fail (GTK_HOTKEY_IS_INFO(self));
339 g_signal_emit (self, info_signals[ACTIVATED], 0, event_time);
343 * gtk_hotkey_info_new:
344 * @app_id: Unique identifier the running application can choose for it self.
345 * May be a free form string, but a descriptive name is encouraged
346 * @key_id: A key the application uses to recognize the hotkey. May be a free
347 * form string, but a descriptive name is encouraged
348 * @signature: A key press signature parsable by gtk_accelerator_parse(). For
349 * examplpe '<Alt>F3' or '<Control><Shift>G'.
350 * @app_info: An optional #GAppInfo to associate with the hotkey. Pass %NULL to
352 * @returns: A new #GtkHotkeyInfo or %NULL on error. Error conditions could for
353 * example be invalid an invalid @signature, or %NULL arguments.
355 * Create a new hotkey. To actually trigger the hotkey when the user enters
356 * the right keyboard combination call gtk_hotkey_info_bind(). To save and
357 * load your hotkey settings use the #GtkHotkeyRegistry provided by
358 * gtk_hotkey_registry_get_default().
361 gtk_hotkey_info_new (const gchar *app_id,
363 const gchar *signature,
366 GtkHotkeyInfo * self;
368 g_return_val_if_fail (app_id != NULL, NULL);
369 g_return_val_if_fail (key_id != NULL, NULL);
371 /* A NULL app_info is ok, but it better be a GAppInfo then */
372 if (app_info != NULL)
373 g_return_val_if_fail (G_IS_APP_INFO(app_info), NULL);
375 self = g_object_new (GTK_HOTKEY_TYPE_INFO, "application-id", app_id,
377 "signature", signature,
378 "app-info", app_info,
384 gtk_hotkey_info_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
386 GtkHotkeyInfo * self;
388 self = GTK_HOTKEY_INFO (object);
390 switch (property_id) {
391 case GTK_HOTKEY_INFO_BOUND:
392 g_value_set_boolean (value,
393 (self->priv->listener != NULL));
395 case GTK_HOTKEY_INFO_APPLICATION_ID:
396 g_value_set_string (value,
397 gtk_hotkey_info_get_application_id (self));
399 case GTK_HOTKEY_INFO_KEY_ID:
400 g_value_set_string (value,
401 gtk_hotkey_info_get_key_id (self));
403 case GTK_HOTKEY_INFO_APP_INFO:
404 g_value_set_object (value,
405 gtk_hotkey_info_get_app_info (self));
407 case GTK_HOTKEY_INFO_SIGNATURE:
408 g_value_set_string (value,
409 gtk_hotkey_info_get_signature (self));
411 case GTK_HOTKEY_INFO_DESCRIPTION:
412 g_value_set_string (value,
413 gtk_hotkey_info_get_description (self));
416 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
423 gtk_hotkey_info_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
426 GtkHotkeyInfoPrivate *priv;
428 self = GTK_HOTKEY_INFO (object);
431 switch (property_id) {
432 case GTK_HOTKEY_INFO_BOUND:
433 g_critical ("Writing to read only property 'bound'");
435 case GTK_HOTKEY_INFO_APPLICATION_ID:
437 g_critical ("Overwriting construct only property 'application-id'");
438 priv->app_id = g_value_dup_string (value);
440 case GTK_HOTKEY_INFO_KEY_ID:
442 g_critical ("Overwriting construct only property 'key-id'");
443 priv->key_id = g_value_dup_string (value);
445 case GTK_HOTKEY_INFO_APP_INFO:
447 g_critical ("Overwriting construct only property 'app-info'");
448 priv->app_info = g_value_dup_object (value);
450 case GTK_HOTKEY_INFO_SIGNATURE:
452 g_critical ("Overwriting construct only property 'signature'");
453 priv->signature = g_value_dup_string (value);
455 case GTK_HOTKEY_INFO_DESCRIPTION:
456 if (priv->description)
457 g_free(priv->description);
458 priv->description = g_value_dup_string (value);
461 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
468 gtk_hotkey_info_class_init (GtkHotkeyInfoClass * klass)
470 gtk_hotkey_info_parent_class = g_type_class_peek_parent (klass);
471 g_type_class_add_private (klass, sizeof (GtkHotkeyInfoPrivate));
473 G_OBJECT_CLASS (klass)->get_property = gtk_hotkey_info_get_property;
474 G_OBJECT_CLASS (klass)->set_property = gtk_hotkey_info_set_property;
475 G_OBJECT_CLASS (klass)->finalize = gtk_hotkey_info_finalize;
478 * GtkHotkeyInfo:bound
480 * Property reflecting whether or not this hotkey has been bound to
481 * a #GtkHotkeyListener. If this property is %TRUE you will receive
482 * #GtkHotkeyInfo::activated signals when the hotkey is triggered
485 g_object_class_install_property (G_OBJECT_CLASS (klass),
486 GTK_HOTKEY_INFO_BOUND,
487 g_param_spec_boolean ("bound",
489 "Whether or not the hotkey is bound to a GtkHotkeyListener",
494 * GtkHotkeyInfo:application-id
496 * A free form string chosen by the application using the hotkey, under
497 * which the application identifies itself.
499 g_object_class_install_property (G_OBJECT_CLASS (klass),
500 GTK_HOTKEY_INFO_APPLICATION_ID,
501 g_param_spec_string ("application-id",
503 "Globally unique application id",
505 G_PARAM_READABLE | G_PARAM_WRITABLE |
506 G_PARAM_CONSTRUCT_ONLY));
509 * GtkHotkeyInfo:key-id
511 * A free form string the application using the hotkey has attributed
512 * the hotkey so that it can be identified later on. Applications are
513 * encouraged to choose descriptive key ids.
515 g_object_class_install_property (G_OBJECT_CLASS (klass),
516 GTK_HOTKEY_INFO_KEY_ID,
517 g_param_spec_string ("key-id",
519 "Globally unique identifier for the hotkey",
521 G_PARAM_READABLE | G_PARAM_WRITABLE |
522 G_PARAM_CONSTRUCT_ONLY));
525 * GtkHotkeyInfo:app-info
527 * A #GAppInfo associated with the key. This is mainly useful for external
528 * applications which can use the information provided by the #GAppInfo
529 * to display meaningful messages to the user. Like 'The keyboard
530 * combination <Alt>F3' is already assigned to the application
531 * "Deskbar Applet", please select another'.
533 g_object_class_install_property (G_OBJECT_CLASS (klass),
534 GTK_HOTKEY_INFO_APP_INFO,
535 g_param_spec_object ("app-info",
536 "Application Information",
537 "Object holding metadata about "
538 "the hotkey's application",
540 G_PARAM_READABLE | G_PARAM_WRITABLE |
541 G_PARAM_CONSTRUCT_ONLY));
544 * GtkHotkeyInfo:signature
546 * The keyboard signature of the hotkey. This could for example by
547 * '<Alt>F3' or '<Control><Shift>G'. The signature should be parsable by
548 * gtk_accelerator_parse().
550 g_object_class_install_property (G_OBJECT_CLASS (klass),
551 GTK_HOTKEY_INFO_SIGNATURE,
552 g_param_spec_string ("signature",
554 "String defining the keyboard shortcut",
556 G_PARAM_READABLE | G_PARAM_WRITABLE |
557 G_PARAM_CONSTRUCT_ONLY));
560 * GtkHotkeyInfo:description
562 * An optional free form description of the hotkey.
564 g_object_class_install_property (G_OBJECT_CLASS (klass),
565 GTK_HOTKEY_INFO_DESCRIPTION,
566 g_param_spec_string ("description",
568 "Short description of what happens upon activation",
570 G_PARAM_READABLE | G_PARAM_WRITABLE));
573 * GtkHotkeyInfo::activated:
574 * @hotkey: a #GtkHotkeyInfo for the hotkey that was activated
575 * @event_time: Time for event triggering the keypress. This is mainly
576 * used to pass to window management functions to pass through
577 * focus stealing prevention
579 * Emitted when a hotkey has been activated.
581 info_signals[ACTIVATED] = \
582 g_signal_new ("activated",
583 GTK_HOTKEY_TYPE_INFO,
586 g_cclosure_marshal_VOID__UINT,
593 gtk_hotkey_info_init (GtkHotkeyInfo * self)
595 self->priv = GTK_HOTKEY_INFO_GET_PRIVATE (self);
597 self->priv->app_id = NULL;
598 self->priv->key_id = NULL;
599 self->priv->app_info = NULL;
604 gtk_hotkey_info_finalize (GObject * obj)
607 GtkHotkeyInfoPrivate *priv;
609 self = GTK_HOTKEY_INFO (obj);
613 g_free (priv->app_id);
615 g_free (priv->key_id);
617 g_object_unref (priv->app_info);
619 g_free (priv->signature);
620 if (priv->description)
621 g_free (priv->description);
622 if (GTK_HOTKEY_IS_LISTENER (priv->listener))
623 g_object_unref (priv->listener);
625 G_OBJECT_CLASS (gtk_hotkey_info_parent_class)->finalize (obj);
630 gtk_hotkey_info_get_type (void)
632 static GType gtk_hotkey_info_type_id = 0;
634 if (G_UNLIKELY (gtk_hotkey_info_type_id == 0)) {
635 static const GTypeInfo g_define_type_info = {
636 sizeof (GtkHotkeyInfoClass),
637 (GBaseInitFunc) NULL,
638 (GBaseFinalizeFunc) NULL,
639 (GClassInitFunc) gtk_hotkey_info_class_init,
640 (GClassFinalizeFunc) NULL,
642 sizeof (GtkHotkeyInfo),
644 (GInstanceInitFunc) gtk_hotkey_info_init
647 gtk_hotkey_info_type_id = g_type_register_static (G_TYPE_OBJECT,
653 return gtk_hotkey_info_type_id;