1 /*
2  * This file is part of GtkHotkey.
3  * Copyright Mikkel Kamstrup Erlandsen, March, 2008
4  *
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.
9  *
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.
14  *
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/>.
17  */
18 
19 #include "gtk-hotkey-info.h"
20 #include "gtk-hotkey-error.h"
21 #include "gtk-hotkey-listener.h"
22 
23 typedef struct {
24 	gchar		*app_id;
25 	gchar		*key_id;
26 	GAppInfo	*app_info;
27 	gchar		*signature;
28 	gchar		*description;
29 	GtkHotkeyListener	*listener;
30 } GtkHotkeyInfoPrivate;
31 
32 #define GTK_HOTKEY_INFO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_HOTKEY_TYPE_INFO, GtkHotkeyInfoPrivate))
33 
34 enum  {
35 	GTK_HOTKEY_INFO_BOUND = 1,
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,
41 };
42 
43 enum {
44 	ACTIVATED,
45 
46 	LAST_SIGNAL
47 };
48 
49 guint				info_signals[LAST_SIGNAL] = { 0 };
50 
51 static void			gtk_hotkey_info_finalize (GObject * obj);
52 
53 #if !GLIB_CHECK_VERSION(2, 58, 0)
G_DEFINE_TYPE(GtkHotkeyInfo,gtk_hotkey_info,G_TYPE_OBJECT)54 G_DEFINE_TYPE(GtkHotkeyInfo, gtk_hotkey_info, G_TYPE_OBJECT)
55 #else
56 G_DEFINE_TYPE_WITH_CODE(GtkHotkeyInfo, gtk_hotkey_info, G_TYPE_OBJECT,
57 		G_ADD_PRIVATE(GtkHotkeyInfo))
58 #endif
59 
60 /**
61  * SECTION:gtk-hotkey-info
62  * @short_description: Primary representation of a hotkey
63  * @see_also: #GtkHotkeyRegistry
64  *
65  * #GtkHotkeyInfo is the primary object around which the GtkHotkey library
66  * revolves.
67  *
68  * Hotkeys are stored and managed via a #GtkHotkeyRegistry, while the actual
69  * binding and listening for key-presses is done by a #GtkHotkeyListener.
70  **/
71 
72 
73 /**
74  * gtk_hotkey_info_bind:
75  * @self: The hotkey to bind
76  * @error: Place to return a #GError, or %NULL to ignore
77  *
78  * Register the hotkey with the system. The #GtkHotkeyInfo::activated signal
79  * will now be emitted when the user presses the keyboard combination
80  * matching the hotkey's signature.
81  *
82  * Returns: %TRUE on success, and %FALSE on error in which case @error
83  *          is also set
84  **/
85 gboolean
86 gtk_hotkey_info_bind (GtkHotkeyInfo* self, GError **error)
87 {
88 	gboolean result;
89 	GtkHotkeyInfoPrivate *priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
90 
91 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), FALSE);
92 
93 	if (gtk_hotkey_info_is_bound(self)) {
94 		g_set_error (error, GTK_HOTKEY_LISTENER_ERROR,
95 					 GTK_HOTKEY_LISTENER_ERROR_BIND,
96 					 "Can not bind hotkey '%s' with signature '%s'. "
97 					 "It is already bound",
98 					 gtk_hotkey_info_get_key_id(self),
99 					 gtk_hotkey_info_get_signature(self));
100 		return FALSE;
101 	}
102 
103 	if (!priv->listener)
104 		priv->listener = gtk_hotkey_listener_get_default ();
105 
106 	g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(priv->listener), FALSE);
107 
108 	result = gtk_hotkey_listener_bind_hotkey (priv->listener, self, error);
109 	if (!result) {
110 		g_object_unref (priv->listener);
111 		priv->listener = NULL;
112 	}
113 
114 	if (result)
115 		g_object_notify (G_OBJECT(self), "bound");
116 
117 	return result;
118 }
119 
120 /**
121  * gtk_hotkey_info_unbind
122  * @self: The hotkey to unbind
123  * @error: Place to return a #GError, or %NULL to ignore
124  * @returns: %TRUE on success, and %FALSE on error in which case @error
125  *          is also set
126  *
127  * Remove the hotkey binding from the system. The #GtkHotkeyInfo::activated
128  * signal will no longer be emitted when the hotkey is pressed.
129  */
130 gboolean
gtk_hotkey_info_unbind(GtkHotkeyInfo * self,GError ** error)131 gtk_hotkey_info_unbind (GtkHotkeyInfo* self, GError **error)
132 {
133 	gboolean result;
134 	GtkHotkeyInfoPrivate *priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
135 
136 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), FALSE);
137 
138 	if (!gtk_hotkey_info_is_bound(self)) {
139 		g_set_error (error, GTK_HOTKEY_LISTENER_ERROR,
140 					 GTK_HOTKEY_LISTENER_ERROR_UNBIND,
141 					 "Can not unbind hotkey '%s' with signature '%s'."
142 					 "It is not bound",
143 					 gtk_hotkey_info_get_key_id(self),
144 					 gtk_hotkey_info_get_signature(self));
145 		return FALSE;
146 	}
147 
148 	g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(priv->listener), FALSE);
149 
150 	result = gtk_hotkey_listener_unbind_hotkey (priv->listener, self,
151 												error);
152 
153 	g_object_unref (priv->listener);
154 	priv->listener = NULL;
155 
156 	if (result)
157 		g_object_notify (G_OBJECT(self), "bound");
158 
159 	return result;
160 }
161 
162 /**
163  * gtk_hotkey_info_is_bound
164  * @self: The hotkey to inspect
165  * @returns: %TRUE if gtk_hotkey_info_bind() has been called and returned %TRUE
166  *           on this hotkey
167  *
168  * Check whether the hotkey has been successfully bound to a #GtkHotkeyListener.
169  */
170 gboolean
gtk_hotkey_info_is_bound(GtkHotkeyInfo * self)171 gtk_hotkey_info_is_bound (GtkHotkeyInfo* self)
172 {
173 	GtkHotkeyInfoPrivate *priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
174 
175 	return (priv->listener != NULL);
176 }
177 
178 /**
179  * gtk_hotkey_info_get_application_id
180  * @self:
181  *
182  * Get the unique system identifier for the hotkey. See
183  * #GtkHotkeyInfo:application-id for details.
184  */
185 const gchar*
gtk_hotkey_info_get_application_id(GtkHotkeyInfo * self)186 gtk_hotkey_info_get_application_id (GtkHotkeyInfo* self)
187 {
188 	GtkHotkeyInfoPrivate *priv;
189 
190 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
191 
192 	priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
193 	return priv->app_id;
194 }
195 
196 /**
197  * gtk_hotkey_info_get_key_id
198  * @self:
199  *
200  * Get the identifier the owning application use to identify this hotkey.
201  * See #GtkHotkeyInfo:key-id for details.
202  */
203 const gchar*
gtk_hotkey_info_get_key_id(GtkHotkeyInfo * self)204 gtk_hotkey_info_get_key_id (GtkHotkeyInfo* self)
205 {
206 	GtkHotkeyInfoPrivate *priv;
207 
208 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
209 
210 	priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
211 	return priv->key_id;
212 }
213 
214 /**
215  * gtk_hotkey_info_get_app_info
216  * @self:
217  *
218  * Not to be confused with the value of the #GtkHotkeyInfo:application-id
219  * property. The hotkey can be associated with an installed desktop application
220  * via a #GAppInfo. This is not mandatory and this method returns %NULL
221  * if no desktop application has been associated with this hotkey.
222  *
223  * See the #GtkHotkeyInfo:app-info property for details.
224  */
225 GAppInfo*
gtk_hotkey_info_get_app_info(GtkHotkeyInfo * self)226 gtk_hotkey_info_get_app_info (GtkHotkeyInfo* self)
227 {
228 	GtkHotkeyInfoPrivate *priv;
229 
230 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
231 
232 	priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
233 	return priv->app_info;
234 }
235 
236 /**
237  * gtk_hotkey_info_get_signature
238  * @self:
239  *
240  * Get the keyboard signature of the hotkey. This could for example be
241  * '&lt;Alt&gt;F3' or '&lt;Control&gt;&lt;Shift&gt;G'.
242  */
243 const gchar*
gtk_hotkey_info_get_signature(GtkHotkeyInfo * self)244 gtk_hotkey_info_get_signature (GtkHotkeyInfo* self)
245 {
246 	GtkHotkeyInfoPrivate *priv;
247 
248 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
249 
250 	priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
251 	return priv->signature;
252 }
253 
254 /**
255  * gtk_hotkey_info_get_description
256  * @self:
257  * @returns: The description of the hotkey or %NULL if none is set
258  *
259  * Get the free form description of the hotkey. The description is not guaranteed
260  * to be set and may be %NULL.
261  *
262  * FIXME: Do we need to take i18n into account here?
263  */
264 const gchar*
gtk_hotkey_info_get_description(GtkHotkeyInfo * self)265 gtk_hotkey_info_get_description (GtkHotkeyInfo* self)
266 {
267 	GtkHotkeyInfoPrivate *priv;
268 
269 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO(self), NULL);
270 
271 	priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
272 	return priv->description;
273 }
274 
275 /**
276  * gtk_hotkey_info_set_description
277  * @self:
278  *
279  * Set a description for the hotkey. See also gtk_hotkey_info_get_description().
280  */
281 void
gtk_hotkey_info_set_description(GtkHotkeyInfo * self,const gchar * description)282 gtk_hotkey_info_set_description (GtkHotkeyInfo* self, const gchar *description)
283 {
284 	g_return_if_fail (GTK_HOTKEY_IS_INFO(self));
285 	g_object_set (self, "description", description, NULL);
286 }
287 
288 /**
289  * gtk_hotkey_info_equals
290  * @hotkey1: The first hotkey to compare
291  * @hotkey2: Second hotkey to compare to
292  * @sloppy_equals: If %TRUE sloppy equality will be used. This ignores
293  *                 the #GtkHotkeyInfo:description and #GtkHotkeyInfo:app-info
294  *                 properties of the objects.
295  * @returns: %TRUE if all the properties of the hotkeys match. Two %NULL hotkeys
296  *           are also considered equal.
297  *
298  * Compare two #GtkHotkeyInfo<!-- -->s to see if they are equal. This method
299  * allows an optional 'sloppy equality' which ignores #GtkHotkeyInfo:description
300  * and #GtkHotkeyInfo:app-info.
301  */
302 gboolean
gtk_hotkey_info_equals(GtkHotkeyInfo * hotkey1,GtkHotkeyInfo * hotkey2,gboolean sloppy_equals)303 gtk_hotkey_info_equals (GtkHotkeyInfo *hotkey1,
304 						GtkHotkeyInfo *hotkey2,
305 						gboolean 			sloppy_equals)
306 {
307 	if (hotkey1 == hotkey2) return TRUE;
308 
309 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (hotkey1), FALSE);
310 	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (hotkey2), FALSE);
311 
312 	if (!g_str_equal (gtk_hotkey_info_get_application_id (hotkey1),
313 					  gtk_hotkey_info_get_application_id (hotkey2)))
314 		return FALSE;
315 
316 	if (!g_str_equal (gtk_hotkey_info_get_key_id (hotkey1),
317 					  gtk_hotkey_info_get_key_id (hotkey2)))
318 		return FALSE;
319 
320 	if (!g_str_equal (gtk_hotkey_info_get_signature (hotkey1),
321 					  gtk_hotkey_info_get_signature (hotkey2)))
322 		return FALSE;
323 
324 	/* For sloppy equality this is good enough */
325 	if (sloppy_equals)
326 		return TRUE;
327 
328 	const gchar	*d1, *d2;
329 	d1 = gtk_hotkey_info_get_description (hotkey1);
330 	d2 = gtk_hotkey_info_get_description (hotkey2);
331 	if (d1 != NULL && d2 != NULL) {
332 		if (!g_str_equal (gtk_hotkey_info_get_description (hotkey1),
333 						  gtk_hotkey_info_get_description (hotkey2)))
334 			return FALSE;
335 	} else if (d1 != d2)
336 		return FALSE;
337 	/* The case d1 == d2 == NULL will pass through the above */
338 
339 	GAppInfo	*app1, *app2;
340 	app1 = gtk_hotkey_info_get_app_info (hotkey1);
341 	app2 = gtk_hotkey_info_get_app_info (hotkey2);
342 	if (app1 != NULL && app2 != NULL) {
343 		if (!g_app_info_equal (app1, app2))
344 			return FALSE;
345 	} else if (app1 != app2)
346 		return FALSE;
347 	/* As above, if app1 == app2 == NULL we count equality */
348 
349 	return TRUE;
350 }
351 
352 /**
353  * gtk_hotkey_info_activated
354  * @self: #GtkHotkeyInfo to emit the #GtkHotkeyInfo::activated signal
355  * @event_time: The system time the event happened on. This is useful for
356  *              applications to pass through focus stealing prevention when
357  *              mapping windows
358  *
359  * Emit the #GtkHotkeyInfo::activated signal on a hotkey. Mainly called
360  * by #GtkHotkeyListener implementations. This method should not normally be
361  * used by applications.
362  */
363 void
gtk_hotkey_info_activated(GtkHotkeyInfo * self,guint event_time)364 gtk_hotkey_info_activated (GtkHotkeyInfo* self, guint event_time)
365 {
366 	g_return_if_fail (GTK_HOTKEY_IS_INFO(self));
367 
368 	g_signal_emit (self, info_signals[ACTIVATED], 0, event_time);
369 }
370 
371 /**
372  * gtk_hotkey_info_new:
373  * @app_id: Unique identifier the running application can choose for it self.
374  *          May be a free form string, but a descriptive name is encouraged
375  * @key_id: A key the application uses to recognize the hotkey. May be a free
376  *          form string, but a descriptive name is encouraged
377  * @signature: A key press signature parsable by gtk_accelerator_parse(). For
378  *             examplpe '&lt;Alt&gt;F3' or '&lt;Control&gt;&lt;Shift&gt;G'.
379  * @app_info: An optional #GAppInfo to associate with the hotkey. Pass %NULL to
380  *            ignore this
381  * @returns: A new #GtkHotkeyInfo or %NULL on error. Error conditions could for
382  *           example be invalid an invalid @signature, or %NULL arguments.
383  *
384  * Create a new hotkey. To actually trigger the hotkey when the user enters
385  * the right keyboard combination call gtk_hotkey_info_bind(). To save and
386  * load your hotkey settings use the #GtkHotkeyRegistry provided by
387  * gtk_hotkey_registry_get_default().
388  **/
389 GtkHotkeyInfo*
gtk_hotkey_info_new(const gchar * app_id,const gchar * key_id,const gchar * signature,GAppInfo * app_info)390 gtk_hotkey_info_new (const gchar	*app_id,
391 					 const gchar	*key_id,
392 					 const gchar	*signature,
393 					 GAppInfo		*app_info)
394 {
395 	GtkHotkeyInfo * self;
396 
397 	g_return_val_if_fail (app_id != NULL, NULL);
398 	g_return_val_if_fail (key_id != NULL, NULL);
399 
400 	/* A NULL app_info is ok, but it better be a GAppInfo then */
401 	if (app_info != NULL)
402 		g_return_val_if_fail (G_IS_APP_INFO(app_info), NULL);
403 
404 	self = g_object_new (GTK_HOTKEY_TYPE_INFO, "application-id", app_id,
405 											   "key-id", key_id,
406 											   "signature", signature,
407 											   "app-info", app_info,
408 												NULL);
409 	return self;
410 }
411 
412 static void
gtk_hotkey_info_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)413 gtk_hotkey_info_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
414 {
415 	GtkHotkeyInfo * self;
416 	GtkHotkeyInfoPrivate *priv;
417 
418 
419 	self = GTK_HOTKEY_INFO (object);
420 	priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
421 
422 	switch (property_id) {
423 		case GTK_HOTKEY_INFO_BOUND:
424 			g_value_set_boolean (value,
425 								 (priv->listener != NULL));
426 			break;
427 		case GTK_HOTKEY_INFO_APPLICATION_ID:
428 			g_value_set_string (value,
429 								gtk_hotkey_info_get_application_id (self));
430 			break;
431 		case GTK_HOTKEY_INFO_KEY_ID:
432 			g_value_set_string (value,
433 								gtk_hotkey_info_get_key_id (self));
434 			break;
435 		case GTK_HOTKEY_INFO_APP_INFO:
436 			g_value_set_object (value,
437 								gtk_hotkey_info_get_app_info (self));
438 			break;
439 		case GTK_HOTKEY_INFO_SIGNATURE:
440 			g_value_set_string (value,
441 								gtk_hotkey_info_get_signature (self));
442 			break;
443 		case GTK_HOTKEY_INFO_DESCRIPTION:
444 			g_value_set_string (value,
445 								gtk_hotkey_info_get_description (self));
446 			break;
447 		default:
448 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
449 			break;
450 	}
451 }
452 
453 
454 static void
gtk_hotkey_info_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)455 gtk_hotkey_info_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
456 {
457 	GtkHotkeyInfo			*self;
458 	GtkHotkeyInfoPrivate	*priv;
459 
460 	self = GTK_HOTKEY_INFO (object);
461 	priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
462 
463 	switch (property_id) {
464 		case GTK_HOTKEY_INFO_BOUND:
465 			g_critical ("Writing to read only property 'bound'");
466 			break;
467 		case GTK_HOTKEY_INFO_APPLICATION_ID:
468 			if (priv->app_id)
469 				g_critical ("Overwriting construct only property 'application-id'");
470 			priv->app_id = g_value_dup_string (value);
471 			break;
472 		case GTK_HOTKEY_INFO_KEY_ID:
473 			if (priv->key_id)
474 				g_critical ("Overwriting construct only property 'key-id'");
475 			priv->key_id = g_value_dup_string (value);
476 			break;
477 		case GTK_HOTKEY_INFO_APP_INFO:
478 			if (priv->app_info)
479 				g_critical ("Overwriting construct only property 'app-info'");
480 			priv->app_info = g_value_dup_object (value);
481 			break;
482 		case GTK_HOTKEY_INFO_SIGNATURE:
483 			if (priv->signature)
484 				g_critical ("Overwriting construct only property 'signature'");
485 			priv->signature = g_value_dup_string (value);
486 			break;
487 		case GTK_HOTKEY_INFO_DESCRIPTION:
488 			if (priv->description)
489 				g_free(priv->description);
490 			priv->description = g_value_dup_string (value);
491 			break;
492 		default:
493 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
494 			break;
495 	}
496 }
497 
498 
499 static void
gtk_hotkey_info_class_init(GtkHotkeyInfoClass * klass)500 gtk_hotkey_info_class_init (GtkHotkeyInfoClass * klass)
501 {
502 	gtk_hotkey_info_parent_class = g_type_class_peek_parent (klass);
503 
504 #if !GLIB_CHECK_VERSION(2, 58, 0)
505 	g_type_class_add_private (G_OBJECT_CLASS (klass),
506 			sizeof (GtkHotkeyInfoPrivate));
507 #endif
508 
509 	G_OBJECT_CLASS (klass)->get_property = gtk_hotkey_info_get_property;
510 	G_OBJECT_CLASS (klass)->set_property = gtk_hotkey_info_set_property;
511 	G_OBJECT_CLASS (klass)->finalize = gtk_hotkey_info_finalize;
512 
513 	/**
514 	 * GtkHotkeyInfo:bound
515 	 *
516 	 * Property reflecting whether or not this hotkey has been bound to
517 	 * a #GtkHotkeyListener. If this property is %TRUE you will receive
518 	 * #GtkHotkeyInfo::activated signals when the hotkey is triggered
519 	 * by the user.
520 	 */
521 	g_object_class_install_property (G_OBJECT_CLASS (klass),
522 									 GTK_HOTKEY_INFO_BOUND,
523 									 g_param_spec_boolean ("bound",
524 														  "Is Bound",
525 														  "Whether or not the hotkey is bound to a GtkHotkeyListener",
526 														  FALSE,
527 														  G_PARAM_READABLE));
528 
529 	/**
530 	 * GtkHotkeyInfo:application-id
531 	 *
532 	 * A free form string chosen by the application using the hotkey, under
533 	 * which the application identifies itself.
534 	 */
535 	g_object_class_install_property (G_OBJECT_CLASS (klass),
536 									 GTK_HOTKEY_INFO_APPLICATION_ID,
537 									 g_param_spec_string ("application-id",
538 														  "Application Id",
539 														  "Globally unique application id",
540 														  NULL,
541 														  G_PARAM_READABLE | G_PARAM_WRITABLE |
542 														  G_PARAM_CONSTRUCT_ONLY));
543 
544 	/**
545 	 * GtkHotkeyInfo:key-id
546 	 *
547 	 * A free form string the application using the hotkey has attributed
548 	 * the hotkey so that it can be identified later on. Applications are
549 	 * encouraged to choose descriptive key ids.
550 	 */
551 	g_object_class_install_property (G_OBJECT_CLASS (klass),
552 									 GTK_HOTKEY_INFO_KEY_ID,
553 									 g_param_spec_string ("key-id",
554 														  "Hotkey Id",
555 														  "Globally unique identifier for the hotkey",
556 														  NULL,
557 														  G_PARAM_READABLE | G_PARAM_WRITABLE |
558 														  G_PARAM_CONSTRUCT_ONLY));
559 
560 	/**
561 	 * GtkHotkeyInfo:app-info
562 	 *
563 	 * A #GAppInfo associated with the key. This is mainly useful for external
564 	 * applications which can use the information provided by the #GAppInfo
565 	 * to display meaningful messages to the user. Like 'The keyboard
566 	 * combination &lt;Alt&gt;F3' is already assigned to the application
567 	 * "Deskbar Applet", please select another'.
568 	 */
569 	g_object_class_install_property (G_OBJECT_CLASS (klass),
570 									 GTK_HOTKEY_INFO_APP_INFO,
571 									 g_param_spec_object ("app-info",
572 														  "Application Information",
573 														  "Object holding metadata about "
574 														  "the hotkey's application",
575 														  G_TYPE_APP_INFO,
576 														  G_PARAM_READABLE | G_PARAM_WRITABLE |
577 														  G_PARAM_CONSTRUCT_ONLY));
578 
579 	/**
580 	 * GtkHotkeyInfo:signature
581 	 *
582 	 * The keyboard signature of the hotkey. This could for example by
583 	 * '&lt;Alt&gt;F3' or '&lt;Control&gt;&lt;Shift&gt;G'. The signature should be parsable by
584 	 * gtk_accelerator_parse().
585 	 */
586 	g_object_class_install_property (G_OBJECT_CLASS (klass),
587 									 GTK_HOTKEY_INFO_SIGNATURE,
588 									 g_param_spec_string ("signature",
589 														  "Signature",
590 														  "String defining the keyboard shortcut",
591 														  NULL,
592 														  G_PARAM_READABLE | G_PARAM_WRITABLE |
593 														  G_PARAM_CONSTRUCT_ONLY));
594 
595 	/**
596 	 * GtkHotkeyInfo:description
597 	 *
598 	 * An optional free form description of the hotkey.
599 	 */
600 	g_object_class_install_property (G_OBJECT_CLASS (klass),
601 									 GTK_HOTKEY_INFO_DESCRIPTION,
602 									 g_param_spec_string ("description",
603 														  "Description",
604 														  "Short description of what happens upon activation",
605 														  "",
606 														  G_PARAM_READABLE | G_PARAM_WRITABLE));
607 
608 	/**
609 	 * GtkHotkeyInfo::activated:
610 	 * @hotkey: a #GtkHotkeyInfo for the hotkey that was activated
611 	 * @event_time: Time for event triggering the keypress. This is mainly
612 	 *              used to pass to window management functions to pass through
613 	 *              focus stealing prevention
614 	 *
615 	 * Emitted when a hotkey has been activated.
616 	 */
617 	info_signals[ACTIVATED] = \
618 	g_signal_new ("activated",
619 				  GTK_HOTKEY_TYPE_INFO,
620 				  G_SIGNAL_RUN_LAST,
621 				  0, NULL, NULL,
622 				  g_cclosure_marshal_VOID__UINT,
623 				  G_TYPE_NONE, 1,
624 				  G_TYPE_UINT);
625 }
626 
627 
628 static void
gtk_hotkey_info_init(GtkHotkeyInfo * self)629 gtk_hotkey_info_init (GtkHotkeyInfo * self)
630 {
631 	GtkHotkeyInfoPrivate *priv = GTK_HOTKEY_INFO_GET_PRIVATE (self);
632 
633 	priv->app_id = NULL;
634 	priv->key_id = NULL;
635 	priv->app_info = NULL;
636 }
637 
638 
639 static void
gtk_hotkey_info_finalize(GObject * obj)640 gtk_hotkey_info_finalize (GObject * obj)
641 {
642 	GtkHotkeyInfo			*self;
643 	GtkHotkeyInfoPrivate	*priv;
644 
645 	self = GTK_HOTKEY_INFO (obj);
646 	priv = GTK_HOTKEY_INFO_GET_PRIVATE(self);
647 
648 	if (priv->app_id)
649 		g_free (priv->app_id);
650 	if (priv->key_id)
651 		g_free (priv->key_id);
652 	if (priv->app_info)
653 		g_object_unref (priv->app_info);
654 	if (priv->signature)
655 		g_free (priv->signature);
656 	if (priv->description)
657 		g_free (priv->description);
658 	if (GTK_HOTKEY_IS_LISTENER (priv->listener))
659 		g_object_unref (priv->listener);
660 
661 	G_OBJECT_CLASS (gtk_hotkey_info_parent_class)->finalize (obj);
662 }
663