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 27
 */
#include "gtkvscrollbar.h"
#include "gtksignal.h"
#include "gdk/gdkkeysyms.h"


#define EPSILON 0.01

#define RANGE_CLASS(w)  GTK_RANGE_CLASS (GTK_OBJECT (w)->klass)

28 29 30 31
enum {
  ARG_0,
  ARG_ADJUSTMENT,
};
Elliot Lee's avatar
Elliot Lee committed
32 33 34

static void gtk_vscrollbar_class_init       (GtkVScrollbarClass *klass);
static void gtk_vscrollbar_init             (GtkVScrollbar      *vscrollbar);
35 36 37 38 39 40
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
41 42
static void gtk_vscrollbar_realize          (GtkWidget          *widget);
static void gtk_vscrollbar_size_allocate    (GtkWidget          *widget,
43
                                             GtkAllocation      *allocation);
Elliot Lee's avatar
Elliot Lee committed
44 45 46 47 48
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,
49 50 51
                                             GdkEventKey *key,
                                             GtkScrollType *scroll,
                                             GtkTroughType *pos);
Elliot Lee's avatar
Elliot Lee committed
52

53 54

GtkType
55
gtk_vscrollbar_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
56
{
57 58
  static GtkType vscrollbar_type = 0;
  
Elliot Lee's avatar
Elliot Lee committed
59 60 61 62
  if (!vscrollbar_type)
    {
      GtkTypeInfo vscrollbar_info =
      {
63 64 65 66 67 68
        "GtkVScrollbar",
        sizeof (GtkVScrollbar),
        sizeof (GtkVScrollbarClass),
        (GtkClassInitFunc) gtk_vscrollbar_class_init,
        (GtkObjectInitFunc) gtk_vscrollbar_init,
        /* reserved_1 */ NULL,
69
        /* reserved_2 */ NULL,
70
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
71
      };
72 73
      
      vscrollbar_type = gtk_type_unique (GTK_TYPE_SCROLLBAR, &vscrollbar_info);
Elliot Lee's avatar
Elliot Lee committed
74
    }
75
  
Elliot Lee's avatar
Elliot Lee committed
76 77 78 79
  return vscrollbar_type;
}

static void
80
gtk_vscrollbar_class_init (GtkVScrollbarClass *class)
Elliot Lee's avatar
Elliot Lee committed
81
{
82
  GtkObjectClass *object_class;
Elliot Lee's avatar
Elliot Lee committed
83 84
  GtkWidgetClass *widget_class;
  GtkRangeClass *range_class;
85 86 87 88 89 90 91 92 93 94 95 96 97
  
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
  range_class = (GtkRangeClass*) class;
  
  gtk_object_add_arg_type ("GtkVScrollbar::adjustment",
                           GTK_TYPE_ADJUSTMENT,
                           GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
                           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
98 99
  widget_class->realize = gtk_vscrollbar_realize;
  widget_class->size_allocate = gtk_vscrollbar_size_allocate;
100
  
Elliot Lee's avatar
Elliot Lee committed
101 102 103 104 105 106 107 108
  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;
}

109 110 111 112 113 114 115 116 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
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
148 149 150 151 152
static void
gtk_vscrollbar_init (GtkVScrollbar *vscrollbar)
{
  GtkWidget *widget;
  GtkRequisition *requisition;
153
  
Elliot Lee's avatar
Elliot Lee committed
154 155
  widget = GTK_WIDGET (vscrollbar);
  requisition = &widget->requisition;
156
  
Elliot Lee's avatar
Elliot Lee committed
157
  requisition->width = (RANGE_CLASS (widget)->slider_width +
158
                        widget->style->klass->xthickness * 2);
Elliot Lee's avatar
Elliot Lee committed
159
  requisition->height = (RANGE_CLASS (widget)->min_slider_size +
160 161 162
                         RANGE_CLASS (widget)->stepper_size +
                         RANGE_CLASS (widget)->stepper_slider_spacing +
                         widget->style->klass->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
163 164 165 166 167
}

GtkWidget*
gtk_vscrollbar_new (GtkAdjustment *adjustment)
{
168 169 170 171 172 173 174
  GtkWidget *vscrollbar;
  
  vscrollbar = gtk_widget_new (GTK_TYPE_VSCROLLBAR,
			       "adjustment", adjustment,
			       NULL);
  
  return vscrollbar;
Elliot Lee's avatar
Elliot Lee committed
175 176 177 178 179 180 181 182 183
}


static void
gtk_vscrollbar_realize (GtkWidget *widget)
{
  GtkRange *range;
  GdkWindowAttr attributes;
  gint attributes_mask;
184
  
Elliot Lee's avatar
Elliot Lee committed
185 186
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
187
  
Elliot Lee's avatar
Elliot Lee committed
188 189
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  range = GTK_RANGE (widget);
190
  
Elliot Lee's avatar
Elliot Lee committed
191 192 193 194 195 196 197 198 199 200
  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 |
201 202 203 204 205
                            GDK_BUTTON_PRESS_MASK |
                            GDK_BUTTON_RELEASE_MASK |
                            GDK_ENTER_NOTIFY_MASK |
                            GDK_LEAVE_NOTIFY_MASK);
  
Elliot Lee's avatar
Elliot Lee committed
206
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
207
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
208
  
Elliot Lee's avatar
Elliot Lee committed
209
  range->trough = widget->window;
210
  gdk_window_ref (range->trough);
211
  
Elliot Lee's avatar
Elliot Lee committed
212 213 214 215
  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;
216
  
Elliot Lee's avatar
Elliot Lee committed
217
  range->step_back = gdk_window_new (range->trough, &attributes, attributes_mask);
218
  
Elliot Lee's avatar
Elliot Lee committed
219
  attributes.y = (widget->allocation.height -
220 221 222
                  widget->style->klass->ythickness -
                  RANGE_CLASS (widget)->stepper_size);
  
Elliot Lee's avatar
Elliot Lee committed
223
  range->step_forw = gdk_window_new (range->trough, &attributes, attributes_mask);
224
  
Elliot Lee's avatar
Elliot Lee committed
225 226 227 228 229
  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 |
230 231
                            GDK_POINTER_MOTION_HINT_MASK);
  
Elliot Lee's avatar
Elliot Lee committed
232
  range->slider = gdk_window_new (range->trough, &attributes, attributes_mask);
233
  
Elliot Lee's avatar
Elliot Lee committed
234 235
  gtk_vscrollbar_calc_slider_size (GTK_VSCROLLBAR (widget));
  gtk_range_slider_update (GTK_RANGE (widget));
236
  
Elliot Lee's avatar
Elliot Lee committed
237
  widget->style = gtk_style_attach (widget->style, widget->window);
238
  
Elliot Lee's avatar
Elliot Lee committed
239 240 241 242
  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);
243
  
Elliot Lee's avatar
Elliot Lee committed
244 245 246 247
  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);
248
  
Elliot Lee's avatar
Elliot Lee committed
249 250 251 252 253 254 255
  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,
256
                              GtkAllocation *allocation)
Elliot Lee's avatar
Elliot Lee committed
257 258
{
  GtkRange *range;
259
  
Elliot Lee's avatar
Elliot Lee committed
260 261 262
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (widget));
  g_return_if_fail (allocation != NULL);
263
  
Elliot Lee's avatar
Elliot Lee committed
264 265 266 267
  widget->allocation = *allocation;
  if (GTK_WIDGET_REALIZED (widget))
    {
      range = GTK_RANGE (widget);
268
      
Elliot Lee's avatar
Elliot Lee committed
269
      gdk_window_move_resize (range->trough,
270 271 272
                              allocation->x + (allocation->width - widget->requisition.width) / 2,
                              allocation->y,
                              widget->requisition.width, allocation->height);
Elliot Lee's avatar
Elliot Lee committed
273
      gdk_window_move_resize (range->step_back,
274 275 276 277
                              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
278
      gdk_window_move_resize (range->step_forw,
279 280 281 282 283
                              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
284
      gdk_window_resize (range->slider,
285 286 287
                         widget->requisition.width - widget->style->klass->xthickness * 2,
                         RANGE_CLASS (range)->min_slider_size);
      
Elliot Lee's avatar
Elliot Lee committed
288 289 290 291 292 293 294 295 296
      gtk_range_slider_update (GTK_RANGE (widget));
    }
}

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

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

static void
gtk_vscrollbar_slider_update (GtkRange *range)
{
  g_return_if_fail (range != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (range));
365
  
Elliot Lee's avatar
Elliot Lee committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
  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;
381
  
Elliot Lee's avatar
Elliot Lee committed
382 383
  g_return_if_fail (vscrollbar != NULL);
  g_return_if_fail (GTK_IS_VSCROLLBAR (vscrollbar));
384
  
Elliot Lee's avatar
Elliot Lee committed
385 386 387
  if (GTK_WIDGET_REALIZED (vscrollbar))
    {
      range = GTK_RANGE (vscrollbar);
388
      
Elliot Lee's avatar
Elliot Lee committed
389 390 391
      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);
392
      
Elliot Lee's avatar
Elliot Lee committed
393
      top = (step_back_y +
394 395
             step_back_height +
             RANGE_CLASS (vscrollbar)->stepper_slider_spacing);
Elliot Lee's avatar
Elliot Lee committed
396 397
      bottom = step_forw_y - RANGE_CLASS (vscrollbar)->stepper_slider_spacing;
      height = bottom - top;
398
      
Elliot Lee's avatar
Elliot Lee committed
399
      if ((range->adjustment->page_size > 0) &&
400 401 402 403 404 405 406 407 408 409 410 411 412
          (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
413
      gdk_window_get_size (range->slider, &slider_width, &slider_height);
414
      
Elliot Lee's avatar
Elliot Lee committed
415
      if (slider_height != height)
416
        gdk_window_resize (range->slider, slider_width, height);
Elliot Lee's avatar
Elliot Lee committed
417 418 419 420 421
    }
}

static gint
gtk_vscrollbar_trough_keys(GtkRange *range,
422 423 424
                           GdkEventKey *key,
                           GtkScrollType *scroll,
                           GtkTroughType *pos)
Elliot Lee's avatar
Elliot Lee committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
{
  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)
440
        *pos = GTK_TROUGH_START;
Elliot Lee's avatar
Elliot Lee committed
441
      else
442
        *scroll = GTK_SCROLL_PAGE_BACKWARD;
Elliot Lee's avatar
Elliot Lee committed
443 444 445 446
      break;
    case GDK_Page_Down:
      return_val = TRUE;
      if (key->state & GDK_CONTROL_MASK)
447
        *pos = GTK_TROUGH_END;
Elliot Lee's avatar
Elliot Lee committed
448
      else
449
        *scroll = GTK_SCROLL_PAGE_FORWARD;
Elliot Lee's avatar
Elliot Lee committed
450 451 452 453
      break;
    }
  return return_val;
}