gtkvscrollbar.c 15 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20 21 22 23 24 25 26

/*
 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

Elliot Lee's avatar
Elliot Lee committed
27 28 29 30 31 32 33
#include "gtkvscrollbar.h"
#include "gtksignal.h"
#include "gdk/gdkkeysyms.h"


#define EPSILON 0.01

34
#define RANGE_CLASS(w)  GTK_RANGE_GET_CLASS (w)
Elliot Lee's avatar
Elliot Lee committed
35

36 37
enum {
  ARG_0,
Manish Singh's avatar
Manish Singh committed
38
  ARG_ADJUSTMENT
39
};
Elliot Lee's avatar
Elliot Lee committed
40 41 42

static void gtk_vscrollbar_class_init       (GtkVScrollbarClass *klass);
static void gtk_vscrollbar_init             (GtkVScrollbar      *vscrollbar);
43 44 45 46 47 48
static void gtk_vscrollbar_set_arg          (GtkObject          *object,
                                             GtkArg             *arg,
                                             guint               arg_id);
static void gtk_vscrollbar_get_arg          (GtkObject          *object,
                                             GtkArg             *arg,
                                             guint               arg_id);
Elliot Lee's avatar
Elliot Lee committed
49 50
static void gtk_vscrollbar_realize          (GtkWidget          *widget);
static void gtk_vscrollbar_size_allocate    (GtkWidget          *widget,
51
                                             GtkAllocation      *allocation);
Elliot Lee's avatar
Elliot Lee committed
52 53 54 55 56
static void gtk_vscrollbar_draw_step_forw   (GtkRange           *range);
static void gtk_vscrollbar_draw_step_back   (GtkRange           *range);
static void gtk_vscrollbar_slider_update    (GtkRange           *range);
static void gtk_vscrollbar_calc_slider_size (GtkVScrollbar      *vscrollbar);
static gint gtk_vscrollbar_trough_keys      (GtkRange *range,
57 58 59
                                             GdkEventKey *key,
                                             GtkScrollType *scroll,
                                             GtkTroughType *pos);
Elliot Lee's avatar
Elliot Lee committed
60

61 62

GtkType
63
gtk_vscrollbar_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
64
{
65 66
  static GtkType vscrollbar_type = 0;
  
Elliot Lee's avatar
Elliot Lee committed
67 68
  if (!vscrollbar_type)
    {
69
      static const GtkTypeInfo vscrollbar_info =
Elliot Lee's avatar
Elliot Lee committed
70
      {
71 72 73 74 75 76
        "GtkVScrollbar",
        sizeof (GtkVScrollbar),
        sizeof (GtkVScrollbarClass),
        (GtkClassInitFunc) gtk_vscrollbar_class_init,
        (GtkObjectInitFunc) gtk_vscrollbar_init,
        /* reserved_1 */ NULL,
77
        /* reserved_2 */ NULL,
78
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
79
      };
80 81
      
      vscrollbar_type = gtk_type_unique (GTK_TYPE_SCROLLBAR, &vscrollbar_info);
Elliot Lee's avatar
Elliot Lee committed
82
    }
83
  
Elliot Lee's avatar
Elliot Lee committed
84 85 86 87
  return vscrollbar_type;
}

static void
88
gtk_vscrollbar_class_init (GtkVScrollbarClass *class)
Elliot Lee's avatar
Elliot Lee committed
89
{
90
  GtkObjectClass *object_class;
Elliot Lee's avatar
Elliot Lee committed
91 92
  GtkWidgetClass *widget_class;
  GtkRangeClass *range_class;
93 94 95 96 97 98 99
  
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
  range_class = (GtkRangeClass*) class;
  
  gtk_object_add_arg_type ("GtkVScrollbar::adjustment",
                           GTK_TYPE_ADJUSTMENT,
100
                           GTK_ARG_READWRITE,
101 102 103 104 105
                           ARG_ADJUSTMENT);
  
  object_class->set_arg = gtk_vscrollbar_set_arg;
  object_class->get_arg = gtk_vscrollbar_get_arg;
  
Elliot Lee's avatar
Elliot Lee committed
106 107
  widget_class->realize = gtk_vscrollbar_realize;
  widget_class->size_allocate = gtk_vscrollbar_size_allocate;
108
  
Elliot Lee's avatar
Elliot Lee committed
109 110 111 112 113 114 115 116
  range_class->draw_step_forw = gtk_vscrollbar_draw_step_forw;
  range_class->draw_step_back = gtk_vscrollbar_draw_step_back;
  range_class->slider_update = gtk_vscrollbar_slider_update;
  range_class->trough_click = gtk_range_default_vtrough_click;
  range_class->trough_keys = gtk_vscrollbar_trough_keys;
  range_class->motion = gtk_range_default_vmotion;
}

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
static void
gtk_vscrollbar_set_arg (GtkObject          *object,
                        GtkArg             *arg,
                        guint               arg_id)
{
  GtkVScrollbar *vscrollbar;
  
  vscrollbar = GTK_VSCROLLBAR (object);
  
  switch (arg_id)
    {
    case ARG_ADJUSTMENT:
      gtk_range_set_adjustment (GTK_RANGE (vscrollbar), GTK_VALUE_POINTER (*arg));
      break;
    default:
      break;
    }
}

static void
gtk_vscrollbar_get_arg (GtkObject          *object,
                        GtkArg             *arg,
                        guint               arg_id)
{
  GtkVScrollbar *vscrollbar;
  
  vscrollbar = GTK_VSCROLLBAR (object);
  
  switch (arg_id)
    {
    case ARG_ADJUSTMENT:
      GTK_VALUE_POINTER (*arg) = GTK_RANGE (vscrollbar);
      break;
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
156 157 158 159 160
static void
gtk_vscrollbar_init (GtkVScrollbar *vscrollbar)
{
  GtkWidget *widget;
  GtkRequisition *requisition;
161
  
Elliot Lee's avatar
Elliot Lee committed
162 163
  widget = GTK_WIDGET (vscrollbar);
  requisition = &widget->requisition;
164
  
Elliot Lee's avatar
Elliot Lee committed
165
  requisition->width = (RANGE_CLASS (widget)->slider_width +
166
                        widget->style->klass->xthickness * 2);
Elliot Lee's avatar
Elliot Lee committed
167
  requisition->height = (RANGE_CLASS (widget)->min_slider_size +
168 169 170
                         RANGE_CLASS (widget)->stepper_size +
                         RANGE_CLASS (widget)->stepper_slider_spacing +
                         widget->style->klass->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
171 172 173 174 175
}

GtkWidget*
gtk_vscrollbar_new (GtkAdjustment *adjustment)
{
176 177 178 179 180 181 182
  GtkWidget *vscrollbar;
  
  vscrollbar = gtk_widget_new (GTK_TYPE_VSCROLLBAR,
			       "adjustment", adjustment,
			       NULL);
  
  return vscrollbar;
Elliot Lee's avatar
Elliot Lee committed
183 184 185 186 187 188 189 190 191
}


static void
gtk_vscrollbar_realize (GtkWidget *widget)
{
  GtkRange *range;
  GdkWindowAttr attributes;
  gint attributes_mask;
192
  
Elliot Lee's avatar
Elliot Lee committed
193 194
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
195
  
Elliot Lee's avatar
Elliot Lee committed
196 197
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  range = GTK_RANGE (widget);
198
  
Elliot Lee's avatar
Elliot Lee committed
199 200 201 202 203 204 205 206 207 208
  attributes.x = widget->allocation.x + (widget->allocation.width - widget->requisition.width) / 2;
  attributes.y = widget->allocation.y;
  attributes.width = widget->requisition.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK |
209 210 211 212 213
                            GDK_BUTTON_PRESS_MASK |
                            GDK_BUTTON_RELEASE_MASK |
                            GDK_ENTER_NOTIFY_MASK |
                            GDK_LEAVE_NOTIFY_MASK);
  
Elliot Lee's avatar
Elliot Lee committed
214
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
215
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
216
  
Elliot Lee's avatar
Elliot Lee committed
217
  range->trough = widget->window;
218
  gdk_window_ref (range->trough);
219
  
Elliot Lee's avatar
Elliot Lee committed
220 221 222 223
  attributes.x = widget->style->klass->xthickness;
  attributes.y = widget->style->klass->ythickness;
  attributes.width = RANGE_CLASS (widget)->stepper_size;
  attributes.height = RANGE_CLASS (widget)->stepper_size;
224
  
Elliot Lee's avatar
Elliot Lee committed
225
  range->step_back = gdk_window_new (range->trough, &attributes, attributes_mask);
226
  
Elliot Lee's avatar
Elliot Lee committed
227
  attributes.y = (widget->allocation.height -
228 229 230
                  widget->style->klass->ythickness -
                  RANGE_CLASS (widget)->stepper_size);
  
Elliot Lee's avatar
Elliot Lee committed
231
  range->step_forw = gdk_window_new (range->trough, &attributes, attributes_mask);
232
  
Elliot Lee's avatar
Elliot Lee committed
233 234 235 236 237
  attributes.x = widget->style->klass->ythickness;
  attributes.y = 0;
  attributes.width = RANGE_CLASS (widget)->slider_width;
  attributes.height = RANGE_CLASS (widget)->min_slider_size;
  attributes.event_mask |= (GDK_BUTTON_MOTION_MASK |
238 239
                            GDK_POINTER_MOTION_HINT_MASK);
  
Elliot Lee's avatar
Elliot Lee committed
240
  range->slider = gdk_window_new (range->trough, &attributes, attributes_mask);
241
  
Elliot Lee's avatar
Elliot Lee committed
242 243
  gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (widget));
  gtk_range_slider_update (GTK_RANGE (widget));
244
  
Elliot Lee's avatar
Elliot Lee committed
245
  widget->style = gtk_style_attach (widget->style, widget->window);
246
  
Elliot Lee's avatar
Elliot Lee committed
247 248 249 250
  gdk_window_set_user_data (range->trough, widget);
  gdk_window_set_user_data (range->slider, widget);
  gdk_window_set_user_data (range->step_forw, widget);
  gdk_window_set_user_data (range->step_back, widget);
251
  
Elliot Lee's avatar
Elliot Lee committed
252 253 254 255
  gtk_style_set_background (widget->style, range->trough, GTK_STATE_ACTIVE);
  gtk_style_set_background (widget->style, range->slider, GTK_STATE_NORMAL);
  gtk_style_set_background (widget->style, range->step_forw, GTK_STATE_ACTIVE);
  gtk_style_set_background (widget->style, range->step_back, GTK_STATE_ACTIVE);
256
  
Elliot Lee's avatar
Elliot Lee committed
257 258 259 260 261 262 263
  gdk_window_show (range->slider);
  gdk_window_show (range->step_forw);
  gdk_window_show (range->step_back);
}

static void
gtk_vscrollbar_size_allocate (GtkWidget     *widget,
264
                              GtkAllocation *allocation)
Elliot Lee's avatar
Elliot Lee committed
265 266
{
  GtkRange *range;
267
  
Elliot Lee's avatar
Elliot Lee committed
268 269 270
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
  g_return_if_fail (allocation != NULL);
271
  
Elliot Lee's avatar
Elliot Lee committed
272 273 274 275
  widget->allocation = *allocation;
  if (GTK_WIDGET_REALIZED (widget))
    {
      range = GTK_RANGE (widget);
276
      
Elliot Lee's avatar
Elliot Lee committed
277
      gdk_window_move_resize (range->trough,
278 279 280
                              allocation->x + (allocation->width - widget->requisition.width) / 2,
                              allocation->y,
                              widget->requisition.width, allocation->height);
Elliot Lee's avatar
Elliot Lee committed
281
      gdk_window_move_resize (range->step_back,
282 283 284 285
                              widget->style->klass->xthickness,
                              widget->style->klass->ythickness,
                              widget->requisition.width - widget->style->klass->xthickness * 2,
                              RANGE_CLASS (widget)->stepper_size);
Elliot Lee's avatar
Elliot Lee committed
286
      gdk_window_move_resize (range->step_forw,
287 288 289 290 291 292
                              widget->style->klass->xthickness,
                              allocation->height - widget->style->klass->ythickness -
                              RANGE_CLASS (widget)->stepper_size,
                              widget->requisition.width - widget->style->klass->xthickness * 2,
                              RANGE_CLASS (widget)->stepper_size);
      
Elliot Lee's avatar
Elliot Lee committed
293 294 295 296 297 298 299 300 301
      gtk_range_slider_update (GTK_RANGE (widget));
    }
}

static void
gtk_vscrollbar_draw_step_forw (GtkRange *range)
{
  GtkStateType state_type;
  GtkShadowType shadow_type;
302
  
Elliot Lee's avatar
Elliot Lee committed
303 304
  g_return_if_fail (range != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (range));
305
  
Elliot Lee's avatar
Elliot Lee committed
306 307 308
  if (GTK_WIDGET_DRAWABLE (range))
    {
      if (range->in_child == RANGE_CLASS (range)->step_forw)
309 310 311 312 313 314
        {
          if (range->click_child == RANGE_CLASS (range)->step_forw)
            state_type = GTK_STATE_ACTIVE;
          else
            state_type = GTK_STATE_PRELIGHT;
        }
Elliot Lee's avatar
Elliot Lee committed
315
      else
316 317
        state_type = GTK_STATE_NORMAL;
      
Elliot Lee's avatar
Elliot Lee committed
318
      if (range->click_child == RANGE_CLASS (range)->step_forw)
319
        shadow_type = GTK_SHADOW_IN;
Elliot Lee's avatar
Elliot Lee committed
320
      else
321 322
        shadow_type = GTK_SHADOW_OUT;
      
323
      gtk_paint_arrow (GTK_WIDGET (range)->style, range->step_forw,
324 325 326 327
                       state_type, shadow_type, 
                       NULL, GTK_WIDGET (range), "vscrollbar",
                       GTK_ARROW_DOWN,
                       TRUE, 0, 0, -1, -1);
Elliot Lee's avatar
Elliot Lee committed
328 329 330 331 332 333 334 335
    }
}

static void
gtk_vscrollbar_draw_step_back (GtkRange *range)
{
  GtkStateType state_type;
  GtkShadowType shadow_type;
336
  
Elliot Lee's avatar
Elliot Lee committed
337 338
  g_return_if_fail (range != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (range));
339
  
Elliot Lee's avatar
Elliot Lee committed
340 341 342
  if (GTK_WIDGET_DRAWABLE (range))
    {
      if (range->in_child == RANGE_CLASS (range)->step_back)
343 344 345 346 347 348
        {
          if (range->click_child == RANGE_CLASS (range)->step_back)
            state_type = GTK_STATE_ACTIVE;
          else
            state_type = GTK_STATE_PRELIGHT;
        }
Elliot Lee's avatar
Elliot Lee committed
349
      else
350 351
        state_type = GTK_STATE_NORMAL;
      
Elliot Lee's avatar
Elliot Lee committed
352
      if (range->click_child == RANGE_CLASS (range)->step_back)
353
        shadow_type = GTK_SHADOW_IN;
Elliot Lee's avatar
Elliot Lee committed
354
      else
355 356
        shadow_type = GTK_SHADOW_OUT;
      
357
      gtk_paint_arrow (GTK_WIDGET (range)->style, range->step_back,
358 359 360 361
                       state_type, shadow_type, 
                       NULL, GTK_WIDGET (range), "vscrollbar",
                       GTK_ARROW_UP,
                       TRUE, 0, 0, -1, -1);
Elliot Lee's avatar
Elliot Lee committed
362 363 364 365 366 367 368 369
    }
}

static void
gtk_vscrollbar_slider_update (GtkRange *range)
{
  g_return_if_fail (range != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (range));
370
  
Elliot Lee's avatar
Elliot Lee committed
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
  gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (range));
  gtk_range_default_vslider_update (range);
}

static void
gtk_vscrollbar_calc_slider_size (GtkVScrollbar *vscrollbar)
{
  GtkRange *range;
  gint step_back_y;
  gint step_back_height;
  gint step_forw_y;
  gint slider_width;
  gint slider_height;
  gint top, bottom;
  gint height;
386
  
Elliot Lee's avatar
Elliot Lee committed
387 388
  g_return_if_fail (vscrollbar != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (vscrollbar));
389
  
Elliot Lee's avatar
Elliot Lee committed
390 391 392
  if (GTK_WIDGET_REALIZED (vscrollbar))
    {
      range = GTK_RANGE (vscrollbar);
393
      
Elliot Lee's avatar
Elliot Lee committed
394 395 396
      gdk_window_get_size (range->step_back, NULL, &step_back_height);
      gdk_window_get_position (range->step_back, NULL, &step_back_y);
      gdk_window_get_position (range->step_forw, NULL, &step_forw_y);
397
      
Elliot Lee's avatar
Elliot Lee committed
398
      top = (step_back_y +
399 400
             step_back_height +
             RANGE_CLASS (vscrollbar)->stepper_slider_spacing);
Elliot Lee's avatar
Elliot Lee committed
401 402
      bottom = step_forw_y - RANGE_CLASS (vscrollbar)->stepper_slider_spacing;
      height = bottom - top;
403
      
Elliot Lee's avatar
Elliot Lee committed
404
      if ((range->adjustment->page_size > 0) &&
405 406 407 408 409 410 411 412 413 414 415 416 417
          (range->adjustment->lower != range->adjustment->upper))
        {
          if (range->adjustment->page_size >
              (range->adjustment->upper - range->adjustment->lower))
            range->adjustment->page_size = range->adjustment->upper - range->adjustment->lower;
          
          height = (height * range->adjustment->page_size /
                    (range->adjustment->upper - range->adjustment->lower));
          
          if (height < RANGE_CLASS (vscrollbar)->min_slider_size)
            height = RANGE_CLASS (vscrollbar)->min_slider_size;
        }
      
Elliot Lee's avatar
Elliot Lee committed
418
      gdk_window_get_size (range->slider, &slider_width, &slider_height);
419
      
Elliot Lee's avatar
Elliot Lee committed
420
      if (slider_height != height)
421 422 423 424
	{
	  gdk_window_resize (range->slider, slider_width, height);
	  gdk_window_invalidate_rect (range->slider, NULL, FALSE);
	}
Elliot Lee's avatar
Elliot Lee committed
425 426 427 428 429
    }
}

static gint
gtk_vscrollbar_trough_keys(GtkRange *range,
430 431 432
                           GdkEventKey *key,
                           GtkScrollType *scroll,
                           GtkTroughType *pos)
Elliot Lee's avatar
Elliot Lee committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
{
  gint return_val = FALSE;
  switch (key->keyval)
    {
    case GDK_Up:
      return_val = TRUE;
      *scroll = GTK_SCROLL_STEP_BACKWARD;
      break;
    case GDK_Down:
      return_val = TRUE;
      *scroll = GTK_SCROLL_STEP_FORWARD;
      break;
    case GDK_Page_Up:
      return_val = TRUE;
      if (key->state & GDK_CONTROL_MASK)
448
        *pos = GTK_TROUGH_START;
Elliot Lee's avatar
Elliot Lee committed
449
      else
450
        *scroll = GTK_SCROLL_PAGE_BACKWARD;
Elliot Lee's avatar
Elliot Lee committed
451 452 453 454
      break;
    case GDK_Page_Down:
      return_val = TRUE;
      if (key->state & GDK_CONTROL_MASK)
455
        *pos = GTK_TROUGH_END;
Elliot Lee's avatar
Elliot Lee committed
456
      else
457
        *scroll = GTK_SCROLL_PAGE_FORWARD;
Elliot Lee's avatar
Elliot Lee committed
458 459 460 461
      break;
    }
  return return_val;
}