gtklist.c 62.6 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* 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
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10
 * 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
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser 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
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * 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/. 
 */

27 28
#undef GTK_DISABLE_DEPRECATED

29 30
#include <string.h> /* memset */

Elliot Lee's avatar
Elliot Lee committed
31 32 33 34
#include "gtklist.h"
#include "gtklistitem.h"
#include "gtkmain.h"
#include "gtksignal.h"
35
#include "gtklabel.h"
36
#include "gtkmarshalers.h"
Elliot Lee's avatar
Elliot Lee committed
37 38 39 40 41 42 43 44

enum {
  SELECTION_CHANGED,
  SELECT_CHILD,
  UNSELECT_CHILD,
  LAST_SIGNAL
};

45 46 47 48 49
enum {
  ARG_0,
  ARG_SELECTION_MODE
};

50 51
#define SCROLL_TIME  100

52
/*** GtkList Methods ***/
53 54
static void gtk_list_class_init	     (GtkListClass   *klass);
static void gtk_list_init	     (GtkList	     *list);
55 56 57 58 59 60
static void gtk_list_set_arg         (GtkObject      *object,
				      GtkArg         *arg,
				      guint           arg_id);
static void gtk_list_get_arg         (GtkObject      *object,
				      GtkArg         *arg,
				      guint           arg_id);
61
/*** GtkObject Methods ***/
Tim Janik's avatar
Tim Janik committed
62
static void gtk_list_dispose	     (GObject	     *object);
63

64
/*** GtkWidget Methods ***/
65 66 67 68 69
static void gtk_list_size_request    (GtkWidget	     *widget,
				      GtkRequisition *requisition);
static void gtk_list_size_allocate   (GtkWidget	     *widget,
				      GtkAllocation  *allocation);
static void gtk_list_realize	     (GtkWidget	     *widget);
70
static void gtk_list_unmap	     (GtkWidget	     *widget);
Owen Taylor's avatar
Owen Taylor committed
71 72
static void gtk_list_style_set	     (GtkWidget      *widget,
				      GtkStyle       *previous_style);
73 74
static gint gtk_list_motion_notify   (GtkWidget      *widget,
				      GdkEventMotion *event);
Elliot Lee's avatar
Elliot Lee committed
75 76
static gint gtk_list_button_press    (GtkWidget      *widget,
				      GdkEventButton *event);
77
static gint gtk_list_button_release  (GtkWidget	     *widget,
Elliot Lee's avatar
Elliot Lee committed
78
				      GdkEventButton *event);
79

80 81 82
static gboolean gtk_list_focus       (GtkWidget        *widget,
                                      GtkDirectionType  direction);

83
/*** GtkContainer Methods ***/
84 85 86 87 88 89 90 91 92 93 94 95
static void gtk_list_add	     (GtkContainer     *container,
				      GtkWidget        *widget);
static void gtk_list_remove	     (GtkContainer     *container,
				      GtkWidget        *widget);
static void gtk_list_forall	     (GtkContainer     *container,
				      gboolean          include_internals,
				      GtkCallback       callback,
				      gpointer          callback_data);
static GtkType gtk_list_child_type   (GtkContainer     *container);
static void gtk_list_set_focus_child (GtkContainer     *container,
				      GtkWidget        *widget);

96
/*** GtkList Private Functions ***/
97 98 99 100 101 102 103 104 105
static void gtk_list_move_focus_child      (GtkList       *list,
					    GtkScrollType  scroll_type,
					    gfloat         position);
static gint gtk_list_horizontal_timeout    (GtkWidget     *list);
static gint gtk_list_vertical_timeout      (GtkWidget     *list);
static void gtk_list_remove_items_internal (GtkList       *list,
					    GList         *items,
					    gboolean       no_unref);

106
/*** GtkList Selection Methods ***/
107 108 109 110 111
static void gtk_real_list_select_child	        (GtkList   *list,
						 GtkWidget *child);
static void gtk_real_list_unselect_child        (GtkList   *list,
						 GtkWidget *child);

112
/*** GtkList Selection Functions ***/
113 114 115 116 117 118 119 120 121 122
static void gtk_list_set_anchor                 (GtkList   *list,
					         gboolean   add_mode,
					         gint       anchor,
					         GtkWidget *undo_focus_child);
static void gtk_list_fake_unselect_all          (GtkList   *list,
			                         GtkWidget *item);
static void gtk_list_fake_toggle_row            (GtkList   *list,
					         GtkWidget *item);
static void gtk_list_update_extended_selection  (GtkList   *list,
					         gint       row);
123
static void gtk_list_reset_extended_selection   (GtkList   *list);
124

125
/*** GtkListItem Signal Functions ***/
126 127 128
static void gtk_list_signal_drag_begin         (GtkWidget      *widget,
						GdkDragContext *context,
						GtkList        *list);
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 156 157 158 159 160 161
static void gtk_list_signal_toggle_focus_row   (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_select_all         (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_unselect_all       (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_undo_selection     (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_start_selection    (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_end_selection      (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_extend_selection   (GtkListItem   *list_item,
						GtkScrollType  scroll_type,
						gfloat         position,
						gboolean       auto_start_selection,
						GtkList       *list);
static void gtk_list_signal_scroll_horizontal  (GtkListItem   *list_item,
						GtkScrollType  scroll_type,
						gfloat         position,
						GtkList       *list);
static void gtk_list_signal_scroll_vertical    (GtkListItem   *list_item,
						GtkScrollType  scroll_type,
						gfloat         position,
						GtkList       *list);
static void gtk_list_signal_toggle_add_mode    (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_item_select        (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_item_deselect      (GtkListItem   *list_item,
						GtkList       *list);
static void gtk_list_signal_item_toggle        (GtkListItem   *list_item,
						GtkList       *list);
Elliot Lee's avatar
Elliot Lee committed
162 163


164 165 166 167
static void gtk_list_drag_begin (GtkWidget      *widget,
				 GdkDragContext *context);


Elliot Lee's avatar
Elliot Lee committed
168
static GtkContainerClass *parent_class = NULL;
169
static guint list_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
170

171
static const gchar vadjustment_key[] = "gtk-vadjustment";
172
static guint        vadjustment_key_id = 0;
173
static const gchar hadjustment_key[] = "gtk-hadjustment";
174
static guint        hadjustment_key_id = 0;
Elliot Lee's avatar
Elliot Lee committed
175

176
GtkType
177
gtk_list_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
178
{
179
  static GtkType list_type = 0;
Elliot Lee's avatar
Elliot Lee committed
180 181 182

  if (!list_type)
    {
183
      static const GtkTypeInfo list_info =
Elliot Lee's avatar
Elliot Lee committed
184 185 186 187 188 189
      {
	"GtkList",
	sizeof (GtkList),
	sizeof (GtkListClass),
	(GtkClassInitFunc) gtk_list_class_init,
	(GtkObjectInitFunc) gtk_list_init,
190 191
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
192
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
193 194
      };

195
      list_type = gtk_type_unique (GTK_TYPE_CONTAINER, &list_info);
Elliot Lee's avatar
Elliot Lee committed
196 197 198 199 200 201 202 203
    }

  return list_type;
}

static void
gtk_list_class_init (GtkListClass *class)
{
204
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
205 206 207 208 209 210 211 212
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
  container_class = (GtkContainerClass*) class;

213
  parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
Elliot Lee's avatar
Elliot Lee committed
214

215 216 217
  vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
  hadjustment_key_id = g_quark_from_static_string (hadjustment_key);

Tim Janik's avatar
Tim Janik committed
218
  gobject_class->dispose = gtk_list_dispose;
219

Elliot Lee's avatar
Elliot Lee committed
220

221 222
  object_class->set_arg = gtk_list_set_arg;
  object_class->get_arg = gtk_list_get_arg;
Elliot Lee's avatar
Elliot Lee committed
223 224

  widget_class->unmap = gtk_list_unmap;
Owen Taylor's avatar
Owen Taylor committed
225
  widget_class->style_set = gtk_list_style_set;
Elliot Lee's avatar
Elliot Lee committed
226 227 228
  widget_class->realize = gtk_list_realize;
  widget_class->button_press_event = gtk_list_button_press;
  widget_class->button_release_event = gtk_list_button_release;
229
  widget_class->motion_notify_event = gtk_list_motion_notify;
Elliot Lee's avatar
Elliot Lee committed
230 231
  widget_class->size_request = gtk_list_size_request;
  widget_class->size_allocate = gtk_list_size_allocate;
232
  widget_class->drag_begin = gtk_list_drag_begin;
233 234
  widget_class->focus = gtk_list_focus;
  
Elliot Lee's avatar
Elliot Lee committed
235 236
  container_class->add = gtk_list_add;
  container_class->remove = gtk_list_remove;
237
  container_class->forall = gtk_list_forall;
238
  container_class->child_type = gtk_list_child_type;
239
  container_class->set_focus_child = gtk_list_set_focus_child;
Elliot Lee's avatar
Elliot Lee committed
240 241 242 243

  class->selection_changed = NULL;
  class->select_child = gtk_real_list_select_child;
  class->unselect_child = gtk_real_list_unselect_child;
244 245 246 247 248 249

  list_signals[SELECTION_CHANGED] =
    gtk_signal_new ("selection_changed",
		    GTK_RUN_FIRST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GtkListClass, selection_changed),
250
		    _gtk_marshal_VOID__VOID,
251 252 253 254 255 256
		    GTK_TYPE_NONE, 0);
  list_signals[SELECT_CHILD] =
    gtk_signal_new ("select_child",
		    GTK_RUN_FIRST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GtkListClass, select_child),
257
		    _gtk_marshal_VOID__OBJECT,
258 259 260 261 262 263 264
		    GTK_TYPE_NONE, 1,
		    GTK_TYPE_WIDGET);
  list_signals[UNSELECT_CHILD] =
    gtk_signal_new ("unselect_child",
		    GTK_RUN_FIRST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GtkListClass, unselect_child),
265
		    _gtk_marshal_VOID__OBJECT,
266 267
		    GTK_TYPE_NONE, 1,
		    GTK_TYPE_WIDGET);
268 269 270 271
  
  gtk_object_add_arg_type ("GtkList::selection_mode",
			   GTK_TYPE_SELECTION_MODE, GTK_ARG_READWRITE,
			   ARG_SELECTION_MODE);
Elliot Lee's avatar
Elliot Lee committed
272 273 274 275 276 277 278
}

static void
gtk_list_init (GtkList *list)
{
  list->children = NULL;
  list->selection = NULL;
279 280 281 282 283 284 285 286 287 288 289 290 291 292

  list->undo_selection = NULL;
  list->undo_unselection = NULL;

  list->last_focus_child = NULL;
  list->undo_focus_child = NULL;

  list->htimer = 0;
  list->vtimer = 0;

  list->anchor = -1;
  list->drag_pos = -1;
  list->anchor_state = GTK_STATE_SELECTED;

Elliot Lee's avatar
Elliot Lee committed
293
  list->selection_mode = GTK_SELECTION_SINGLE;
294 295
  list->drag_selection = FALSE;
  list->add_mode = FALSE;
Elliot Lee's avatar
Elliot Lee committed
296 297
}

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
static void
gtk_list_set_arg (GtkObject      *object,
		  GtkArg         *arg,
		  guint           arg_id)
{
  GtkList *list = GTK_LIST (object);
  
  switch (arg_id)
    {
    case ARG_SELECTION_MODE:
      gtk_list_set_selection_mode (list, GTK_VALUE_ENUM (*arg));
      break;
    }
}

static void
gtk_list_get_arg (GtkObject      *object,
		  GtkArg         *arg,
		  guint           arg_id)
{
  GtkList *list = GTK_LIST (object);
  
  switch (arg_id)
    {
    case ARG_SELECTION_MODE: 
      GTK_VALUE_ENUM (*arg) = list->selection_mode; 
      break;
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
331
GtkWidget*
332
gtk_list_new (void)
Elliot Lee's avatar
Elliot Lee committed
333
{
334
  return GTK_WIDGET (gtk_type_new (GTK_TYPE_LIST));
Elliot Lee's avatar
Elliot Lee committed
335 336
}

337 338 339

/* Private GtkObject Methods :
 * 
Tim Janik's avatar
Tim Janik committed
340
 * gtk_list_dispose
341
 */
342
static void
Tim Janik's avatar
Tim Janik committed
343
gtk_list_dispose (GObject *object)
344 345
{
  gtk_list_clear_items (GTK_LIST (object), 0, -1);
346

Tim Janik's avatar
Tim Janik committed
347
  G_OBJECT_CLASS (parent_class)->dispose (object);
348 349
}

350 351 352 353 354 355 356 357 358 359 360

/* Private GtkWidget Methods :
 * 
 * gtk_list_size_request
 * gtk_list_size_allocate
 * gtk_list_realize
 * gtk_list_unmap
 * gtk_list_motion_notify
 * gtk_list_button_press
 * gtk_list_button_release
 */
361
static void
362 363
gtk_list_size_request (GtkWidget      *widget,
		       GtkRequisition *requisition)
364
{
365 366 367
  GtkList *list;
  GtkWidget *child;
  GList *children;
368

369 370
  g_return_if_fail (GTK_IS_LIST (widget));
  g_return_if_fail (requisition != NULL);
371

372 373 374
  list = GTK_LIST (widget);
  requisition->width = 0;
  requisition->height = 0;
375

376 377
  children = list->children;
  while (children)
378
    {
379 380
      child = children->data;
      children = children->next;
381

382 383
      if (GTK_WIDGET_VISIBLE (child))
	{
384 385 386
	  GtkRequisition child_requisition;
	  
	  gtk_widget_size_request (child, &child_requisition);
387

388
	  requisition->width = MAX (requisition->width,
389 390
				    child_requisition.width);
	  requisition->height += child_requisition.height;
391 392
	}
    }
393

394 395
  requisition->width += GTK_CONTAINER (list)->border_width * 2;
  requisition->height += GTK_CONTAINER (list)->border_width * 2;
396

397 398
  requisition->width = MAX (requisition->width, 1);
  requisition->height = MAX (requisition->height, 1);
399 400
}

401 402 403
static void
gtk_list_size_allocate (GtkWidget     *widget,
			GtkAllocation *allocation)
Elliot Lee's avatar
Elliot Lee committed
404
{
405 406 407 408
  GtkList *list;
  GtkWidget *child;
  GtkAllocation child_allocation;
  GList *children;
Elliot Lee's avatar
Elliot Lee committed
409

410 411
  g_return_if_fail (GTK_IS_LIST (widget));
  g_return_if_fail (allocation != NULL);
Elliot Lee's avatar
Elliot Lee committed
412

413
  list = GTK_LIST (widget);
Elliot Lee's avatar
Elliot Lee committed
414

415 416 417 418 419
  widget->allocation = *allocation;
  if (GTK_WIDGET_REALIZED (widget))
    gdk_window_move_resize (widget->window,
			    allocation->x, allocation->y,
			    allocation->width, allocation->height);
420

421
  if (list->children)
Elliot Lee's avatar
Elliot Lee committed
422
    {
423 424
      child_allocation.x = GTK_CONTAINER (list)->border_width;
      child_allocation.y = GTK_CONTAINER (list)->border_width;
425
      child_allocation.width = MAX (1, (gint)allocation->width -
426
				    child_allocation.x * 2);
Elliot Lee's avatar
Elliot Lee committed
427

428
      children = list->children;
Elliot Lee's avatar
Elliot Lee committed
429

430
      while (children)
Elliot Lee's avatar
Elliot Lee committed
431
	{
432 433
	  child = children->data;
	  children = children->next;
Elliot Lee's avatar
Elliot Lee committed
434

435 436
	  if (GTK_WIDGET_VISIBLE (child))
	    {
437 438 439 440
	      GtkRequisition child_requisition;
	      gtk_widget_get_child_requisition (child, &child_requisition);
	      
	      child_allocation.height = child_requisition.height;
Elliot Lee's avatar
Elliot Lee committed
441

442
	      gtk_widget_size_allocate (child, &child_allocation);
Elliot Lee's avatar
Elliot Lee committed
443

444 445
	      child_allocation.y += child_allocation.height;
	    }
Elliot Lee's avatar
Elliot Lee committed
446 447
	}
    }
448
}
Elliot Lee's avatar
Elliot Lee committed
449

450 451 452 453 454
static void
gtk_list_realize (GtkWidget *widget)
{
  GdkWindowAttr attributes;
  gint attributes_mask;
Elliot Lee's avatar
Elliot Lee committed
455

456
  g_return_if_fail (GTK_IS_LIST (widget));
Elliot Lee's avatar
Elliot Lee committed
457

458
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
Elliot Lee's avatar
Elliot Lee committed
459

460 461 462 463 464 465 466 467 468
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
Elliot Lee's avatar
Elliot Lee committed
469

470
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
Elliot Lee's avatar
Elliot Lee committed
471

472 473 474
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
				   &attributes, attributes_mask);
  gdk_window_set_user_data (widget->window, widget);
Elliot Lee's avatar
Elliot Lee committed
475

476 477 478
  widget->style = gtk_style_attach (widget->style, widget->window);
  gdk_window_set_background (widget->window, 
			     &widget->style->base[GTK_STATE_NORMAL]);
Elliot Lee's avatar
Elliot Lee committed
479 480
}

481 482 483 484 485 486 487 488
static gboolean
list_has_grab (GtkList *list)
{
  return (GTK_WIDGET_HAS_GRAB (list) &&
	  gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (list))));
	  
}

489 490
static void
gtk_list_unmap (GtkWidget *widget)
491
{
492 493
  GtkList *list;

494
  g_return_if_fail (GTK_IS_LIST (widget));
495

496 497 498 499 500
  if (!GTK_WIDGET_MAPPED (widget))
    return;

  list = GTK_LIST (widget);

501
  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
502

503
  if (list_has_grab (list))
504 505 506
    {
      gtk_list_end_drag_selection (list);

507
      if (list->anchor != -1 && list->selection_mode == GTK_SELECTION_MULTIPLE)
508 509 510
	gtk_list_end_selection (list);
    }

511
  gdk_window_hide (widget->window);
512 513
}

514 515 516
static gint
gtk_list_motion_notify (GtkWidget      *widget,
			GdkEventMotion *event)
Elliot Lee's avatar
Elliot Lee committed
517
{
518 519 520 521 522 523 524 525 526 527
  GtkList *list;
  GtkWidget *item = NULL;
  GtkAdjustment *adj;
  GtkContainer *container;
  GList *work;
  gint x;
  gint y;
  gint row = -1;
  gint focus_row = 0;
  gint length = 0;
528

529 530
  g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
Elliot Lee's avatar
Elliot Lee committed
531

532
  list = GTK_LIST (widget);
Elliot Lee's avatar
Elliot Lee committed
533

534 535
  if (!list->drag_selection || !list->children)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
536

537
  container = GTK_CONTAINER (widget);
538

539 540
  if (event->is_hint || event->window != widget->window)
    gdk_window_get_pointer (widget->window, &x, &y, NULL);
541 542 543 544 545
  else
    {
      x = event->x;
      y = event->y;
    }
Elliot Lee's avatar
Elliot Lee committed
546

547
  adj = gtk_object_get_data_by_id (GTK_OBJECT (list), hadjustment_key_id);
Elliot Lee's avatar
Elliot Lee committed
548

549 550 551 552 553
  /* horizontal autoscrolling */
  if (adj && widget->allocation.width > adj->page_size &&
      (x < adj->value || x >= adj->value + adj->page_size))
    {
      if (list->htimer == 0)
Elliot Lee's avatar
Elliot Lee committed
554
	{
555
	  list->htimer = g_timeout_add
556 557 558 559 560
	    (SCROLL_TIME, (GtkFunction) gtk_list_horizontal_timeout, widget);
	  
	  if (!((x < adj->value && adj->value <= 0) ||
		(x > adj->value + adj->page_size &&
		 adj->value >= adj->upper - adj->page_size)))
Elliot Lee's avatar
Elliot Lee committed
561
	    {
562
	      gdouble value;
Elliot Lee's avatar
Elliot Lee committed
563

564 565 566 567
	      if (x < adj->value)
		value = adj->value + (x - adj->value) / 2 - 1;
	      else
		value = adj->value + 1 + (x - adj->value - adj->page_size) / 2;
Elliot Lee's avatar
Elliot Lee committed
568

569 570 571 572 573 574 575 576
	      gtk_adjustment_set_value (adj,
					CLAMP (value, 0.0,
					       adj->upper - adj->page_size));
	    }
	}
      else
	return FALSE;
    }
Tim Janik's avatar
Tim Janik committed
577

578 579 580 581 582
  
  /* vertical autoscrolling */
  for (work = list->children; work; length++, work = work->next)
    {
      if (row < 0)
Elliot Lee's avatar
Elliot Lee committed
583
	{
584 585 586 587 588
	  item = GTK_WIDGET (work->data);
	  if (item->allocation.y > y || 
	      (item->allocation.y <= y &&
	       item->allocation.y + item->allocation.height > y))
	    row = length;
Elliot Lee's avatar
Elliot Lee committed
589 590
	}

591 592
      if (work->data == container->focus_child)
	focus_row = length;
Elliot Lee's avatar
Elliot Lee committed
593
    }
594 595 596
  
  if (row < 0)
    row = length - 1;
Elliot Lee's avatar
Elliot Lee committed
597

598 599
  if (list->vtimer != 0)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
600

601 602
  if (!((y < 0 && focus_row == 0) ||
	(y > widget->allocation.height && focus_row >= length - 1)))
603 604 605
    list->vtimer = g_timeout_add (SCROLL_TIME,
				  (GtkFunction) gtk_list_vertical_timeout,
				  list);
Elliot Lee's avatar
Elliot Lee committed
606

607 608
  if (row != focus_row)
    gtk_widget_grab_focus (item);
609

610 611 612 613 614
  switch (list->selection_mode)
    {
    case GTK_SELECTION_BROWSE:
      gtk_list_select_child (list, item);
      break;
615
    case GTK_SELECTION_MULTIPLE:
616 617 618 619 620
      gtk_list_update_extended_selection (list, row);
      break;
    default:
      break;
    }
Elliot Lee's avatar
Elliot Lee committed
621

622
  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
623 624
}

625 626 627
static gint
gtk_list_button_press (GtkWidget      *widget,
		       GdkEventButton *event)
Elliot Lee's avatar
Elliot Lee committed
628
{
629 630
  GtkList *list;
  GtkWidget *item;
Elliot Lee's avatar
Elliot Lee committed
631

632 633
  g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
Elliot Lee's avatar
Elliot Lee committed
634

635 636
  if (event->button != 1)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
637

638 639
  list = GTK_LIST (widget);
  item = gtk_get_event_widget ((GdkEvent*) event);
Elliot Lee's avatar
Elliot Lee committed
640

641 642
  while (item && !GTK_IS_LIST_ITEM (item))
    item = item->parent;
Elliot Lee's avatar
Elliot Lee committed
643

644
  if (item && (item->parent == widget))
Elliot Lee's avatar
Elliot Lee committed
645
    {
646 647
      gint last_focus_row;
      gint focus_row;
Elliot Lee's avatar
Elliot Lee committed
648

649 650 651
      if (event->type == GDK_BUTTON_PRESS)
	{
	  gtk_grab_add (widget);
652
	  list->drag_selection = TRUE;
653
	}
654
      else if (list_has_grab (list))
655 656 657 658
	gtk_list_end_drag_selection (list);
	  
      if (!GTK_WIDGET_HAS_FOCUS(item))
	gtk_widget_grab_focus (item);
Elliot Lee's avatar
Elliot Lee committed
659

660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
      if (list->add_mode)
	{
	  list->add_mode = FALSE;
	  gtk_widget_queue_draw (item);
	}
      
      switch (list->selection_mode)
	{
	case GTK_SELECTION_SINGLE:
	  if (event->type != GDK_BUTTON_PRESS)
	    gtk_list_select_child (list, item);
	  else
	    list->undo_focus_child = item;
	  break;
	  
	case GTK_SELECTION_BROWSE:
	  break;
Elliot Lee's avatar
Elliot Lee committed
677

678
	case GTK_SELECTION_MULTIPLE:
679
	  focus_row = g_list_index (list->children, item);
Elliot Lee's avatar
Elliot Lee committed
680

681 682 683 684 685 686 687 688
	  if (list->last_focus_child)
	    last_focus_row = g_list_index (list->children,
					   list->last_focus_child);
	  else
	    {
	      last_focus_row = focus_row;
	      list->last_focus_child = item;
	    }
689

690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
	  if (event->type != GDK_BUTTON_PRESS)
	    {
	      if (list->anchor >= 0)
		{
		  gtk_list_update_extended_selection (list, focus_row);
		  gtk_list_end_selection (list);
		}
	      gtk_list_select_child (list, item);
	      break;
	    }
	      
	  if (event->state & GDK_CONTROL_MASK)
	    {
	      if (event->state & GDK_SHIFT_MASK)
		{
		  if (list->anchor < 0)
		    {
		      g_list_free (list->undo_selection);
		      g_list_free (list->undo_unselection);
		      list->undo_selection = NULL;
		      list->undo_unselection = NULL;
711

712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
		      list->anchor = last_focus_row;
		      list->drag_pos = last_focus_row;
		      list->undo_focus_child = list->last_focus_child;
		    }
		  gtk_list_update_extended_selection (list, focus_row);
		}
	      else
		{
		  if (list->anchor < 0)
		    gtk_list_set_anchor (list, TRUE,
					 focus_row, list->last_focus_child);
		  else
		    gtk_list_update_extended_selection (list, focus_row);
		}
	      break;
	    }
728

729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
	  if (event->state & GDK_SHIFT_MASK)
	    {
	      gtk_list_set_anchor (list, FALSE,
				   last_focus_row, list->last_focus_child);
	      gtk_list_update_extended_selection (list, focus_row);
	      break;
	    }

	  if (list->anchor < 0)
	    gtk_list_set_anchor (list, FALSE, focus_row,
				 list->last_focus_child);
	  else
	    gtk_list_update_extended_selection (list, focus_row);
	  break;
	  
	default:
	  break;
	}
747 748

      return TRUE;
749
    }
Elliot Lee's avatar
Elliot Lee committed
750

751 752
  return FALSE;
}
Elliot Lee's avatar
Elliot Lee committed
753

754 755 756
static gint
gtk_list_button_release (GtkWidget	*widget,
			 GdkEventButton *event)
Elliot Lee's avatar
Elliot Lee committed
757 758
{
  GtkList *list;
759
  GtkWidget *item;
Elliot Lee's avatar
Elliot Lee committed
760

761 762
  g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
Elliot Lee's avatar
Elliot Lee committed
763 764 765

  list = GTK_LIST (widget);

766 767 768
  /* we don't handle button 2 and 3 */
  if (event->button != 1)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
769

770
  if (list->drag_selection)
Elliot Lee's avatar
Elliot Lee committed
771
    {
772
      gtk_list_end_drag_selection (list);
Elliot Lee's avatar
Elliot Lee committed
773

774 775
      switch (list->selection_mode)
	{
776
	case GTK_SELECTION_MULTIPLE:
777 778 779
 	  if (!(event->state & GDK_SHIFT_MASK))
	    gtk_list_end_selection (list);
	  break;
Elliot Lee's avatar
Elliot Lee committed
780

781
	case GTK_SELECTION_SINGLE:
Elliot Lee's avatar
Elliot Lee committed
782

783 784 785 786 787 788 789 790 791 792 793 794
	  item = gtk_get_event_widget ((GdkEvent*) event);
  
	  while (item && !GTK_IS_LIST_ITEM (item))
	    item = item->parent;
	  
	  if (item && item->parent == widget)
	    {
	      if (list->undo_focus_child == item)
		gtk_list_toggle_row (list, item);
	    }
	  list->undo_focus_child = NULL;
	  break;
Elliot Lee's avatar
Elliot Lee committed
795

796 797 798
	default:
	  break;
	}
799 800

      return TRUE;
801 802 803
    }
  
  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
804 805
}

Owen Taylor's avatar
Owen Taylor committed
806 807 808 809 810 811 812 813 814
static void 
gtk_list_style_set	(GtkWidget      *widget,
			 GtkStyle       *previous_style)
{
  g_return_if_fail (widget != NULL);

  if (previous_style && GTK_WIDGET_REALIZED (widget))
    gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
}
815 816 817 818 819 820 821 822 823

/* GtkContainer Methods :
 * gtk_list_add
 * gtk_list_remove
 * gtk_list_forall
 * gtk_list_child_type
 * gtk_list_set_focus_child
 * gtk_list_focus
 */
824
static void
825 826
gtk_list_add (GtkContainer *container,
	      GtkWidget	   *widget)
827
{
828
  GList *item_list;
829

830 831
  g_return_if_fail (GTK_IS_LIST (container));
  g_return_if_fail (GTK_IS_LIST_ITEM (widget));
832

833 834 835 836
  item_list = g_list_alloc ();
  item_list->data = widget;
  
  gtk_list_append_items (GTK_LIST (container), item_list);
837 838
}

839 840 841
static void
gtk_list_remove (GtkContainer *container,
		 GtkWidget    *widget)
842
{
843 844 845 846 847 848 849 850 851 852 853 854 855
  GList *item_list;
  
  g_return_if_fail (GTK_IS_LIST (container));
  g_return_if_fail (widget != NULL);
  g_return_if_fail (container == GTK_CONTAINER (widget->parent));
  
  item_list = g_list_alloc ();
  item_list->data = widget;
  
  gtk_list_remove_items (GTK_LIST (container), item_list);
  
  g_list_free (item_list);
}
856

857 858 859 860 861
static void
gtk_list_forall (GtkContainer  *container,
		 gboolean       include_internals,
		 GtkCallback	callback,
		 gpointer	callback_data)
862
{
863 864 865
  GtkList *list;
  GtkWidget *child;
  GList *children;
866

867 868
  g_return_if_fail (GTK_IS_LIST (container));
  g_return_if_fail (callback != NULL);
869

870 871
  list = GTK_LIST (container);
  children = list->children;
872

873 874 875 876
  while (children)
    {
      child = children->data;
      children = children->next;
877

878 879 880
      (* callback) (child, callback_data);
    }
}
881

882 883 884 885
static GtkType
gtk_list_child_type (GtkContainer *container)
{
  return GTK_TYPE_LIST_ITEM;
886 887
}

888 889 890
static void
gtk_list_set_focus_child (GtkContainer *container,
			  GtkWidget    *child)
Elliot Lee's avatar
Elliot Lee committed
891
{
892
  GtkList *list;
893

894
  g_return_if_fail (GTK_IS_LIST (container));
895
 
896 897
  if (child)
    g_return_if_fail (GTK_IS_WIDGET (child));
898

899
  list = GTK_LIST (container);
900

901
  if (child != container->focus_child)
902
    {
903
      if (container->focus_child)
904 905 906 907
	{
	  list->last_focus_child = container->focus_child;
	  gtk_widget_unref (container->focus_child);
	}
908 909 910
      container->focus_child = child;
      if (container->focus_child)
        gtk_widget_ref (container->focus_child);
911 912
    }

913 914
  /* check for v adjustment */
  if (container->focus_child)
915
    {
916
      GtkAdjustment *adjustment;
917

918 919 920 921 922 923 924
      adjustment = gtk_object_get_data_by_id (GTK_OBJECT (container),
					      vadjustment_key_id);
      if (adjustment)
        gtk_adjustment_clamp_page (adjustment,
                                   container->focus_child->allocation.y,
                                   (container->focus_child->allocation.y +
                                    container->focus_child->allocation.height));
925 926 927 928 929
      switch (list->selection_mode)
	{
	case GTK_SELECTION_BROWSE:
	  gtk_list_select_child (list, child);
	  break;
930
	case GTK_SELECTION_MULTIPLE:
931 932 933 934 935 936 937 938 939 940
	  if (!list->last_focus_child && !list->add_mode)
	    {
	      list->undo_focus_child = list->last_focus_child;
	      gtk_list_unselect_all (list);
	      gtk_list_select_child (list, child);
	    }
	  break;
	default:
	  break;
	}
941
    }
Elliot Lee's avatar
Elliot Lee committed
942 943
}

944 945
static gboolean
gtk_list_focus (GtkWidget        *widget,
946
		GtkDirectionType  direction)
Elliot Lee's avatar
Elliot Lee committed
947
{
948
  gint return_val = FALSE;
949
  GtkContainer *container;
Elliot Lee's avatar
Elliot Lee committed
950

951 952
  container = GTK_CONTAINER (widget);
  
953
  if (container->focus_child == NULL ||
954 955
      !GTK_WIDGET_HAS_FOCUS (container->focus_child))
    {
956 957 958 959
      if (GTK_LIST (container)->last_focus_child)
	gtk_container_set_focus_child
	  (container, GTK_LIST (container)->last_focus_child);

960 961 962
      if (GTK_WIDGET_CLASS (parent_class)->focus)
	return_val = GTK_WIDGET_CLASS (parent_class)->focus (widget,
                                                             direction);
963
    }
964

965 966 967
  if (!return_val)
    {
      GtkList *list;
968

969
      list =  GTK_LIST (container);
970
      if (list->selection_mode == GTK_SELECTION_MULTIPLE && list->anchor >= 0)
971
	gtk_list_end_selection (list);
972 973 974

      if (container->focus_child)
	list->last_focus_child = container->focus_child;
975
    }
976

977 978
  return return_val;
}
Elliot Lee's avatar
Elliot Lee committed
979

980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007

/* Public GtkList Methods :
 *
 * gtk_list_insert_items
 * gtk_list_append_items
 * gtk_list_prepend_items
 * gtk_list_remove_items
 * gtk_list_remove_items_no_unref
 * gtk_list_clear_items
 *
 * gtk_list_child_position
 */
void
gtk_list_insert_items (GtkList *list,
		       GList   *items,
		       gint	position)
{
  GtkWidget *widget;
  GList *tmp_list;
  GList *last;
  gint nchildren;

  g_return_if_fail (GTK_IS_LIST (list));

  if (!items)
    return;

  gtk_list_end_drag_selection (list);
1008
  if (list->selection_mode == GTK_SELECTION_MULTIPLE && list->anchor >= 0)
1009 1010 1011 1012
    gtk_list_end_selection (list);

  tmp_list = items;
  while (tmp_list)
1013
    {
1014 1015
      widget = tmp_list->data;
      tmp_list = tmp_list->next;
1016

1017
      gtk_widget_set_parent (widget, GTK_WIDGET (list));
1018 1019 1020
      gtk_signal_connect (GTK_OBJECT (widget), "drag_begin",
			  GTK_SIGNAL_FUNC (gtk_list_signal_drag_begin),
			  list);
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
      gtk_signal_connect (GTK_OBJECT (widget), "toggle_focus_row",
			  GTK_SIGNAL_FUNC (gtk_list_signal_toggle_focus_row),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "select_all",
			  GTK_SIGNAL_FUNC (gtk_list_signal_select_all),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "unselect_all",
			  GTK_SIGNAL_FUNC (gtk_list_signal_unselect_all),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "undo_selection",
			  GTK_SIGNAL_FUNC (gtk_list_signal_undo_selection),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "start_selection",
			  GTK_SIGNAL_FUNC (gtk_list_signal_start_selection),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "end_selection",
			  GTK_SIGNAL_FUNC (gtk_list_signal_end_selection),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "extend_selection",
			  GTK_SIGNAL_FUNC (gtk_list_signal_extend_selection),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "scroll_horizontal",
			  GTK_SIGNAL_FUNC (gtk_list_signal_scroll_horizontal),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "scroll_vertical",
			  GTK_SIGNAL_FUNC (gtk_list_signal_scroll_vertical),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "toggle_add_mode",
			  GTK_SIGNAL_FUNC (gtk_list_signal_toggle_add_mode),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "select",
			  GTK_SIGNAL_FUNC (gtk_list_signal_item_select),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "deselect",
			  GTK_SIGNAL_FUNC (gtk_list_signal_item_deselect),
			  list);
      gtk_signal_connect (GTK_OBJECT (widget), "toggle",
			  GTK_SIGNAL_FUNC (gtk_list_signal_item_toggle),
			  list);
    }
1061

1062

1063 1064 1065 1066 1067 1068 1069
  nchildren = g_list_length (list->children);
  if ((position < 0) || (position > nchildren))
    position = nchildren;

  if (position == nchildren)
    {
      if (list->children)
1070
	{
1071 1072 1073
	  tmp_list = g_list_last (list->children);
	  tmp_list->next = items;
	  items->prev = tmp_list;
1074
	}
1075
      else
1076
	{
1077 1078 1079 1080 1081 1082 1083
	  list->children = items;
	}
    }
  else
    {
      tmp_list = g_list_nth (list->children, position);
      last = g_list_last (items);
1084

1085 1086 1087 1088 1089
      if (tmp_list->prev)
	tmp_list->prev->next = items;
      last->next = tmp_list;
      items->prev = tmp_list->prev;
      tmp_list->prev = last;
1090

1091 1092 1093
      if (tmp_list == list->children)
	list->children = items;
    }
1094
  
1095 1096 1097 1098 1099 1100 1101
  if (list->children && !list->selection &&
      (list->selection_mode == GTK_SELECTION_BROWSE))
    {
      widget = list->children->data;
      gtk_list_select_child (list, widget);
    }
}
1102

1103 1104 1105 1106 1107
void
gtk_list_append_items (GtkList *list,
		       GList   *items)
{
  g_return_if_fail (GTK_IS_LIST (list));
1108

1109 1110
  gtk_list_insert_items (list, items, -1);
}
1111

1112 1113 1114 1115 1116 1117 1118
void
gtk_list_prepend_items (GtkList *list,
			GList	*items)
{
  g_return_if_fail (GTK_IS_LIST (list));

  gtk_list_insert_items (list, items, 0);
Elliot Lee's avatar
Elliot Lee committed
1119 1120
}

1121 1122 1123
void
gtk_list_remove_items (GtkList	*list,
		       GList	*items)
Elliot Lee's avatar
Elliot Lee committed
1124
{
1125 1126
  gtk_list_remove_items_internal (list, items, FALSE);
}
Elliot Lee's avatar
Elliot Lee committed
1127

1128 1129 1130 1131 1132 1133
void
gtk_list_remove_items_no_unref (GtkList	 *list,
				GList	 *items)
{
  gtk_list_remove_items_internal (list, items, TRUE);
}
Elliot Lee's avatar
Elliot Lee committed
1134

1135 1136 1137 1138 1139
void
gtk_list_clear_items (GtkList *list,
		      gint     start,
		      gint     end)
{
1140
  GtkContainer *container;
1141
  GtkWidget *widget;
1142
  GtkWidget *new_focus_child = NULL;
1143 1144 1145 1146
  GList *start_list;
  GList *end_list;
  GList *tmp_list;
  guint nchildren;
1147
  gboolean grab_focus = FALSE;
Elliot Lee's avatar
Elliot Lee committed
1148

1149
  g_return_if_fail (GTK_IS_LIST (list));
1150

1151 1152
  nchildren = g_list_length (list->children);

1153 1154
  if (nchildren == 0)
    return;
1155

1156 1157
  if ((end < 0) || (end > nchildren))
    end = nchildren;
1158

1159 1160
  if (start >= end)
    return;
1161

1162
  container = GTK_CONTAINER (list);
1163

1164
  gtk_list_end_drag_selection (list);
1165
  if (list->selection_mode == GTK_SELECTION_MULTIPLE)
1166 1167
    {
      if (list->anchor >= 0)
1168
	gtk_list_end_selection (list);
1169

1170 1171
      gtk_list_reset_extended_selection (list);
    }
1172

1173 1174
  start_list = g_list_nth (list->children, start);
  end_list = g_list_nth (list->children, end);
1175

1176 1177 1178 1179 1180 1181 1182 1183
  if (start_list->prev)
    start_list->prev->next = end_list;
  if (end_list && end_list->prev)
    end_list->prev->next = NULL;
  if (end_list)
    end_list->prev = start_list->prev;
  if (start_list == list->children)
    list->children = end_list;
1184

1185 1186 1187 1188 1189 1190 1191
  if (container->focus_child)
    {
      if (g_list_find (start_list, container->focus_child))
	{
	  if (start_list->prev)
	    new_focus_child = start_list->prev->data;
	  else if (list->children)
1192
	    new_focus_child = list->children->data;
1193

1194 1195
	  if (GTK_WIDGET_HAS_FOCUS (container->focus_child))
	    grab_focus = TRUE;
1196
	}
1197 1198 1199 1200 1201 1202 1203 1204
    }

  tmp_list = start_list;
  while (tmp_list)
    {
      widget = tmp_list->data;
      tmp_list = tmp_list->next;

1205 1206
      gtk_widget_ref (widget);

1207 1208 1209
      if (widget->state == GTK_STATE_SELECTED)
	gtk_list_unselect_child (list, widget);

1210 1211 1212
      gtk_signal_disconnect_by_data (GTK_OBJECT (widget), (gpointer) list);
      gtk_widget_unparent (widget);
      
1213 1214 1215 1216
      if (widget == list->undo_focus_child)
	list->undo_focus_child = NULL;
      if (widget == list->last_focus_child)
	list->last_focus_child = NULL;
1217

1218
      gtk_widget_unref (widget);
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
    }

  g_list_free (start_list);

  if (new_focus_child)
    {
      if (grab_focus)
	gtk_widget_grab_focus (new_focus_child);
      else if (container->focus_child)
	gtk_container_set_focus_child (container, new_focus_child);