source: proiecte/PPPP/gdm/gui/simple-greeter/gdm-scrollable-widget.c @ 134

Last change on this file since 134 was 134, checked in by (none), 14 years ago

gdm sources with the modifications for webcam

File size: 27.3 KB
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2008 Red Hat, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
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 General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 *
20 * Written by: Ray Strode <rstrode@redhat.com>
21 *
22 * Parts taken from gtkscrolledwindow.c in the GTK+ toolkit.
23 */
24
25#include "config.h"
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <unistd.h>
30#include <string.h>
31#include <errno.h>
32
33#include <glib.h>
34#include <glib/gi18n.h>
35#include <gdk/gdkkeysyms.h>
36#include <gtk/gtk.h>
37
38#include "gdm-scrollable-widget.h"
39#include "gdm-timer.h"
40
41#define GDM_SCROLLABLE_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SCROLLABLE_WIDGET, GdmScrollableWidgetPrivate))
42
43enum
44{
45        SCROLL_CHILD,
46        MOVE_FOCUS_OUT,
47        NUMBER_OF_SIGNALS
48};
49
50typedef struct GdmScrollableWidgetAnimation GdmScrollableWidgetAnimation;
51
52struct GdmScrollableWidgetPrivate
53{
54        GtkWidget *scrollbar;
55
56        GdmScrollableWidgetAnimation *animation;
57        GtkWidget *invisible_event_sink;
58        guint      key_press_signal_id;
59        guint      key_release_signal_id;
60
61        GQueue    *key_event_queue;
62
63        guint      child_adjustments_stale : 1;
64};
65
66struct GdmScrollableWidgetAnimation
67{
68        GtkWidget *widget;
69        GdmTimer  *timer;
70        int        start_height;
71        int        desired_height;
72        GdmScrollableWidgetSlideStepFunc step_func;
73        gpointer   step_func_user_data;
74        GdmScrollableWidgetSlideDoneFunc done_func;
75        gpointer   done_func_user_data;
76};
77
78static void     gdm_scrollable_widget_class_init  (GdmScrollableWidgetClass *klass);
79static void     gdm_scrollable_widget_init        (GdmScrollableWidget      *clock_widget);
80static void     gdm_scrollable_widget_finalize    (GObject             *object);
81
82static guint signals[NUMBER_OF_SIGNALS] = { 0 };
83
84G_DEFINE_TYPE (GdmScrollableWidget, gdm_scrollable_widget, GTK_TYPE_BIN)
85
86static GdmScrollableWidgetAnimation *
87gdm_scrollable_widget_animation_new (GtkWidget *widget,
88                                     int        start_height,
89                                     int        desired_height,
90                                     GdmScrollableWidgetSlideStepFunc step_func,
91                                     gpointer   step_func_user_data,
92                                     GdmScrollableWidgetSlideDoneFunc done_func,
93                                     gpointer   done_func_user_data)
94{
95        GdmScrollableWidgetAnimation *animation;
96
97        animation = g_slice_new (GdmScrollableWidgetAnimation);
98
99        animation->widget = widget;
100        animation->timer = gdm_timer_new ();
101        animation->start_height = start_height;
102        animation->desired_height = desired_height;
103        animation->step_func = step_func;
104        animation->step_func_user_data = step_func_user_data;
105        animation->done_func = done_func;
106        animation->done_func_user_data = done_func_user_data;
107
108        return animation;
109}
110
111static void
112gdm_scrollable_widget_animation_free (GdmScrollableWidgetAnimation *animation)
113{
114        g_object_unref (animation->timer);
115        animation->timer = NULL;
116        g_slice_free (GdmScrollableWidgetAnimation, animation);
117}
118
119static void
120on_animation_tick (GdmScrollableWidgetAnimation *animation,
121                   double                        progress)
122{
123        int progress_in_pixels;
124        int width;
125        int height;
126
127        progress_in_pixels = progress * (animation->start_height - animation->desired_height);
128
129        height = animation->start_height - progress_in_pixels;
130
131        gtk_widget_get_size_request (animation->widget, &width, NULL);
132        gtk_widget_set_size_request (animation->widget, width, height);
133
134        if (animation->step_func != NULL) {
135                animation->step_func (GDM_SCROLLABLE_WIDGET (animation->widget),
136                                      progress,
137                                      animation->step_func_user_data);
138        }
139}
140
141static gboolean
142on_key_event (GdmScrollableWidget *scrollable_widget,
143              GdkEventKey         *key_event)
144{
145        g_queue_push_tail (scrollable_widget->priv->key_event_queue,
146                           gdk_event_copy ((GdkEvent *)key_event));
147        return FALSE;
148}
149
150static gboolean
151gdm_scrollable_redirect_input_to_event_sink (GdmScrollableWidget *scrollable_widget)
152{
153        GdkGrabStatus status;
154
155        status = gdk_pointer_grab (scrollable_widget->priv->invisible_event_sink->window,
156                          FALSE, 0, NULL, NULL, GDK_CURRENT_TIME);
157        if (status != GDK_GRAB_SUCCESS) {
158                return FALSE;
159        }
160
161        status = gdk_keyboard_grab (scrollable_widget->priv->invisible_event_sink->window,
162                           FALSE, GDK_CURRENT_TIME);
163        if (status != GDK_GRAB_SUCCESS) {
164                gdk_pointer_ungrab (GDK_CURRENT_TIME);
165                return FALSE;
166        }
167
168        scrollable_widget->priv->key_press_signal_id =
169            g_signal_connect_swapped (scrollable_widget->priv->invisible_event_sink,
170                                      "key-press-event", G_CALLBACK (on_key_event),
171                                      scrollable_widget);
172
173        scrollable_widget->priv->key_release_signal_id =
174            g_signal_connect_swapped (scrollable_widget->priv->invisible_event_sink,
175                                      "key-release-event", G_CALLBACK (on_key_event),
176                                      scrollable_widget);
177
178        return TRUE;
179}
180
181static void
182gdm_scrollable_unredirect_input (GdmScrollableWidget *scrollable_widget)
183{
184        g_signal_handler_disconnect (scrollable_widget->priv->invisible_event_sink,
185                                     scrollable_widget->priv->key_press_signal_id);
186        scrollable_widget->priv->key_press_signal_id = 0;
187
188        g_signal_handler_disconnect (scrollable_widget->priv->invisible_event_sink,
189                                     scrollable_widget->priv->key_release_signal_id);
190        scrollable_widget->priv->key_release_signal_id = 0;
191        gdk_keyboard_ungrab (GDK_CURRENT_TIME);
192        gdk_pointer_ungrab (GDK_CURRENT_TIME);
193}
194
195static void
196on_animation_stop (GdmScrollableWidgetAnimation *animation)
197{
198        GdmScrollableWidget *widget;
199        int                  width;
200
201        widget = GDM_SCROLLABLE_WIDGET (animation->widget);
202
203        if (animation->done_func != NULL) {
204                animation->done_func (widget, animation->done_func_user_data);
205        }
206
207        gdm_scrollable_widget_animation_free (widget->priv->animation);
208        widget->priv->animation = NULL;
209
210        gtk_widget_get_size_request (GTK_WIDGET (widget), &width, NULL);
211        gtk_widget_set_size_request (GTK_WIDGET (widget), width, -1);
212        gtk_widget_queue_resize (GTK_WIDGET (widget));
213
214        gdm_scrollable_unredirect_input (widget);
215}
216
217static void
218gdm_scrollable_widget_animation_start (GdmScrollableWidgetAnimation *animation)
219{
220        g_signal_connect_swapped (G_OBJECT (animation->timer), "tick",
221                                  G_CALLBACK (on_animation_tick),
222                                  animation);
223        g_signal_connect_swapped (G_OBJECT (animation->timer), "stop",
224                                  G_CALLBACK (on_animation_stop),
225                                  animation);
226        gdm_timer_start (animation->timer, .10);
227}
228
229static void
230gdm_scrollable_widget_animation_stop (GdmScrollableWidgetAnimation *animation)
231{
232        gdm_timer_stop (animation->timer);
233}
234
235static gboolean
236gdm_scrollable_widget_needs_scrollbar (GdmScrollableWidget *widget)
237{
238        GtkAdjustment *adjustment;
239
240        if (widget->priv->scrollbar == NULL) {
241                return FALSE;
242        }
243
244        if (widget->priv->animation != NULL) {
245                return FALSE;
246        }
247
248        if (widget->priv->child_adjustments_stale) {
249                return FALSE;
250        }
251
252        adjustment = gtk_range_get_adjustment (GTK_RANGE (widget->priv->scrollbar));
253
254        return adjustment->upper - adjustment->lower > adjustment->page_size;
255}
256
257static void
258gdm_scrollable_widget_size_request (GtkWidget      *widget,
259                                    GtkRequisition *requisition)
260{
261        GdmScrollableWidget *scrollable_widget;
262        GtkRequisition       child_requisition;
263        gboolean             child_adjustments_stale;
264
265        scrollable_widget = GDM_SCROLLABLE_WIDGET (widget);
266
267        requisition->width = 2 * GTK_CONTAINER (widget)->border_width;
268        requisition->height = 2 * GTK_CONTAINER (widget)->border_width;
269
270        requisition->width += 2 * widget->style->xthickness;
271        requisition->height += 2 * widget->style->ythickness;
272
273        child_adjustments_stale = FALSE;
274        if (GTK_BIN (widget)->child && GTK_WIDGET_VISIBLE (GTK_BIN (widget)->child)) {
275
276                int old_child_height;
277                gtk_widget_get_child_requisition (GTK_BIN (widget)->child,
278                                                  &child_requisition);
279                old_child_height = child_requisition.height;
280
281                gtk_widget_size_request (GTK_BIN (widget)->child,
282                                         &child_requisition);
283
284                requisition->width += child_requisition.width;
285                requisition->height += child_requisition.height;
286
287                child_adjustments_stale = old_child_height != child_requisition.height;
288        }
289
290        if (gdm_scrollable_widget_needs_scrollbar (scrollable_widget)) {
291                GtkRequisition scrollbar_requisition;
292
293                gtk_widget_show (scrollable_widget->priv->scrollbar);
294
295                gtk_widget_size_request (scrollable_widget->priv->scrollbar,
296                                         &scrollbar_requisition);
297
298                requisition->height = MAX (requisition->height,
299                                           scrollbar_requisition.height);
300                requisition->width += scrollbar_requisition.width;
301        } else {
302                gtk_widget_hide (scrollable_widget->priv->scrollbar);
303        }
304
305        scrollable_widget->priv->child_adjustments_stale = child_adjustments_stale;
306}
307
308static void
309gdm_scrollable_widget_size_allocate (GtkWidget     *widget,
310                                     GtkAllocation *allocation)
311{
312        GdmScrollableWidget *scrollable_widget;
313        GtkAllocation        scrollbar_allocation;
314        GtkAllocation        child_allocation;
315        gboolean             has_child;
316        gboolean             needs_scrollbar;
317        gboolean             is_flipped;
318
319        scrollable_widget = GDM_SCROLLABLE_WIDGET (widget);
320
321        widget->allocation = *allocation;
322
323        has_child = GTK_BIN (widget)->child && GTK_WIDGET_VISIBLE (GTK_BIN (widget)->child);
324        needs_scrollbar = gdm_scrollable_widget_needs_scrollbar (scrollable_widget);
325        is_flipped = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
326
327        if (needs_scrollbar) {
328                GtkRequisition scrollbar_requisition;
329
330                gtk_widget_get_child_requisition (scrollable_widget->priv->scrollbar,
331                                                  &scrollbar_requisition);
332
333                scrollbar_allocation.width = scrollbar_requisition.width;
334
335                if (!is_flipped) {
336                        scrollbar_allocation.x = allocation->x + allocation->width;
337                        scrollbar_allocation.x -= GTK_CONTAINER (widget)->border_width;
338                        scrollbar_allocation.x -= scrollbar_allocation.width;
339                } else {
340                        scrollbar_allocation.x = allocation->x;
341                        scrollbar_allocation.x += GTK_CONTAINER (widget)->border_width;
342                }
343
344                scrollbar_allocation.height = allocation->height;
345                scrollbar_allocation.height -= 2 * GTK_CONTAINER (widget)->border_width;
346
347                scrollbar_allocation.y = allocation->y;
348                scrollbar_allocation.y += GTK_CONTAINER (widget)->border_width;
349
350                gtk_widget_size_allocate (scrollable_widget->priv->scrollbar,
351                                          &scrollbar_allocation);
352        }
353
354        if (has_child) {
355                child_allocation.width = allocation->width;
356                child_allocation.width -= 2 * GTK_CONTAINER (widget)->border_width;
357                child_allocation.width -= 2 * widget->style->xthickness;
358
359                if (needs_scrollbar) {
360                        child_allocation.width -= scrollbar_allocation.width;
361                }
362
363                if (!is_flipped) {
364                        child_allocation.x = allocation->x;
365                        child_allocation.x += GTK_CONTAINER (widget)->border_width;
366                        child_allocation.x += widget->style->xthickness;
367                } else {
368                        child_allocation.x = allocation->x + allocation->width;
369                        child_allocation.x -= GTK_CONTAINER (widget)->border_width;
370                        child_allocation.x -= child_allocation.width;
371                        child_allocation.x -= widget->style->xthickness;
372                }
373
374                child_allocation.height = allocation->height;
375                child_allocation.height -= 2 * GTK_CONTAINER (widget)->border_width;
376                child_allocation.height -= 2 * widget->style->ythickness;
377
378                child_allocation.y = allocation->y;
379                child_allocation.y += GTK_CONTAINER (widget)->border_width;
380                child_allocation.y += widget->style->ythickness;
381
382                gtk_widget_size_allocate (GTK_BIN (widget)->child,
383                                          &child_allocation);
384                scrollable_widget->priv->child_adjustments_stale = FALSE;
385        }
386}
387
388static void
389gdm_scrollable_widget_add (GtkContainer *container,
390                           GtkWidget    *child)
391{
392        GtkAdjustment *adjustment;
393
394        GTK_CONTAINER_CLASS (gdm_scrollable_widget_parent_class)->add (container, child);
395
396        adjustment = gtk_range_get_adjustment (GTK_RANGE (GDM_SCROLLABLE_WIDGET (container)->priv->scrollbar));
397
398        g_signal_connect_swapped (adjustment, "changed",
399                                  G_CALLBACK (gtk_widget_queue_resize),
400                                  container);
401
402        gtk_widget_set_scroll_adjustments (child, NULL, adjustment);
403}
404
405static void
406gdm_scrollable_widget_remove (GtkContainer *container,
407                              GtkWidget    *child)
408{
409        gtk_widget_set_scroll_adjustments (child, NULL, NULL);
410
411        GTK_CONTAINER_CLASS (gdm_scrollable_widget_parent_class)->remove (container, child);
412}
413
414static void
415gdm_scrollable_widget_forall (GtkContainer *container,
416                              gboolean      include_internals,
417                              GtkCallback   callback,
418                              gpointer      callback_data)
419{
420
421        GdmScrollableWidget *scrollable_widget;
422
423        scrollable_widget = GDM_SCROLLABLE_WIDGET (container);
424
425        GTK_CONTAINER_CLASS (gdm_scrollable_widget_parent_class)->forall (container,
426                                                                          include_internals,
427                                                                          callback,
428                                                                          callback_data);
429
430        if (!include_internals) {
431                return;
432        }
433
434        if (scrollable_widget->priv->scrollbar != NULL) {
435                callback (scrollable_widget->priv->scrollbar, callback_data);
436        }
437}
438
439static void
440gdm_scrollable_widget_destroy (GtkObject *object)
441{
442        GdmScrollableWidget *scrollable_widget;
443
444        scrollable_widget = GDM_SCROLLABLE_WIDGET (object);
445
446        gtk_widget_unparent (scrollable_widget->priv->scrollbar);
447        gtk_widget_destroy (scrollable_widget->priv->scrollbar);
448
449        GTK_OBJECT_CLASS (gdm_scrollable_widget_parent_class)->destroy (object);
450}
451
452static void
453gdm_scrollable_widget_finalize (GObject *object)
454{
455        GdmScrollableWidget *scrollable_widget;
456
457        scrollable_widget = GDM_SCROLLABLE_WIDGET (object);
458
459        g_queue_free (scrollable_widget->priv->key_event_queue);
460
461        G_OBJECT_CLASS (gdm_scrollable_widget_parent_class)->finalize (object);
462}
463
464static gboolean
465gdm_scrollable_widget_expose_event (GtkWidget      *widget,
466                                    GdkEventExpose *event)
467{
468        GdmScrollableWidget *scrollable_widget;
469        int                  x;
470        int                  y;
471        int                  width;
472        int                  height;
473        gboolean             is_flipped;
474
475        scrollable_widget = GDM_SCROLLABLE_WIDGET (widget);
476
477        if (!GTK_WIDGET_DRAWABLE (widget)) {
478                return FALSE;
479        }
480
481        is_flipped = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
482
483        x = widget->allocation.x;
484        x += 2 * GTK_CONTAINER (widget)->border_width;
485
486        width = widget->allocation.width;
487        width -= 2 * GTK_CONTAINER (widget)->border_width;
488
489        if (gdm_scrollable_widget_needs_scrollbar (scrollable_widget)) {
490                width -= scrollable_widget->priv->scrollbar->allocation.width;
491
492                if (is_flipped) {
493                        x += scrollable_widget->priv->scrollbar->allocation.width;
494                }
495        }
496
497        y = widget->allocation.y;
498        y += 2 * GTK_CONTAINER (widget)->border_width;
499
500        height = widget->allocation.height;
501        height -= 2 * GTK_CONTAINER (widget)->border_width;
502
503        gtk_paint_shadow (widget->style, widget->window,
504                          GTK_WIDGET_STATE (widget), GTK_SHADOW_IN,
505                          &event->area, widget, "scrolled_window",
506                          x, y, width, height);
507
508        return GTK_WIDGET_CLASS (gdm_scrollable_widget_parent_class)->expose_event (widget, event);
509}
510
511static gboolean
512gdm_scrollable_widget_scroll_event (GtkWidget      *widget,
513                                    GdkEventScroll *event)
514{
515        if (event->direction != GDK_SCROLL_UP && event->direction != GDK_SCROLL_DOWN) {
516                return FALSE;
517        }
518
519        if (!GTK_WIDGET_VISIBLE (GTK_WIDGET (widget))) {
520                return FALSE;
521        }
522
523        return gtk_widget_event (GDM_SCROLLABLE_WIDGET (widget)->priv->scrollbar,
524                                 (GdkEvent *) event);
525}
526
527static void
528add_scroll_binding (GtkBindingSet  *binding_set,
529                    guint           keyval,
530                    GdkModifierType mask,
531                    GtkScrollType   scroll)
532{
533        guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
534
535        gtk_binding_entry_add_signal (binding_set, keyval, mask,
536                                      "scroll-child", 1,
537                                      GTK_TYPE_SCROLL_TYPE, scroll);
538        gtk_binding_entry_add_signal (binding_set, keypad_keyval, mask,
539                                      "scroll-child", 1,
540                                      GTK_TYPE_SCROLL_TYPE, scroll);
541}
542
543static void
544add_tab_bindings (GtkBindingSet    *binding_set,
545                  GdkModifierType   modifiers,
546                  GtkDirectionType  direction)
547{
548        gtk_binding_entry_add_signal (binding_set, GDK_Tab, modifiers,
549                                      "move-focus-out", 1,
550                                      GTK_TYPE_DIRECTION_TYPE, direction);
551        gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers,
552                                      "move-focus-out", 1,
553                                      GTK_TYPE_DIRECTION_TYPE, direction);
554}
555
556static void
557gdm_scrollable_widget_class_install_bindings (GdmScrollableWidgetClass *klass)
558{
559        GtkBindingSet *binding_set;
560
561        binding_set = gtk_binding_set_by_class (klass);
562
563        add_scroll_binding (binding_set, GDK_Up, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD);
564        add_scroll_binding (binding_set, GDK_Down, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD);
565
566        add_scroll_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
567        add_scroll_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_FORWARD);
568
569        add_scroll_binding (binding_set, GDK_Home, 0, GTK_SCROLL_START);
570        add_scroll_binding (binding_set, GDK_End, 0, GTK_SCROLL_END);
571
572        add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
573        add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
574}
575
576static void
577gdm_scrollable_widget_class_init (GdmScrollableWidgetClass *klass)
578{
579        GObjectClass             *object_class;
580        GtkObjectClass           *gtk_object_class;
581        GtkWidgetClass           *widget_class;
582        GtkContainerClass        *container_class;
583        GdmScrollableWidgetClass *scrollable_widget_class;
584
585        object_class = G_OBJECT_CLASS (klass);
586        gtk_object_class = GTK_OBJECT_CLASS (klass);
587        widget_class = GTK_WIDGET_CLASS (klass);
588        container_class = GTK_CONTAINER_CLASS (klass);
589        scrollable_widget_class = GDM_SCROLLABLE_WIDGET_CLASS (klass);
590
591        object_class->finalize = gdm_scrollable_widget_finalize;
592
593        gtk_object_class->destroy = gdm_scrollable_widget_destroy;
594
595        widget_class->size_request = gdm_scrollable_widget_size_request;
596        widget_class->size_allocate = gdm_scrollable_widget_size_allocate;
597        widget_class->expose_event = gdm_scrollable_widget_expose_event;
598        widget_class->scroll_event = gdm_scrollable_widget_scroll_event;
599
600        container_class->add = gdm_scrollable_widget_add;
601        container_class->remove = gdm_scrollable_widget_remove;
602        container_class->forall = gdm_scrollable_widget_forall;
603
604        signals[SCROLL_CHILD] =
605          g_signal_new ("scroll-child",
606                        G_TYPE_FROM_CLASS (object_class),
607                        G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
608                        G_STRUCT_OFFSET (GtkScrolledWindowClass, scroll_child),
609                        NULL, NULL,
610                        g_cclosure_marshal_VOID__ENUM,
611                        G_TYPE_BOOLEAN, 1,
612                        GTK_TYPE_SCROLL_TYPE);
613        signals[MOVE_FOCUS_OUT] =
614          g_signal_new ("move-focus-out",
615                        G_TYPE_FROM_CLASS (object_class),
616                        G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
617                        G_STRUCT_OFFSET (GtkScrolledWindowClass, move_focus_out),
618                        NULL, NULL,
619                        g_cclosure_marshal_VOID__ENUM,
620                        G_TYPE_NONE, 1,
621                        GTK_TYPE_DIRECTION_TYPE);
622        gdm_scrollable_widget_class_install_bindings (klass);
623
624        g_type_class_add_private (klass, sizeof (GdmScrollableWidgetPrivate));
625}
626
627static void
628gdm_scrollable_widget_add_scrollbar (GdmScrollableWidget *widget)
629{
630        gtk_widget_push_composite_child ();
631        widget->priv->scrollbar = gtk_vscrollbar_new (NULL);
632        gtk_widget_set_composite_name (widget->priv->scrollbar, "scrollbar");
633        gtk_widget_pop_composite_child ();
634        gtk_widget_set_parent (widget->priv->scrollbar, GTK_WIDGET (widget));
635        g_object_ref (widget->priv->scrollbar);
636}
637
638static void
639gdm_scrollable_widget_add_invisible_event_sink (GdmScrollableWidget *widget)
640{
641        widget->priv->invisible_event_sink =
642            gtk_invisible_new_for_screen (gtk_widget_get_screen (GTK_WIDGET (widget)));
643        gtk_widget_show (widget->priv->invisible_event_sink);
644
645        widget->priv->key_event_queue = g_queue_new ();
646}
647
648static void
649gdm_scrollable_widget_init (GdmScrollableWidget *widget)
650{
651        widget->priv = GDM_SCROLLABLE_WIDGET_GET_PRIVATE (widget);
652
653        gdm_scrollable_widget_add_scrollbar (widget);
654        gdm_scrollable_widget_add_invisible_event_sink (widget);
655}
656
657GtkWidget *
658gdm_scrollable_widget_new (void)
659{
660        GObject *object;
661
662        object = g_object_new (GDM_TYPE_SCROLLABLE_WIDGET, NULL);
663
664        return GTK_WIDGET (object);
665}
666
667static gboolean
668gdm_scrollable_widget_animations_are_disabled (GdmScrollableWidget *scrollable_widget)
669{
670        GtkSettings *settings;
671        gboolean     animations_are_enabled;
672
673        settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (scrollable_widget)));
674        g_object_get (settings, "gtk-enable-animations", &animations_are_enabled, NULL);
675
676        return animations_are_enabled == FALSE;
677}
678
679void
680gdm_scrollable_widget_stop_sliding (GdmScrollableWidget *scrollable_widget)
681{
682        g_return_if_fail (GDM_IS_SCROLLABLE_WIDGET (scrollable_widget));
683
684        if (scrollable_widget->priv->animation != NULL) {
685                gdm_scrollable_widget_animation_stop (scrollable_widget->priv->animation);
686        }
687
688        g_assert (scrollable_widget->priv->animation == NULL);
689}
690
691void
692gdm_scrollable_widget_slide_to_height (GdmScrollableWidget *scrollable_widget,
693                                       int                  height,
694                                       GdmScrollableWidgetSlideStepFunc step_func,
695                                       gpointer             step_user_data,
696                                       GdmScrollableWidgetSlideDoneFunc done_func,
697                                       gpointer             done_user_data)
698{
699        GtkWidget *widget;
700        gboolean   input_redirected;
701
702        g_return_if_fail (GDM_IS_SCROLLABLE_WIDGET (scrollable_widget));
703        widget = GTK_WIDGET (scrollable_widget);
704
705        gdm_scrollable_widget_stop_sliding (scrollable_widget);
706
707        input_redirected = gdm_scrollable_redirect_input_to_event_sink (scrollable_widget);
708
709        if (!input_redirected || gdm_scrollable_widget_animations_are_disabled (scrollable_widget)) {
710                if (step_func != NULL) {
711                        step_func (scrollable_widget, 0.0, step_user_data);
712                }
713
714                if (done_func != NULL) {
715                        done_func (scrollable_widget, done_user_data);
716                }
717
718                if (input_redirected) {
719                        gdm_scrollable_unredirect_input (scrollable_widget);
720                }
721
722                return;
723        }
724
725        height += widget->style->ythickness * 2;
726        height += GTK_CONTAINER (widget)->border_width * 2;
727
728        scrollable_widget->priv->animation =
729            gdm_scrollable_widget_animation_new (widget,
730                                                 widget->allocation.height,
731                                                 height, step_func, step_user_data,
732                                                 done_func, done_user_data);
733
734        gdm_scrollable_widget_animation_start (scrollable_widget->priv->animation);
735}
736
737gboolean
738gdm_scrollable_widget_has_queued_key_events (GdmScrollableWidget *widget)
739{
740        g_return_val_if_fail (GDM_IS_SCROLLABLE_WIDGET (widget), FALSE);
741
742        return !g_queue_is_empty (widget->priv->key_event_queue);
743}
744
745void
746gdm_scrollable_widget_replay_queued_key_events (GdmScrollableWidget *widget)
747{
748        GtkWidget *toplevel;
749        GdkEvent  *event;
750
751        toplevel = gtk_widget_get_toplevel (GTK_WIDGET (widget));
752
753        while ((event = g_queue_pop_head (widget->priv->key_event_queue)) != NULL) {
754                gtk_propagate_event (toplevel, event);
755        }
756}
Note: See TracBrowser for help on using the repository browser.