1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 
3 /*
4  * GImageView
5  * Copyright (C) 2001 Takuro Ashie
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * $Id: gimv_scrolled.c,v 1.3 2004/09/21 08:44:32 makeinu Exp $
22  */
23 
24 /*
25  *  These codes are mostly taken from Another X image viewer.
26  *
27  *  Another X image viewer Author:
28  *     David Ramboz <dramboz@users.sourceforge.net>
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 #endif
34 
35 #include "gtk2-compat.h"
36 #include "gimv_scrolled.h"
37 
38 /* for auto-scroll and auto-expand at drag */
39 #define AUTO_SCROLL_TIMEOUT    100
40 #define AUTO_SCROLL_EDGE_WIDTH 20
41 #define AUTO_SCROLL_SCALE_RATE 32 /* times / pixel */
42 typedef gboolean (*desirable_fn) (GimvScrolled *scrolled, gint x, gint y);
43 typedef gboolean (*scroll_fn)    (gpointer data);
44 
45 #define bw(widget)                  GTK_CONTAINER(widget)->border_width
46 
47 #ifdef USE_GTK2
48 #   define WIDGET_DRAW(widget, area) gdk_window_invalidate_rect (widget->window, area, TRUE)
49 #else
50 #   define WIDGET_DRAW(widget, area) (gtk_signal_emit_by_name (GTK_OBJECT(widget), "draw", area))
51 #endif
52 
53 #define adjustment_check_value(value, adj) \
54 { \
55    if (value < adj->lower) \
56       value = adj->lower; \
57    if (value > adj->upper - adj->page_size) \
58       value = adj->upper - adj->page_size; \
59 }
60 
61 enum {
62    ADJUST_ADJUSTMENTS,
63    LAST_SIGNAL
64 };
65 
66 static void     gimv_scrolled_class_init             (GimvScrolledClass  *klass);
67 static void     gimv_scrolled_init                   (GimvScrolled       *scrolled);
68 static void     gimv_scrolled_set_scroll_adjustments (GtkWidget      *widget,
69                                                  GtkAdjustment  *hadjustment,
70                                                  GtkAdjustment  *vadjustment);
71 static gint     gimv_scrolled_button_press           (GtkWidget      *widget,
72                                                  GdkEventButton *event);
73 static gint     gimv_scrolled_button_release         (GtkWidget      *widget,
74                                                  GdkEventButton *event);
75 static gint     gimv_scrolled_motion_notify          (GtkWidget      *widget,
76                                                  GdkEventMotion *event);
77 static gint     gimv_scrolled_drag_motion            (GtkWidget      *widget,
78                                                  GdkDragContext *context,
79                                                  gint            x,
80                                                  gint            y,
81                                                  guint           time);
82 static void     gimv_scrolled_drag_leave             (GtkWidget      *dirtree,
83                                                  GdkDragContext *context,
84                                                  guint           time);
85 
86 static void     hadjustment_value_changed      (GtkAdjustment   *hadjustment,
87                                                 gpointer         data);
88 static void     vadjustment_value_changed      (GtkAdjustment   *vadjustment,
89                                                 gpointer         data);
90 static gboolean horizontal_timeout             (gpointer         data);
91 static gboolean vertical_timeout               (gpointer         data);
92 #ifndef USE_GTK2
93 static void     check_exposures                (GtkWidget       *widget);
94 #endif /* USE_GTK2 */
95 
96 
97 static GtkWidgetClass *parent_class                   = NULL;
98 static guint           gimv_scrolled_signals [LAST_SIGNAL] = {0};
99 
100 
101 GtkType
gimv_scrolled_get_type(void)102 gimv_scrolled_get_type (void)
103 {
104    static GtkType type = 0;
105 
106    if (!type) {
107       static const GtkTypeInfo type_info = {
108          "GimvScrolled",
109          sizeof (GimvScrolled),
110          sizeof (GimvScrolledClass),
111          (GtkClassInitFunc) gimv_scrolled_class_init,
112          (GtkObjectInitFunc) gimv_scrolled_init,
113          /* reserved_1 */ NULL,
114          /* reserved_2 */ NULL,
115          (GtkClassInitFunc) NULL,
116       };
117 
118       type = gtk_type_unique (GTK_TYPE_CONTAINER, &type_info);
119    }
120 
121    return type;
122 }
123 
124 
125 static void
gimv_scrolled_class_init(GimvScrolledClass * klass)126 gimv_scrolled_class_init (GimvScrolledClass *klass)
127 {
128    GtkObjectClass *object_class;
129    GtkWidgetClass *widget_class;
130    GtkContainerClass *container_class;
131 
132    parent_class    = gtk_type_class (GTK_TYPE_CONTAINER);
133 
134    object_class    = (GtkObjectClass*) klass;
135    widget_class    = (GtkWidgetClass*) klass;
136    container_class = (GtkContainerClass*) klass;
137 
138    widget_class->set_scroll_adjustments_signal =
139       gtk_signal_new ("set_scroll_adjustments",
140                       GTK_RUN_LAST,
141                       GTK_CLASS_TYPE(object_class),
142                       GTK_SIGNAL_OFFSET(GimvScrolledClass, set_scroll_adjustments),
143                       gtk_marshal_NONE__POINTER_POINTER,
144                       GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
145 
146    gimv_scrolled_signals[ADJUST_ADJUSTMENTS] =
147       gtk_signal_new ("adjust_adjustments",
148                       GTK_RUN_FIRST,
149                       GTK_CLASS_TYPE(object_class),
150                       GTK_SIGNAL_OFFSET(GimvScrolledClass, adjust_adjustments),
151                       gtk_marshal_NONE__NONE,
152                       GTK_TYPE_NONE, 0);
153 
154    widget_class->button_press_event     = gimv_scrolled_button_press;
155    widget_class->button_release_event   = gimv_scrolled_button_release;
156    widget_class->motion_notify_event    = gimv_scrolled_motion_notify;
157    widget_class->drag_motion            = gimv_scrolled_drag_motion;
158    widget_class->drag_leave             = gimv_scrolled_drag_leave;
159 
160    klass->set_scroll_adjustments = gimv_scrolled_set_scroll_adjustments;
161    klass->adjust_adjustments     = NULL;
162 }
163 
164 static void
gimv_scrolled_init(GimvScrolled * scrolled)165 gimv_scrolled_init (GimvScrolled *scrolled)
166 {
167    scrolled->x_offset         = 0;
168    scrolled->y_offset         = 0;
169    scrolled->h_adjustment     = NULL;
170    scrolled->v_adjustment     = NULL;
171    scrolled->freeze_count     = 0;
172    scrolled->copy_gc          = NULL;
173 
174    /* for auto scroll */
175    scrolled->autoscroll_flags = 0;
176    scrolled->scroll_edge_x    = AUTO_SCROLL_EDGE_WIDTH;
177    scrolled->scroll_edge_y    = AUTO_SCROLL_EDGE_WIDTH;
178    scrolled->x_step           = -1;
179    scrolled->y_step           = -1;
180    scrolled->x_interval       = AUTO_SCROLL_TIMEOUT;
181    scrolled->y_interval       = AUTO_SCROLL_TIMEOUT;
182    scrolled->pressed          = FALSE;
183    scrolled->drag_start_vx    = -1;
184    scrolled->drag_start_vy    = -1;
185    scrolled->drag_motion_x    = -1;
186    scrolled->drag_motion_y    = -1;
187    scrolled->step_scale       = 0.0;
188    scrolled->hscroll_timer_id = -1;
189    scrolled->vscroll_timer_id = -1;
190 }
191 
192 void
gimv_scrolled_realize(GimvScrolled * scrolled)193 gimv_scrolled_realize (GimvScrolled *scrolled)
194 {
195    g_return_if_fail (scrolled && GTK_WIDGET(scrolled)->window);
196 
197    scrolled->copy_gc = gdk_gc_new (GTK_WIDGET(scrolled)->window);
198    gdk_gc_set_exposures (scrolled->copy_gc, TRUE);
199 }
200 
201 void
gimv_scrolled_unrealize(GimvScrolled * scrolled)202 gimv_scrolled_unrealize (GimvScrolled *scrolled)
203 {
204    g_return_if_fail (scrolled);
205 
206    gdk_gc_destroy (scrolled->copy_gc);
207 }
208 
209 static void
hadjustment_value_changed(GtkAdjustment * hadjustment,gpointer data)210 hadjustment_value_changed (GtkAdjustment *hadjustment,
211                            gpointer data)
212 {
213    GimvScrolled *scrolled;
214    GtkWidget *widget;
215    GdkRectangle area;
216    int value, diff;
217 
218    widget = GTK_WIDGET(data);
219    scrolled = GIMV_SCROLLED(data);
220 
221    if (!GTK_WIDGET_DRAWABLE(widget) || scrolled->x_offset == hadjustment->value)
222       return;
223 
224    value = hadjustment->value;
225 
226    if (value < 0)
227       value = 0;
228 
229    if (scrolled->freeze_count) {
230       scrolled->x_offset = value;
231       return;
232    }
233 
234    if (value > scrolled->x_offset) {
235       diff = value - scrolled->x_offset;
236 
237       if (diff >= widget->allocation.width) {
238          scrolled->x_offset = value;
239          WIDGET_DRAW(widget, NULL);
240          return;
241       }
242 
243       if (diff > 0 && !scrolled->freeze_count)
244          gdk_window_copy_area (widget->window,
245                                scrolled->copy_gc,
246                                0, 0,
247                                widget->window,
248                                diff, 0,
249                                widget->allocation.width  - 2 * bw (widget) - diff,
250                                widget->allocation.height - 2 * bw (widget));
251 
252       area.x = widget->allocation.width - 2 * bw (widget) - diff;
253       area.y = 0;
254       area.width = diff;
255       area.height = widget->allocation.height - 2 * bw (widget);
256 
257    } else {
258       diff = scrolled->x_offset - value;
259 
260       if (diff >= widget->allocation.width) {
261          scrolled->x_offset = value;
262          WIDGET_DRAW(widget, NULL);
263          return;
264       }
265 
266       if (diff > 0)
267          gdk_window_copy_area (widget->window,
268                                scrolled->copy_gc,
269                                diff, 0,
270                                widget->window,
271                                0, 0,
272                                widget->allocation.width  - 2 * bw (widget) - diff,
273                                widget->allocation.height - 2 * bw (widget));
274 
275       area.x = 0;
276       area.y = 0;
277       area.width = diff;
278       area.height = widget->allocation.height - 2 * bw (widget);
279    }
280 
281 
282    if (diff >  0) {
283       scrolled->x_offset = value;
284 #ifndef USE_GTK2
285       check_exposures (widget);
286 #endif /* USE_GTK2 */
287       /* WIDGET_DRAW(widget, &area); */
288       WIDGET_DRAW(widget, NULL);
289    }
290 }
291 
292 
293 static void
vadjustment_value_changed(GtkAdjustment * vadjustment,gpointer data)294 vadjustment_value_changed (GtkAdjustment *vadjustment,
295                            gpointer data)
296 {
297    GimvScrolled *scrolled;
298    GtkWidget *widget;
299    GdkRectangle area;
300    int value, diff;
301 
302    widget = GTK_WIDGET(data);
303    scrolled = GIMV_SCROLLED(data);
304 
305    if (!GTK_WIDGET_DRAWABLE(widget) || scrolled->y_offset == vadjustment->value)
306       return;
307 
308    value = vadjustment->value;
309 
310    if (value < 0)
311       value = 0;
312 
313    if (scrolled->freeze_count) {
314       scrolled->y_offset = value;
315       return;
316    }
317 
318    if (value > scrolled->y_offset) {
319       diff = value - scrolled->y_offset;
320 
321       if (diff >= widget->allocation.height) {
322          scrolled->y_offset = value;
323          WIDGET_DRAW(widget, NULL);
324          return;
325       }
326 
327       if (diff > 0)
328          gdk_window_copy_area (widget->window,
329                                scrolled->copy_gc,
330                                0, 0,
331                                widget->window,
332                                0, diff,
333                                widget->allocation.width  - 2 * bw (widget),
334                                widget->allocation.height - 2 * bw (widget) - diff);
335 
336       area.x = 0;
337       area.y = widget->allocation.height - 2 * bw (widget) - diff;
338       area.width = widget->allocation.width - 2 * bw (widget);
339       area.height = diff;
340 
341    } else {
342       diff = scrolled->y_offset - value;
343 
344       if (diff >= widget->allocation.height) {
345          scrolled->y_offset = value;
346          WIDGET_DRAW(widget, NULL);
347          return;
348       }
349 
350       if (diff > 0)
351          gdk_window_copy_area (widget->window,
352                                scrolled->copy_gc,
353                                0, diff,
354                                widget->window,
355                                0, 0,
356                                widget->allocation.width - 2 * bw (widget),
357                                widget->allocation.height - 2 * bw (widget) - diff);
358 
359       area.x = 0;
360       area.y = 0;
361       area.width = widget->allocation.width - 2 * bw (widget);
362       area.height = diff;
363    }
364 
365 
366    if (diff >  0) {
367       scrolled->y_offset = value;
368 #ifndef USE_GTK2
369       check_exposures (widget);
370 #endif /* USE_GTK2 */
371       /* WIDGET_DRAW(widget, &area); */
372       WIDGET_DRAW(widget, NULL);
373    }
374 }
375 
376 static void
gimv_scrolled_set_scroll_adjustments(GtkWidget * widget,GtkAdjustment * hadjustment,GtkAdjustment * vadjustment)377 gimv_scrolled_set_scroll_adjustments (GtkWidget *widget,
378                                  GtkAdjustment *hadjustment,
379                                  GtkAdjustment *vadjustment)
380 {
381    GimvScrolled *scrolled;
382    scrolled = GIMV_SCROLLED(widget);
383 
384    if (scrolled->h_adjustment != hadjustment) {
385       if (scrolled->h_adjustment) {
386          gtk_signal_disconnect_by_data (GTK_OBJECT(scrolled->h_adjustment),
387                                         scrolled);
388          gtk_object_unref (GTK_OBJECT(scrolled->h_adjustment));
389       }
390 
391       scrolled->h_adjustment = hadjustment;
392 
393       if (hadjustment) {
394          gtk_object_ref (GTK_OBJECT(hadjustment));
395          gtk_signal_connect (GTK_OBJECT(hadjustment),
396                              "value_changed",
397                              (GtkSignalFunc) hadjustment_value_changed,
398                              scrolled);
399       }
400    }
401 
402 
403    if (scrolled->v_adjustment != vadjustment) {
404       if (scrolled->v_adjustment) {
405          gtk_signal_disconnect_by_data (GTK_OBJECT(scrolled->v_adjustment),
406                                         scrolled);
407          gtk_object_unref (GTK_OBJECT(scrolled->v_adjustment));
408       }
409 
410       scrolled->v_adjustment = vadjustment;
411 
412       if (vadjustment) {
413          gtk_object_ref (GTK_OBJECT(vadjustment));
414          gtk_signal_connect (GTK_OBJECT(vadjustment),
415                              "value_changed",
416                              (GtkSignalFunc) vadjustment_value_changed,
417                              scrolled);
418       }
419    }
420 
421    gtk_signal_emit (GTK_OBJECT(scrolled),
422                     gimv_scrolled_signals[ADJUST_ADJUSTMENTS]);
423 }
424 
425 void
gimv_scrolled_freeze(GimvScrolled * scrolled)426 gimv_scrolled_freeze (GimvScrolled *scrolled)
427 {
428    g_return_if_fail (scrolled);
429    g_return_if_fail (scrolled->freeze_count != (guint) -1);
430 
431    scrolled->freeze_count ++;
432 }
433 
434 void
gimv_scrolled_thawn(GimvScrolled * scrolled)435 gimv_scrolled_thawn (GimvScrolled *scrolled)
436 {
437    g_return_if_fail (scrolled);
438    g_return_if_fail (scrolled->freeze_count);
439 
440    scrolled->freeze_count --;
441    if (!scrolled->freeze_count) {
442       gtk_signal_emit (GTK_OBJECT(scrolled),
443                        gimv_scrolled_signals [ADJUST_ADJUSTMENTS]);
444       gtk_widget_draw (GTK_WIDGET(scrolled), NULL);
445    }
446 }
447 
448 
449 #ifndef USE_GTK2
450 /* from gtkclist.c */
451 static void
check_exposures(GtkWidget * widget)452 check_exposures (GtkWidget *widget)
453 {
454    GdkEvent *event;
455 
456    if (!GTK_WIDGET_REALIZED (widget))
457       return;
458 
459    /*
460     *  Make sure graphics expose events are processed before scrolling
461     *  again
462     */
463    while ((event = gdk_event_get_graphics_expose (widget->window)) != NULL) {
464       gtk_widget_event (widget, event);
465       if (event->expose.count == 0) {
466          gdk_event_free (event);
467          break;
468       }
469       gdk_event_free (event);
470    }
471 }
472 #endif /* USE_GTK2 */
473 
474 
475 void
gimv_scrolled_page_up(GimvScrolled * scrolled)476 gimv_scrolled_page_up (GimvScrolled *scrolled)
477 {
478    GtkAdjustment *vadj;
479 
480    g_return_if_fail (scrolled);
481    g_return_if_fail (GIMV_IS_SCROLLED (scrolled));
482 
483    vadj = scrolled->v_adjustment;
484 
485    vadj->value -= vadj->page_size;
486    adjustment_check_value (vadj->value, vadj);
487 
488    gtk_signal_emit_by_name (GTK_OBJECT(vadj), "value_changed");
489 }
490 
491 
492 void
gimv_scrolled_page_down(GimvScrolled * scrolled)493 gimv_scrolled_page_down (GimvScrolled *scrolled)
494 {
495    GtkAdjustment *vadj;
496 
497    g_return_if_fail (scrolled);
498    g_return_if_fail (GIMV_IS_SCROLLED (scrolled));
499 
500    vadj = scrolled->v_adjustment;
501 
502    vadj->value += vadj->page_size;
503    adjustment_check_value (vadj->value, vadj);
504 
505    gtk_signal_emit_by_name (GTK_OBJECT(vadj), "value_changed");
506 }
507 
508 
509 void
gimv_scrolled_page_left(GimvScrolled * scrolled)510 gimv_scrolled_page_left (GimvScrolled *scrolled)
511 {
512    GtkAdjustment *hadj;
513 
514    g_return_if_fail (scrolled);
515    g_return_if_fail (GIMV_IS_SCROLLED (scrolled));
516 
517    hadj = scrolled->h_adjustment;
518 
519    hadj->value -= hadj->page_size;
520    adjustment_check_value (hadj->value, hadj);
521 
522    gtk_signal_emit_by_name (GTK_OBJECT(hadj), "value_changed");
523 }
524 
525 
526 void
gimv_scrolled_page_right(GimvScrolled * scrolled)527 gimv_scrolled_page_right (GimvScrolled *scrolled)
528 {
529    GtkAdjustment *hadj;
530 
531    g_return_if_fail (scrolled);
532    g_return_if_fail (GIMV_IS_SCROLLED (scrolled));
533 
534    hadj = scrolled->h_adjustment;
535    hadj->value = hadj->value;
536 
537    hadj->value += hadj->page_size;
538    adjustment_check_value (hadj->value, hadj);
539 
540    gtk_signal_emit_by_name (GTK_OBJECT(hadj), "value_changed");
541 }
542 
543 
544 /******************************************************************************
545  *
546  *   for auto scroll related functions
547  *
548  ******************************************************************************/
549 enum
550 {
551    HORIZONTAL,
552    VERTICAL
553 };
554 
555 static void
cancel_auto_scroll(GimvScrolled * scrolled)556 cancel_auto_scroll (GimvScrolled *scrolled)
557 {
558    g_return_if_fail (scrolled);
559    g_return_if_fail (GIMV_IS_SCROLLED (scrolled));
560 
561    /* horizontal */
562    if (scrolled->hscroll_timer_id != -1){
563       gtk_timeout_remove (scrolled->hscroll_timer_id);
564       scrolled->hscroll_timer_id = -1;
565    }
566 
567    /* vertival */
568    if (scrolled->vscroll_timer_id != -1){
569       gtk_timeout_remove (scrolled->vscroll_timer_id);
570       scrolled->vscroll_timer_id = -1;
571    }
572 }
573 
574 
575 static gboolean
scrolling_is_desirable(GimvScrolled * scrolled,gint direction,gint x,gint y,gfloat * scale)576 scrolling_is_desirable (GimvScrolled *scrolled, gint direction,
577                         gint x, gint y, gfloat *scale)
578 {
579    GtkAdjustment *adj;
580    gint pos, edge_width, flags;
581    gfloat upper;
582 
583    flags = scrolled->autoscroll_flags;
584 
585    if (direction == HORIZONTAL) {
586       if (!(flags & GIMV_SCROLLED_AUTO_SCROLL_HORIZONTAL)
587           && !(flags & GIMV_SCROLLED_AUTO_SCROLL_BOTH))
588       {
589          return FALSE;
590       }
591 
592       adj = scrolled->h_adjustment;
593       pos = x;
594       edge_width = scrolled->scroll_edge_x;
595    } else if (direction == VERTICAL) {
596       if (!(flags & GIMV_SCROLLED_AUTO_SCROLL_VERTICAL)
597           && !(flags & GIMV_SCROLLED_AUTO_SCROLL_BOTH))
598       {
599          return FALSE;
600       }
601 
602       adj = scrolled->v_adjustment;
603       pos = y;
604       edge_width = scrolled->scroll_edge_y;
605    } else {
606       return FALSE;
607    }
608 
609    upper = adj->upper - adj->page_size;
610 
611    if ((pos < edge_width) && (adj->value > adj->lower)) {
612       if (scale)
613          *scale = 1 + (0 - pos) / AUTO_SCROLL_SCALE_RATE;
614       return TRUE;
615    } else if ((pos > (adj->page_size - edge_width)) && (adj->value < upper)) {
616       if (scale)
617          *scale = 1 + (pos - adj->page_size) / AUTO_SCROLL_SCALE_RATE;
618       return TRUE;
619    }
620 
621    return 0;
622 }
623 
624 
625 static gboolean
vertical_timeout(gpointer data)626 vertical_timeout (gpointer data)
627 {
628    GimvScrolled *scrolled = data;
629    GtkAdjustment *vadj;
630    gint step;
631 
632    vadj = scrolled->v_adjustment;
633 
634    if (scrolled->y_step < 0)
635       step = vadj->step_increment;
636    else
637       step = scrolled->y_step;
638 
639    step *= scrolled->step_scale;
640 
641    if (scrolled->drag_motion_y < scrolled->scroll_edge_y)
642       vadj->value = vadj->value - step;
643    else
644       vadj->value = vadj->value + step;
645 
646    adjustment_check_value (vadj->value, vadj);
647    gtk_signal_emit_by_name (GTK_OBJECT(vadj), "value_changed");
648 
649    return TRUE;
650 }
651 
652 
653 static gboolean
horizontal_timeout(gpointer data)654 horizontal_timeout (gpointer data)
655 {
656    GimvScrolled *scrolled = data;
657    GtkAdjustment *hadj;
658    gint step;
659 
660    hadj = scrolled->h_adjustment;
661 
662    if (scrolled->x_step < 0)
663       step = hadj->step_increment;
664    else
665       step = scrolled->x_step;
666 
667    step *= scrolled->step_scale;
668 
669    if (scrolled->drag_motion_x < scrolled->scroll_edge_x)
670       hadj->value = hadj->value - step;
671    else
672       hadj->value = hadj->value + step;
673 
674    adjustment_check_value (hadj->value, hadj);
675    gtk_signal_emit_by_name (GTK_OBJECT(hadj), "value_changed");
676 
677    return TRUE;
678 }
679 
680 
681 static void
setup_drag_scroll(GimvScrolled * scrolled,gint x,gint y)682 setup_drag_scroll (GimvScrolled *scrolled,
683                    gint x, gint y)
684 {
685    GtkAdjustment *hadj, *vadj;
686    gboolean desirable;
687 
688    hadj = scrolled->h_adjustment;
689    vadj = scrolled->v_adjustment;
690 
691    cancel_auto_scroll (scrolled);
692 
693    scrolled->drag_motion_x = x;
694    scrolled->drag_motion_y = y;
695 
696    /* horizonal */
697    desirable = scrolling_is_desirable (scrolled, HORIZONTAL,
698                                        x, y, &scrolled->step_scale);
699 
700    if (desirable)
701       scrolled->hscroll_timer_id
702          = gtk_timeout_add (scrolled->x_interval,
703                             horizontal_timeout, scrolled);
704 
705    /* vertical */
706    desirable = scrolling_is_desirable (scrolled, VERTICAL,
707                                        x, y, &scrolled->step_scale);
708 
709    if (desirable)
710       scrolled->vscroll_timer_id
711          = gtk_timeout_add (scrolled->y_interval,
712                             vertical_timeout, scrolled);
713 }
714 
715 
716 static gint
gimv_scrolled_button_press(GtkWidget * widget,GdkEventButton * event)717 gimv_scrolled_button_press (GtkWidget *widget, GdkEventButton *event)
718 {
719    GimvScrolled *scrolled;
720 
721    g_return_val_if_fail (widget && event, FALSE);
722    g_return_val_if_fail (GIMV_IS_SCROLLED(widget), FALSE);
723 
724    scrolled = GIMV_SCROLLED (widget);
725 
726    scrolled->pressed = TRUE;
727    scrolled->drag_start_vx = GIMV_SCROLLED_VX (scrolled, (gint) event->x);
728    scrolled->drag_start_vy = GIMV_SCROLLED_VY (scrolled, (gint) event->y);
729 
730    return FALSE;
731 }
732 
733 
734 static gint
gimv_scrolled_button_release(GtkWidget * widget,GdkEventButton * event)735 gimv_scrolled_button_release (GtkWidget *widget, GdkEventButton *event)
736 {
737    g_return_val_if_fail (widget && event, FALSE);
738    g_return_val_if_fail (GIMV_IS_SCROLLED(widget), FALSE);
739 
740    gimv_scrolled_stop_auto_scroll (GIMV_SCROLLED (widget));
741 
742    return FALSE;
743 }
744 
745 
746 static gint
gimv_scrolled_motion_notify(GtkWidget * widget,GdkEventMotion * event)747 gimv_scrolled_motion_notify (GtkWidget *widget, GdkEventMotion *event)
748 {
749    GimvScrolled *scrolled;
750    gint flags;
751    gboolean pressed;
752 
753    g_return_val_if_fail (widget && event, FALSE);
754    g_return_val_if_fail (GIMV_IS_SCROLLED(widget), FALSE);
755 
756    scrolled = GIMV_SCROLLED (widget);
757 
758    flags = scrolled->autoscroll_flags;
759    pressed = scrolled->pressed;
760 
761    scrolled->drag_motion_x = event->x;
762    scrolled->drag_motion_y = event->y;
763 
764    if ((pressed && (flags & GIMV_SCROLLED_AUTO_SCROLL_MOTION))
765        || (flags & GIMV_SCROLLED_AUTO_SCROLL_MOTION_ALL))
766    {
767       setup_drag_scroll (scrolled, (gint) event->x, (gint) event->y);
768    }
769 
770    return FALSE;
771 }
772 
773 
774 static gint
gimv_scrolled_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)775 gimv_scrolled_drag_motion (GtkWidget *widget,
776                            GdkDragContext *context,
777                            gint x,
778                            gint y,
779                            guint time)
780 {
781    GimvScrolled *scrolled;
782    gint flags;
783 
784    g_return_val_if_fail (widget && context, FALSE);
785    g_return_val_if_fail (GIMV_IS_SCROLLED(widget), FALSE);
786 
787    scrolled = GIMV_SCROLLED (widget);
788 
789    flags = scrolled->autoscroll_flags;
790 
791    if (flags & GIMV_SCROLLED_AUTO_SCROLL_DND) {
792       setup_drag_scroll (GIMV_SCROLLED (widget), x, y);
793    }
794 
795    return FALSE;
796 }
797 
798 
799 static void
gimv_scrolled_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time)800 gimv_scrolled_drag_leave (GtkWidget *widget,
801                           GdkDragContext *context,
802                           guint time)
803 {
804    g_return_if_fail (widget && context);
805    g_return_if_fail (GIMV_IS_SCROLLED(widget));
806 
807    cancel_auto_scroll (GIMV_SCROLLED (widget));
808 }
809 
810 
811 void
gimv_scrolled_set_auto_scroll(GimvScrolled * scrolled,GimvScrolledAutoScrollFlags flags)812 gimv_scrolled_set_auto_scroll (GimvScrolled *scrolled,
813                                GimvScrolledAutoScrollFlags flags)
814 {
815    g_return_if_fail (scrolled);
816    g_return_if_fail (GIMV_IS_SCROLLED(scrolled));
817 
818    scrolled->autoscroll_flags |= flags;
819 }
820 
821 
822 void
gimv_scrolled_unset_auto_scroll(GimvScrolled * scrolled)823 gimv_scrolled_unset_auto_scroll (GimvScrolled *scrolled)
824 {
825    g_return_if_fail (scrolled);
826    g_return_if_fail (GIMV_IS_SCROLLED(scrolled));
827 
828    scrolled->autoscroll_flags = 0;
829 }
830 
831 
832 void
gimv_scrolled_set_auto_scroll_edge_width(GimvScrolled * scrolled,gint x_edge,gint y_edge)833 gimv_scrolled_set_auto_scroll_edge_width (GimvScrolled *scrolled,
834                                           gint x_edge,
835                                           gint y_edge)
836 {
837    g_return_if_fail (scrolled);
838    g_return_if_fail (GIMV_IS_SCROLLED(scrolled));
839 
840    if (x_edge < 0)
841       scrolled->scroll_edge_x = AUTO_SCROLL_EDGE_WIDTH;
842    else
843       scrolled->scroll_edge_x = x_edge;
844 
845    if (y_edge < 0)
846       scrolled->scroll_edge_y = AUTO_SCROLL_EDGE_WIDTH;
847    else
848       scrolled->scroll_edge_y = y_edge;
849 }
850 
851 
852 void
gimv_scrolled_set_h_auto_scroll_resolution(GimvScrolled * scrolled,gint step,gint interval)853 gimv_scrolled_set_h_auto_scroll_resolution (GimvScrolled *scrolled,
854                                             gint step,
855                                             gint interval)
856 {
857    g_return_if_fail (scrolled);
858    g_return_if_fail (GIMV_IS_SCROLLED(scrolled));
859 
860    scrolled->x_step = step;
861    if (interval <= 0)
862       scrolled->x_interval = AUTO_SCROLL_TIMEOUT;
863    else
864       scrolled->x_interval = interval;
865 }
866 
867 
868 void
gimv_scrolled_set_v_auto_scroll_resolution(GimvScrolled * scrolled,gint step,gint interval)869 gimv_scrolled_set_v_auto_scroll_resolution (GimvScrolled *scrolled,
870                                             gint step,
871                                             gint interval)
872 {
873    g_return_if_fail (scrolled);
874    g_return_if_fail (GIMV_IS_SCROLLED(scrolled));
875 
876    scrolled->y_step = step;
877    if (interval <= 0)
878       scrolled->y_interval = AUTO_SCROLL_TIMEOUT;
879    else
880       scrolled->y_interval = interval;
881 }
882 
883 
884 gboolean
gimv_scrolled_is_dragging(GimvScrolled * scrolled)885 gimv_scrolled_is_dragging (GimvScrolled *scrolled)
886 {
887    g_return_val_if_fail (scrolled, FALSE);
888    g_return_val_if_fail (scrolled, FALSE);
889 
890    if (scrolled->pressed
891        && scrolled->drag_motion_x >= 0
892        && scrolled->drag_motion_y >= 0)
893    {
894       return TRUE;
895    }
896 
897    return FALSE;
898 }
899 
900 
901 void
gimv_scrolled_stop_auto_scroll(GimvScrolled * scrolled)902 gimv_scrolled_stop_auto_scroll (GimvScrolled *scrolled)
903 {
904    g_return_if_fail (scrolled);
905    g_return_if_fail (GIMV_IS_SCROLLED(scrolled));
906 
907    cancel_auto_scroll (scrolled);
908 
909    scrolled->pressed = FALSE;
910    scrolled->drag_start_vx = -1;
911    scrolled->drag_start_vy = -1;
912    scrolled->drag_motion_x = -1;
913    scrolled->drag_motion_y = -1;
914 }
915