2 * This file is a part of hildon-extras
4 * Copyright (C) 2009 Cornelius Hald <hald@icandy.de>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser Public License as published by
8 * the Free Software Foundation; version 2 of the license.
10 * This program 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 Public License for more details.
18 * SECTION:he-fullscreen-button
19 * @short_description: A semi-transparent button to leave fullscreen mode.
21 * #HeFullscreenButton is a semi transparent button which is automatically
22 * displayed whenever it's parent window enters fullscreen state.
23 * It's always displayed in the lower right corner of the parent window.
25 * The button is automatically hidden after 5 seconds of mouse click
26 * inactivity. If the user clicks the parent window the button is shown
27 * for another 5 second.
29 * If the user clicks the button, the "clicked" signal is emitted. If you did
30 * not provide a signal handler for the "clicked" signal, then the default
31 * handler will call gtk_window_unfullscreen() on the parent window.
32 * If you provide a signal handler, the default handler will not be called
35 * So, if your application has just one window. It will be enough, if you create
36 * an instance of HeFullscreenButton with this window as parent. Now if your
37 * window switches to fullscreen the HeFullscreenButton is automatically shown
38 * and can be used to leave fullscreen mode. In this case you don't have to provide
39 * a signal handler and you don't have to take care of the buttons destruction.
43 #include <hildon/hildon.h>
45 #include "he-fullscreen-button.h"
47 #define FULLSCREEN_BUTTON_WIDTH 80
48 #define FULLSCREEN_BUTTON_HEIGHT 70
49 #define FULLSCREEN_BUTTON_HIDE_DELAY 5000
50 #define FULLSCREEN_BUTTON_CORNER_RADIUS 20
51 #define FULLSCREEN_BUTTON_ICON "general_fullsize"
52 #define FULLSCREEN_BUTTON_ICON_SIZE 48
55 typedef struct _HeFullscreenButtonPrivate HeFullscreenButtonPrivate
;
57 struct _HeFullscreenButtonPrivate
59 gboolean release_event
;
60 guint32 last_event_time
;
62 guint button_press_signal_id
;
63 guint button_release_signal_id
;
65 gulong button_press_hook_id
;
66 gulong button_release_hook_id
;
68 gboolean act_on_state_change
;
73 #define HE_FULLSCREEN_BUTTON_GET_PRIVATE(object) \
74 (G_TYPE_INSTANCE_GET_PRIVATE((object), \
75 HE_TYPE_FULLSCREEN_BUTTON, HeFullscreenButtonPrivate))
77 G_DEFINE_TYPE(HeFullscreenButton
, he_fullscreen_button
, G_TYPE_OBJECT
)
81 * Hides the fullscreen button.
84 fullscreen_button_hide (HeFullscreenButton
*self
)
86 g_return_if_fail (HE_IS_FULLSCREEN_BUTTON (self
));
88 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
89 g_return_if_fail (priv
!= NULL
);
92 g_source_remove_by_user_data ((gpointer
) self
);
94 if (priv
->overlay
!= NULL
&& GTK_IS_WIDGET (priv
->overlay
)) {
95 gtk_widget_hide (priv
->overlay
);
101 * Changes the position of the fullscreen button.
104 fullscreen_button_set_position (HeFullscreenButton
*self
)
106 GtkWidget
*parent
= GTK_WIDGET (self
->parent_window
);
107 GtkWidget
*overlay
= NULL
;
109 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
110 g_return_if_fail (priv
!= NULL
);
112 overlay
= GTK_WIDGET (priv
->overlay
);
114 /* For some reason I have to call hide/show to make it appear at the new position */
115 gint x
= parent
->allocation
.width
- overlay
->allocation
.width
;
116 gint y
= parent
->allocation
.height
- overlay
->allocation
.height
- OFFSET
;
118 gtk_widget_hide (overlay
);
119 gtk_window_move (GTK_WINDOW (overlay
), x
, y
);
120 gtk_widget_show (overlay
);
125 * Everytime the timer runs out, we hide the fullscreen button.
128 fullscreen_button_on_hide_timer (gpointer data
)
130 g_return_val_if_fail (data
!= NULL
, FALSE
);
131 fullscreen_button_hide (HE_FULLSCREEN_BUTTON (data
));
137 * Shows the full screen button.
140 fullscreen_button_show (HeFullscreenButton
*self
)
142 g_return_if_fail (self
!= NULL
);
144 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
145 g_return_if_fail (priv
!= NULL
);
147 g_return_if_fail (GTK_IS_WIDGET (priv
->overlay
));
149 /* Stop return button hide timeout */
150 g_source_remove_by_user_data ((gpointer
) self
);
152 /* Only show overlay if we come here through a button release event, not a button press event */
153 if (priv
->release_event
) {
155 fullscreen_button_set_position (self
);
156 gtk_widget_show (priv
->overlay
);
158 /* Set the return button hide timeout */
159 g_timeout_add (FULLSCREEN_BUTTON_HIDE_DELAY
,
160 fullscreen_button_on_hide_timer
, (gpointer
) self
);
166 * This hook function is called for each mouse button press or
167 * button release on the parent window.
170 fullscreen_button_input_activity_hook (GSignalInvocationHint
*ihint G_GNUC_UNUSED
,
171 guint n_param_values
,
172 const GValue
*param_values
,
175 HeFullscreenButton
*self
= HE_FULLSCREEN_BUTTON (data
);
176 g_return_val_if_fail (self
, FALSE
);
178 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
179 g_return_val_if_fail (priv
!= NULL
, FALSE
);
181 GdkEventAny
*event
= NULL
;
183 if (n_param_values
>= 2)
184 event
= (GdkEventAny
*) g_value_peek_pointer (&(param_values
[1]));
186 g_return_val_if_fail (event
, TRUE
);
189 switch (event
->type
) {
190 case GDK_BUTTON_PRESS
:
191 case GDK_BUTTON_RELEASE
:
192 time
= ((GdkEventButton
*) event
)->time
;
195 case GDK_KEY_RELEASE
:
196 time
= ((GdkEventKey
*) event
)->time
;
199 /* Filter out unexpected messages */
203 /* We're likely to get events multiple times as they're propagated, so
204 filter out events that we've already seen. */
205 if (time
== priv
->last_event_time
) {
208 priv
->last_event_time
= time
;
210 if (event
&& (event
->type
== GDK_BUTTON_PRESS
)) {
211 priv
->release_event
= FALSE
;
213 priv
->release_event
= TRUE
;
216 fullscreen_button_show (self
);
223 * This function makes the full screen button visible and hooks mouse and
224 * key event signal emissions. The button is hidden after some time and
225 * is reshown when ever one of the signal hooks are activated.
227 * Note: The button may be shown automatically by itself
228 * if you have not set the act_on_state_change property to
231 * @param self A HeFullscreenButton instance.
234 he_fullscreen_button_enable (HeFullscreenButton
*self
)
236 g_return_if_fail(HE_IS_FULLSCREEN_BUTTON(self
));
238 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
239 g_return_if_fail (priv
!= NULL
);
241 if (priv
->button_press_hook_id
== 0) {
242 priv
->button_press_signal_id
=
243 g_signal_lookup ("button-press-event", GTK_TYPE_WIDGET
);
244 priv
->button_press_hook_id
=
245 g_signal_add_emission_hook (priv
->button_press_signal_id
, 0,
246 fullscreen_button_input_activity_hook
,
247 (gpointer
) self
, NULL
);
250 if (priv
->button_release_hook_id
== 0) {
251 priv
->button_release_signal_id
=
252 g_signal_lookup ("button-release-event", GTK_TYPE_WIDGET
);
253 priv
->button_release_hook_id
=
254 g_signal_add_emission_hook (priv
->button_release_signal_id
, 0,
255 fullscreen_button_input_activity_hook
,
256 (gpointer
) self
, NULL
);
259 fullscreen_button_show(self
);
264 * Hides the full screen button and releases mouse and
265 * key event signal emission hooks.
267 * Note: The button may be hidden automatically by itself
268 * if you have not set the act_on_state_change property to
271 * @param self A HeFullscreenButton instance.
274 he_fullscreen_button_disable (HeFullscreenButton
*self
)
276 g_return_if_fail (HE_IS_FULLSCREEN_BUTTON (self
));
278 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
279 g_return_if_fail (priv
!= NULL
);
281 fullscreen_button_hide (self
);
283 if (priv
->button_release_hook_id
> 0) {
284 g_signal_remove_emission_hook (priv
->button_release_signal_id
,
285 priv
->button_release_hook_id
);
286 priv
->button_release_hook_id
= 0;
289 if (priv
->button_press_hook_id
> 0) {
290 g_signal_remove_emission_hook (priv
->button_press_signal_id
,
291 priv
->button_press_hook_id
);
292 priv
->button_press_hook_id
= 0;
298 * Everytime the button is clicked, be emmit the "clicked" signal.
301 fullscreen_button_on_clicked (GtkWidget
*widget
, GdkEventButton
*event G_GNUC_UNUSED
, gpointer data
)
303 HeFullscreenButton
*self
= HE_FULLSCREEN_BUTTON (data
);
304 g_signal_emit_by_name (self
, "clicked");
311 * Creates a rectangle with a rounded upper left corner.
314 fullscreen_button_create_rectangle (cairo_t
*ctx
, double x
, double y
, double w
, double h
, double r
)
316 cairo_move_to(ctx
, x
+r
, y
);
317 cairo_line_to(ctx
, x
+w
, y
);
318 cairo_line_to(ctx
, x
+w
, y
+h
);
319 cairo_line_to(ctx
, x
, y
+h
);
320 cairo_line_to(ctx
, x
, y
+r
);
322 /* Left upper corner is rounded */
323 cairo_curve_to(ctx
, x
, y
, x
, y
, x
+r
, y
);
328 * Does the actuall drawing of the semi transparent button graphic.
331 fullscreen_button_on_expose_event (GtkWidget
*widget
, GdkEventExpose
*event G_GNUC_UNUSED
, gpointer data
)
334 GdkPixbuf
*pixbuf
= GDK_PIXBUF (data
);
337 ctx
= gdk_cairo_create (widget
->window
);
340 cairo_set_operator (ctx
, CAIRO_OPERATOR_CLEAR
);
344 cairo_set_operator (ctx
, CAIRO_OPERATOR_OVER
);
345 cairo_set_source_rgba (ctx
, 0, 0, 0, 0.60);
346 fullscreen_button_create_rectangle (ctx
, 0, 0, FULLSCREEN_BUTTON_WIDTH
, FULLSCREEN_BUTTON_HEIGHT
, FULLSCREEN_BUTTON_CORNER_RADIUS
);
350 gdk_cairo_set_source_pixbuf (ctx
, pixbuf
, 15, 10);
353 /* Destroy context */
360 * Creates the semi transparent button graphic.
363 fullscreen_button_create_gfx (HeFullscreenButton
*self
)
365 g_return_val_if_fail(HE_IS_FULLSCREEN_BUTTON(self
), NULL
);
367 GdkPixbuf
*pixbuf
= gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), FULLSCREEN_BUTTON_ICON
, FULLSCREEN_BUTTON_ICON_SIZE
, 0, NULL
);
368 GtkWidget
*img
= gtk_image_new_from_pixbuf (pixbuf
);
369 gtk_widget_show (img
);
370 g_object_unref (pixbuf
);
371 g_signal_connect (img
, "expose_event", G_CALLBACK (fullscreen_button_on_expose_event
), pixbuf
);
373 GtkWidget
*box
= gtk_event_box_new ();
374 gtk_event_box_set_visible_window (GTK_EVENT_BOX(box
), FALSE
);
375 gtk_widget_show (box
);
376 gtk_container_add (GTK_CONTAINER(box
), img
);
377 g_signal_connect (box
, "button-release-event", G_CALLBACK (fullscreen_button_on_clicked
), self
);
379 GtkWidget
*overlay
= gtk_window_new (GTK_WINDOW_POPUP
);
380 gtk_window_set_decorated (GTK_WINDOW (overlay
), FALSE
);
381 gtk_widget_set_size_request (overlay
, FULLSCREEN_BUTTON_WIDTH
, FULLSCREEN_BUTTON_HEIGHT
);
382 gtk_window_set_resizable (GTK_WINDOW (overlay
), FALSE
);
383 gtk_window_set_transient_for (GTK_WINDOW (overlay
), self
->parent_window
);
384 gtk_window_set_destroy_with_parent (GTK_WINDOW (overlay
), TRUE
);
385 gtk_container_add (GTK_CONTAINER (overlay
), box
);
387 GdkScreen
*screen
= gtk_widget_get_screen (overlay
);
388 gtk_widget_set_colormap (overlay
, gdk_screen_get_rgba_colormap (screen
));
390 gtk_widget_realize (overlay
);
396 * Called when the parent_window's is on the screen/not on the screen.
397 * Only called if parent_window is a HildonWindow (or derived from it).
399 * We check if the window is on the screen or not on the screen.
400 * If it is, and the window is in fullscreen mode, we enable the fullscreen button. If not, we disable the button.
403 fullscreen_button_on_is_topmost_changed (GObject
*object G_GNUC_UNUSED
,
404 GParamSpec
*property G_GNUC_UNUSED
,
407 HeFullscreenButton
*self
= HE_FULLSCREEN_BUTTON (data
);
409 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
410 g_return_if_fail (priv
!= NULL
);
412 /* Only run if the "act-on-state-change" property is TRUE. */
413 if (!priv
->act_on_state_change
) {
417 if (hildon_window_get_is_topmost (HILDON_WINDOW(self
->parent_window
))) {
418 if (gdk_window_get_state (GTK_WIDGET (self
->parent_window
)->window
) & GDK_WINDOW_STATE_FULLSCREEN
) {
419 he_fullscreen_button_enable (self
);
423 he_fullscreen_button_disable (self
);
428 * Called, whenever the state of the parents window's GdkWindow changes.
429 * We check if the new state is fullscreen or non-fullscreen.
430 * If it is fullscreen, we enable the fullscreen button. If not, not.
433 fullscreen_button_on_window_state_changed (GtkWidget
*widget G_GNUC_UNUSED
,
434 GdkEventWindowState
*event
,
437 HeFullscreenButton
*self
= HE_FULLSCREEN_BUTTON (data
);
439 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
440 g_return_val_if_fail (priv
!= NULL
, FALSE
);
442 /* Only run if the "act-on-state-change" property is TRUE. */
443 if (!priv
->act_on_state_change
) {
447 /* Only run if this window is topmost. */
448 if (HILDON_IS_WINDOW (self
->parent_window
)) {
449 if (!hildon_window_get_is_topmost (HILDON_WINDOW(self
->parent_window
))) {
454 if (event
->changed_mask
& GDK_WINDOW_STATE_FULLSCREEN
) {
456 if (event
->new_window_state
== GDK_WINDOW_STATE_FULLSCREEN
) {
457 he_fullscreen_button_enable (self
);
459 he_fullscreen_button_disable (self
);
469 * Destroys the fullscreen button and disconnects itself from the parent window.
472 fullscreen_button_destroy (GtkWidget
*parent_window G_GNUC_UNUSED
, HeFullscreenButton
*self
)
474 g_return_if_fail (self
!= NULL
);
476 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
477 g_return_if_fail (priv
!= NULL
);
479 if (self
->parent_window
!= NULL
) {
480 g_signal_handlers_disconnect_by_func (self
->parent_window
, fullscreen_button_destroy
, self
);
481 g_signal_handlers_disconnect_by_func (self
->parent_window
, fullscreen_button_on_window_state_changed
, self
);
482 if (HILDON_IS_WINDOW(parent_window
)) {
483 g_signal_handlers_disconnect_by_func (self
->parent_window
, fullscreen_button_on_is_topmost_changed
, self
);
487 he_fullscreen_button_disable (self
);
489 if (priv
->overlay
!= NULL
&& GTK_IS_WIDGET(priv
->overlay
)) {
490 gtk_widget_destroy (GTK_WIDGET(priv
->overlay
));
491 priv
->overlay
= NULL
;
497 * Called when the size allocation of the parent window changes.
498 * We change the position of the fullscreen button to always be in
499 * the lower right corner.
502 fullscreen_button_on_parent_size_changed (GtkWidget
*widget
,
503 GtkAllocation
*allocation
,
506 g_return_if_fail (widget
!= NULL
);
507 g_return_if_fail (allocation
!= NULL
);
509 HeFullscreenButton
*self
= HE_FULLSCREEN_BUTTON(user_data
);
510 g_return_if_fail (self
!= NULL
);
512 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
513 g_return_if_fail (priv
!= NULL
);
515 GtkWidget
*ui_win
= GTK_WIDGET(priv
->overlay
);
516 g_return_if_fail (ui_win
!= NULL
);
518 if (gdk_window_is_visible(priv
->overlay
->window
)) {
519 fullscreen_button_set_position(self
);
525 * Default handler for the "clicked" signal. If no user handler is
526 * defined we call gtk_window_unfullscreen() on the parent window.
527 * Otherwise only the user handler is executed.
530 fullscreen_button_clicked_default_handler (HeFullscreenButton
*self
)
532 guint signal_id
= g_signal_lookup ("clicked", HE_TYPE_FULLSCREEN_BUTTON
);
533 gulong handler_id
= g_signal_handler_find (self
, G_SIGNAL_MATCH_ID
, signal_id
, 0, NULL
, NULL
, NULL
);
535 /* Run only, if no signal handler is connected */
536 if (handler_id
== 0) {
537 GtkWindow
*window
= he_fullscreen_button_get_window (self
);
538 gtk_window_unfullscreen (window
);
543 * Create new full screen button instance.
544 * This function attaches the full screen button to the given parent window.
545 * By default, the button automatically becomes visible if the parent window
546 * changes to fullscreen and vice versa. Change the "act-on-state-change"
547 * property to modify this behaviour.
549 * If you destroy the parent window, this HeFullscreenButton instance get
552 * Pass it a HildonWindow (or one of its deriatives) to ensure the widget disables/
553 * enables itself on focus-out/focus-in respectively.
555 * @param parent_window A GtkWindow instance.
556 * @return New HeFullscreenButton instance.
559 he_fullscreen_button_new (GtkWindow
*parent_window
)
561 g_return_val_if_fail (parent_window
!= NULL
, NULL
);
562 g_return_val_if_fail (GTK_IS_WINDOW (parent_window
), NULL
);
564 HeFullscreenButton
*self
= g_object_new (HE_TYPE_FULLSCREEN_BUTTON
, NULL
);
566 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
567 g_return_val_if_fail (priv
!= NULL
, NULL
);
569 self
->parent_window
= parent_window
;
570 priv
->overlay
= fullscreen_button_create_gfx (self
);
572 g_signal_connect (parent_window
, "destroy",
573 G_CALLBACK(fullscreen_button_destroy
), self
);
575 g_signal_connect (parent_window
, "window-state-event",
576 G_CALLBACK(fullscreen_button_on_window_state_changed
), self
);
578 g_signal_connect_after (parent_window
, "size-allocate",
579 G_CALLBACK(fullscreen_button_on_parent_size_changed
), self
);
581 if (HILDON_IS_WINDOW(parent_window
)) {
582 g_signal_connect (parent_window
, "notify::is-topmost",
583 G_CALLBACK(fullscreen_button_on_is_topmost_changed
), self
);
590 * Returns the GtkWidget displaying the actual overlaid button.
592 * @param self An instance of HeFullscreenButton
593 * @return The GtkWidget of the overlaid button. This widget belongs to HeFullscreenButton and must not be destroyed or modified.
596 he_fullscreen_button_get_overlay (HeFullscreenButton
*self
)
598 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
599 g_return_val_if_fail (priv
!= NULL
, NULL
);
601 return priv
->overlay
;
605 * Returns the GtkWindow that this HeFullscreenButton
608 * @param self An instance of HeFullscreenButton
609 * @return The GtkWindow to which this button is attached to
612 he_fullscreen_button_get_window (HeFullscreenButton
*self
)
614 return self
->parent_window
;
627 static guint signals
[LAST_SIGNAL
] = {0};
631 PROP_ACT_ON_STATE_CHANGE
635 fullscreen_button_get_property (GObject
*object
,
640 HeFullscreenButton
*self
= HE_FULLSCREEN_BUTTON (object
);
641 g_return_if_fail (self
);
643 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
644 g_return_if_fail (priv
!= NULL
);
646 switch (property_id
) {
647 case PROP_ACT_ON_STATE_CHANGE
:
648 g_value_set_boolean (value
, priv
->act_on_state_change
);
652 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, property_id
, pspec
);
658 fullscreen_button_set_property (GObject
*object
,
663 HeFullscreenButton
*self
= HE_FULLSCREEN_BUTTON (object
);
664 g_return_if_fail (self
);
666 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
667 g_return_if_fail (priv
!= NULL
);
669 switch (property_id
) {
670 case PROP_ACT_ON_STATE_CHANGE
:
671 priv
->act_on_state_change
= g_value_get_boolean (value
);
672 g_object_notify (G_OBJECT (object
), "act-on-state-change");
676 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, property_id
, pspec
);
683 he_fullscreen_button_class_init (HeFullscreenButtonClass
*klass
)
685 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
687 object_class
->get_property
= fullscreen_button_get_property
;
688 object_class
->set_property
= fullscreen_button_set_property
;
690 klass
->clicked
= fullscreen_button_clicked_default_handler
;
693 * HeFullscreenButton::clicked
695 * Emitted when the #HeFullscreenButton was clicked by the user.
700 HE_TYPE_FULLSCREEN_BUTTON
,
702 G_STRUCT_OFFSET(HeFullscreenButtonClass
, clicked
),
704 g_cclosure_marshal_VOID__VOID
,
708 g_object_class_install_property(
709 object_class
, PROP_ACT_ON_STATE_CHANGE
,
710 g_param_spec_boolean ("act-on-state-change",
711 "Act on window state changes",
712 "Whether to automatically enable/disable the button "
713 "when its parent window fullscreens/unfullscreens "
718 g_type_class_add_private (klass
, sizeof (HeFullscreenButtonPrivate
));
723 he_fullscreen_button_init (HeFullscreenButton
*self
)
725 g_return_if_fail (self
!= NULL
);
727 HeFullscreenButtonPrivate
*priv
= HE_FULLSCREEN_BUTTON_GET_PRIVATE (self
);
728 g_return_if_fail (priv
!= NULL
);
730 memset (priv
, 0, sizeof (HeFullscreenButtonPrivate
));
732 self
->parent_window
= NULL
;
733 priv
->overlay
= NULL
;
734 priv
->release_event
= TRUE
;
735 priv
->last_event_time
= 0;
737 priv
->act_on_state_change
= TRUE
;
739 priv
->button_press_signal_id
= 0;
740 priv
->button_release_signal_id
= 0;
742 priv
->button_press_hook_id
= 0;
743 priv
->button_release_hook_id
= 0;