gtklist.c 63.3 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10
/* 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
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
Elliot Lee's avatar
Elliot Lee committed
12 13 14
 * 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
 */
#include "gtklist.h"
#include "gtklistitem.h"
#include "gtkmain.h"
#include "gtksignal.h"
23
#include "gtklabel.h"
Elliot Lee's avatar
Elliot Lee committed
24 25 26 27 28 29 30 31

enum {
  SELECTION_CHANGED,
  SELECT_CHILD,
  UNSELECT_CHILD,
  LAST_SIGNAL
};

32 33
#define SCROLL_TIME  100

34
/** GtkList Methods **/
35 36
static void gtk_list_class_init	     (GtkListClass   *klass);
static void gtk_list_init	     (GtkList	     *list);
37 38

/** GtkObject Methods **/
39
static void gtk_list_shutdown	     (GtkObject	     *object);
40 41 42 43 44 45 46

/** GtkWidget Methods **/
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);
47 48
static void gtk_list_map	     (GtkWidget	     *widget);
static void gtk_list_unmap	     (GtkWidget	     *widget);
Owen Taylor's avatar
Owen Taylor committed
49 50
static void gtk_list_style_set	     (GtkWidget      *widget,
				      GtkStyle       *previous_style);
51
static void gtk_list_draw	     (GtkWidget	     *widget,
Elliot Lee's avatar
Elliot Lee committed
52
				      GdkRectangle   *area);
53
static gint gtk_list_expose	     (GtkWidget	     *widget,
Elliot Lee's avatar
Elliot Lee committed
54
				      GdkEventExpose *event);
55 56
static gint gtk_list_motion_notify   (GtkWidget      *widget,
				      GdkEventMotion *event);
Elliot Lee's avatar
Elliot Lee committed
57 58
static gint gtk_list_button_press    (GtkWidget      *widget,
				      GdkEventButton *event);
59
static gint gtk_list_button_release  (GtkWidget	     *widget,
Elliot Lee's avatar
Elliot Lee committed
60
				      GdkEventButton *event);
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 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

/** GtkContainer Methods **/
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);
static gint gtk_list_focus           (GtkContainer     *container,
				      GtkDirectionType  direction);

/** GtkList Private Functions **/
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);

/** GtkList Selection Methods **/
static void gtk_real_list_select_child	        (GtkList   *list,
						 GtkWidget *child);
static void gtk_real_list_unselect_child        (GtkList   *list,
						 GtkWidget *child);

/** GtkList Selection Functions **/
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);

/** GtkListItem Signal Functions **/
static void gtk_list_signal_focus_lost         (GtkWidget     *item,
						GdkEventKey   *event,
						GtkList       *list);
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
142 143 144


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

147 148 149 150
static const gchar *vadjustment_key = "gtk-vadjustment";
static guint        vadjustment_key_id = 0;
static const gchar *hadjustment_key = "gtk-hadjustment";
static guint        hadjustment_key_id = 0;
Elliot Lee's avatar
Elliot Lee committed
151

152
GtkType
153
gtk_list_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
154
{
155
  static GtkType list_type = 0;
Elliot Lee's avatar
Elliot Lee committed
156 157 158

  if (!list_type)
    {
159
      static const GtkTypeInfo list_info =
Elliot Lee's avatar
Elliot Lee committed
160 161 162 163 164 165
      {
	"GtkList",
	sizeof (GtkList),
	sizeof (GtkListClass),
	(GtkClassInitFunc) gtk_list_class_init,
	(GtkObjectInitFunc) gtk_list_init,
166 167
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
168
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
169 170
      };

171
      list_type = gtk_type_unique (GTK_TYPE_CONTAINER, &list_info);
Elliot Lee's avatar
Elliot Lee committed
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    }

  return list_type;
}

static void
gtk_list_class_init (GtkListClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

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

188
  parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
Elliot Lee's avatar
Elliot Lee committed
189

190 191 192
  vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
  hadjustment_key_id = g_quark_from_static_string (hadjustment_key);

Elliot Lee's avatar
Elliot Lee committed
193 194
  list_signals[SELECTION_CHANGED] =
    gtk_signal_new ("selection_changed",
195 196 197
		    GTK_RUN_FIRST,
		    object_class->type,
		    GTK_SIGNAL_OFFSET (GtkListClass, selection_changed),
198
		    gtk_marshal_NONE__NONE,
Elliot Lee's avatar
Elliot Lee committed
199 200 201
		    GTK_TYPE_NONE, 0);
  list_signals[SELECT_CHILD] =
    gtk_signal_new ("select_child",
202 203 204
		    GTK_RUN_FIRST,
		    object_class->type,
		    GTK_SIGNAL_OFFSET (GtkListClass, select_child),
205
		    gtk_marshal_NONE__POINTER,
Elliot Lee's avatar
Elliot Lee committed
206
		    GTK_TYPE_NONE, 1,
207
		    GTK_TYPE_WIDGET);
Elliot Lee's avatar
Elliot Lee committed
208 209
  list_signals[UNSELECT_CHILD] =
    gtk_signal_new ("unselect_child",
210 211 212
		    GTK_RUN_FIRST,
		    object_class->type,
		    GTK_SIGNAL_OFFSET (GtkListClass, unselect_child),
213
		    gtk_marshal_NONE__POINTER,
Elliot Lee's avatar
Elliot Lee committed
214
		    GTK_TYPE_NONE, 1,
215
		    GTK_TYPE_WIDGET);
Elliot Lee's avatar
Elliot Lee committed
216 217 218

  gtk_object_class_add_signals (object_class, list_signals, LAST_SIGNAL);

219
  object_class->shutdown = gtk_list_shutdown;
Elliot Lee's avatar
Elliot Lee committed
220 221 222

  widget_class->map = gtk_list_map;
  widget_class->unmap = gtk_list_unmap;
Owen Taylor's avatar
Owen Taylor committed
223
  widget_class->style_set = gtk_list_style_set;
Elliot Lee's avatar
Elliot Lee committed
224 225 226 227 228
  widget_class->realize = gtk_list_realize;
  widget_class->draw = gtk_list_draw;
  widget_class->expose_event = gtk_list_expose;
  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 232 233 234
  widget_class->size_request = gtk_list_size_request;
  widget_class->size_allocate = gtk_list_size_allocate;

  container_class->add = gtk_list_add;
  container_class->remove = gtk_list_remove;
235
  container_class->forall = gtk_list_forall;
236
  container_class->child_type = gtk_list_child_type;
237 238
  container_class->set_focus_child = gtk_list_set_focus_child;
  container_class->focus = gtk_list_focus;
Elliot Lee's avatar
Elliot Lee committed
239 240 241 242 243 244 245 246 247 248 249

  class->selection_changed = NULL;
  class->select_child = gtk_real_list_select_child;
  class->unselect_child = gtk_real_list_unselect_child;
}

static void
gtk_list_init (GtkList *list)
{
  list->children = NULL;
  list->selection = NULL;
250 251 252 253 254 255 256 257 258 259 260 261 262 263

  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
264
  list->selection_mode = GTK_SELECTION_SINGLE;
265 266
  list->drag_selection = FALSE;
  list->add_mode = FALSE;
Elliot Lee's avatar
Elliot Lee committed
267 268 269
}

GtkWidget*
270
gtk_list_new (void)
Elliot Lee's avatar
Elliot Lee committed
271
{
272
  return GTK_WIDGET (gtk_type_new (GTK_TYPE_LIST));
Elliot Lee's avatar
Elliot Lee committed
273 274
}

275 276 277 278 279

/* Private GtkObject Methods :
 * 
 * gtk_list_shutdown
 */
280 281 282 283 284 285 286
static void
gtk_list_shutdown (GtkObject *object)
{
  gtk_list_clear_items (GTK_LIST (object), 0, -1);
  GTK_OBJECT_CLASS (parent_class)->shutdown (object);
}

287 288 289 290 291 292 293 294 295 296 297 298

/* Private GtkWidget Methods :
 * 
 * gtk_list_size_request
 * gtk_list_size_allocate
 * gtk_list_realize
 * gtk_list_map
 * gtk_list_unmap
 * gtk_list_motion_notify
 * gtk_list_button_press
 * gtk_list_button_release
 */
299
static void
300 301
gtk_list_size_request (GtkWidget      *widget,
		       GtkRequisition *requisition)
302
{
303 304 305
  GtkList *list;
  GtkWidget *child;
  GList *children;
306

307 308 309
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_LIST (widget));
  g_return_if_fail (requisition != NULL);
310

311 312 313
  list = GTK_LIST (widget);
  requisition->width = 0;
  requisition->height = 0;
314

315 316
  children = list->children;
  while (children)
317
    {
318 319
      child = children->data;
      children = children->next;
320

321 322 323
      if (GTK_WIDGET_VISIBLE (child))
	{
	  gtk_widget_size_request (child, &child->requisition);
324

325 326 327 328 329
	  requisition->width = MAX (requisition->width,
				    child->requisition.width);
	  requisition->height += child->requisition.height;
	}
    }
330

331 332
  requisition->width += GTK_CONTAINER (list)->border_width * 2;
  requisition->height += GTK_CONTAINER (list)->border_width * 2;
333

334 335
  requisition->width = MAX (requisition->width, 1);
  requisition->height = MAX (requisition->height, 1);
336 337
}

338 339 340
static void
gtk_list_size_allocate (GtkWidget     *widget,
			GtkAllocation *allocation)
Elliot Lee's avatar
Elliot Lee committed
341
{
342 343 344 345
  GtkList *list;
  GtkWidget *child;
  GtkAllocation child_allocation;
  GList *children;
Elliot Lee's avatar
Elliot Lee committed
346

347 348 349
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_LIST (widget));
  g_return_if_fail (allocation != NULL);
Elliot Lee's avatar
Elliot Lee committed
350

351
  list = GTK_LIST (widget);
Elliot Lee's avatar
Elliot Lee committed
352

353 354 355 356 357
  widget->allocation = *allocation;
  if (GTK_WIDGET_REALIZED (widget))
    gdk_window_move_resize (widget->window,
			    allocation->x, allocation->y,
			    allocation->width, allocation->height);
358

359
  if (list->children)
Elliot Lee's avatar
Elliot Lee committed
360
    {
361 362 363 364
      child_allocation.x = GTK_CONTAINER (list)->border_width;
      child_allocation.y = GTK_CONTAINER (list)->border_width;
      child_allocation.width = MAX (1, allocation->width -
				    child_allocation.x * 2);
Elliot Lee's avatar
Elliot Lee committed
365

366
      children = list->children;
Elliot Lee's avatar
Elliot Lee committed
367

368
      while (children)
Elliot Lee's avatar
Elliot Lee committed
369
	{
370 371
	  child = children->data;
	  children = children->next;
Elliot Lee's avatar
Elliot Lee committed
372

373 374 375
	  if (GTK_WIDGET_VISIBLE (child))
	    {
	      child_allocation.height = child->requisition.height;
Elliot Lee's avatar
Elliot Lee committed
376

377
	      gtk_widget_size_allocate (child, &child_allocation);
Elliot Lee's avatar
Elliot Lee committed
378

379 380
	      child_allocation.y += child_allocation.height;
	    }
Elliot Lee's avatar
Elliot Lee committed
381 382
	}
    }
383
}
Elliot Lee's avatar
Elliot Lee committed
384

385 386 387 388 389
static void
gtk_list_realize (GtkWidget *widget)
{
  GdkWindowAttr attributes;
  gint attributes_mask;
Elliot Lee's avatar
Elliot Lee committed
390

391 392
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_LIST (widget));
Elliot Lee's avatar
Elliot Lee committed
393

394
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
Elliot Lee's avatar
Elliot Lee committed
395

396 397 398 399 400 401 402 403 404
  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
405

406
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
Elliot Lee's avatar
Elliot Lee committed
407

408 409 410
  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
411

412 413 414
  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
415 416
}

417
static void
418
gtk_list_map (GtkWidget *widget)
Elliot Lee's avatar
Elliot Lee committed
419
{
420 421 422
  GtkList *list;
  GtkWidget *child;
  GList *children;
423

424 425
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_LIST (widget));
426

427 428
  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
  list = GTK_LIST (widget);
429

430
  gdk_window_show (widget->window);
431

432 433
  children = list->children;
  while (children)
Elliot Lee's avatar
Elliot Lee committed
434
    {
435 436
      child = children->data;
      children = children->next;
437

438 439 440
      if (GTK_WIDGET_VISIBLE (child) &&
	  !GTK_WIDGET_MAPPED (child))
	gtk_widget_map (child);
Elliot Lee's avatar
Elliot Lee committed
441 442 443
    }
}

444 445
static void
gtk_list_unmap (GtkWidget *widget)
446
{
447 448
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_LIST (widget));
449

450 451
  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
  gdk_window_hide (widget->window);
452 453
}

454 455 456
static gint
gtk_list_motion_notify (GtkWidget      *widget,
			GdkEventMotion *event)
Elliot Lee's avatar
Elliot Lee committed
457
{
458 459 460 461 462 463 464 465 466 467
  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;
468

469 470 471
  g_return_val_if_fail (widget != NULL, FALSE);
  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
472

473
  list = GTK_LIST (widget);
Elliot Lee's avatar
Elliot Lee committed
474

475 476
  if (!list->drag_selection || !list->children)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
477

478
  container = GTK_CONTAINER (widget);
479

480 481
  if (event->is_hint || event->window != widget->window)
    gdk_window_get_pointer (widget->window, &x, &y, NULL);
Elliot Lee's avatar
Elliot Lee committed
482

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

485 486 487 488 489
  /* 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
490
	{
491 492 493 494 495 496
	  list->htimer = gtk_timeout_add
	    (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
497
	    {
498
	      gfloat value;
Elliot Lee's avatar
Elliot Lee committed
499

500 501 502 503
	      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
504

505 506 507 508 509 510 511 512
	      gtk_adjustment_set_value (adj,
					CLAMP (value, 0.0,
					       adj->upper - adj->page_size));
	    }
	}
      else
	return FALSE;
    }
Tim Janik's avatar
Tim Janik committed
513

514 515 516 517 518
  
  /* vertical autoscrolling */
  for (work = list->children; work; length++, work = work->next)
    {
      if (row < 0)
Elliot Lee's avatar
Elliot Lee committed
519
	{
520 521 522 523 524
	  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
525 526
	}

527 528
      if (work->data == container->focus_child)
	focus_row = length;
Elliot Lee's avatar
Elliot Lee committed
529
    }
530 531 532
  
  if (row < 0)
    row = length - 1;
Elliot Lee's avatar
Elliot Lee committed
533

534 535
  if (list->vtimer != 0)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
536

537 538 539 540 541
  if (!((y < 0 && focus_row == 0) ||
	(y > widget->allocation.height && focus_row >= length - 1)))
    list->vtimer = gtk_timeout_add (SCROLL_TIME,
				    (GtkFunction) gtk_list_vertical_timeout,
				    list);
Elliot Lee's avatar
Elliot Lee committed
542

543 544 545 546 547 548 549 550 551 552 553 554
  if (row != focus_row)
    gtk_widget_grab_focus (item);
	  
  switch (list->selection_mode)
    {
    case GTK_SELECTION_BROWSE:
      gtk_list_select_child (list, item);
      break;
      
    case GTK_SELECTION_EXTENDED:
      gtk_list_update_extended_selection (list, row);
      break;
Elliot Lee's avatar
Elliot Lee committed
555

556 557 558
    default:
      break;
    }
Elliot Lee's avatar
Elliot Lee committed
559

560
  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
561 562
}

563 564 565
static gint
gtk_list_button_press (GtkWidget      *widget,
		       GdkEventButton *event)
Elliot Lee's avatar
Elliot Lee committed
566
{
567 568
  GtkList *list;
  GtkWidget *item;
Elliot Lee's avatar
Elliot Lee committed
569

570 571 572
  g_return_val_if_fail (widget != NULL, FALSE);
  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
573

574 575
  if (event->button != 1)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
576

577 578
  list = GTK_LIST (widget);
  item = gtk_get_event_widget ((GdkEvent*) event);
Elliot Lee's avatar
Elliot Lee committed
579

580 581
  while (item && !GTK_IS_LIST_ITEM (item))
    item = item->parent;
Elliot Lee's avatar
Elliot Lee committed
582

583
  if (item && (item->parent == widget))
Elliot Lee's avatar
Elliot Lee committed
584
    {
585 586
      gint last_focus_row;
      gint focus_row;
Elliot Lee's avatar
Elliot Lee committed
587

588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
      if (event->type == GDK_BUTTON_PRESS)
	{
	  list->drag_selection = TRUE;
	  gdk_pointer_grab (widget->window, TRUE,
			    GDK_POINTER_MOTION_HINT_MASK |
			    GDK_BUTTON1_MOTION_MASK |
			    GDK_BUTTON_RELEASE_MASK,
			    NULL, NULL, event->time);
	  gtk_grab_add (widget);
	}
      else if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
	gtk_list_end_drag_selection (list);
	  
      if (!GTK_WIDGET_HAS_FOCUS(item))
	gtk_widget_grab_focus (item);
Elliot Lee's avatar
Elliot Lee committed
603

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
      if (list->add_mode)
	{
	  list->add_mode = FALSE;
	  gtk_widget_queue_draw (item);
	}
      
      switch (list->selection_mode)
	{
	case GTK_SELECTION_SINGLE:
	case GTK_SELECTION_MULTIPLE:
	  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
622

623 624
	case GTK_SELECTION_EXTENDED:
	  focus_row = g_list_index (list->children, item);
Elliot Lee's avatar
Elliot Lee committed
625

626 627 628 629 630 631 632 633
	  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;
	    }
634

635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
	  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;
656

657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
		      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;
	    }
673

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
	  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;
	}
692
    }
Elliot Lee's avatar
Elliot Lee committed
693

694 695
  return FALSE;
}
Elliot Lee's avatar
Elliot Lee committed
696

697 698 699
static gint
gtk_list_button_release (GtkWidget	*widget,
			 GdkEventButton *event)
Elliot Lee's avatar
Elliot Lee committed
700 701
{
  GtkList *list;
702
  GtkWidget *item;
Elliot Lee's avatar
Elliot Lee committed
703

704 705 706
  g_return_val_if_fail (widget != NULL, FALSE);
  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
707 708 709

  list = GTK_LIST (widget);

710 711 712
  /* we don't handle button 2 and 3 */
  if (event->button != 1)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
713

714
  if (list->drag_selection)
Elliot Lee's avatar
Elliot Lee committed
715
    {
716
      gtk_list_end_drag_selection (list);
Elliot Lee's avatar
Elliot Lee committed
717

718 719 720 721 722 723
      switch (list->selection_mode)
	{
	case GTK_SELECTION_EXTENDED:
 	  if (!(event->state & GDK_SHIFT_MASK))
	    gtk_list_end_selection (list);
	  break;
Elliot Lee's avatar
Elliot Lee committed
724

725 726
	case GTK_SELECTION_SINGLE:
	case GTK_SELECTION_MULTIPLE:
Elliot Lee's avatar
Elliot Lee committed
727

728 729 730 731 732 733 734 735 736 737 738 739
	  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
740

741 742 743 744 745 746
	default:
	  break;
	}
    }
  
  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
}

static void
gtk_list_draw (GtkWidget    *widget,
	       GdkRectangle *area)
{
  GtkList *list;
  GtkWidget *child;
  GdkRectangle child_area;
  GList *children;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_LIST (widget));
  g_return_if_fail (area != NULL);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      list = GTK_LIST (widget);

      children = list->children;
      while (children)
	{
	  child = children->data;
	  children = children->next;

	  if (gtk_widget_intersect (child, area, &child_area))
	    gtk_widget_draw (child, &child_area);
	}
    }
}

static gint
779
gtk_list_expose (GtkWidget	*widget,
Elliot Lee's avatar
Elliot Lee committed
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
		 GdkEventExpose *event)
{
  GtkList *list;
  GtkWidget *child;
  GdkEventExpose child_event;
  GList *children;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      list = GTK_LIST (widget);

      child_event = *event;

      children = list->children;
      while (children)
	{
	  child = children->data;
	  children = children->next;

	  if (GTK_WIDGET_NO_WINDOW (child) &&
	      gtk_widget_intersect (child, &event->area, &child_event.area))
	    gtk_widget_event (child, (GdkEvent*) &child_event);
	}
    }

  return FALSE;
}

Owen Taylor's avatar
Owen Taylor committed
812 813 814 815 816 817 818 819 820
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)]);
}
821 822 823 824 825 826 827 828 829

/* GtkContainer Methods :
 * gtk_list_add
 * gtk_list_remove
 * gtk_list_forall
 * gtk_list_child_type
 * gtk_list_set_focus_child
 * gtk_list_focus
 */
830
static void
831 832
gtk_list_add (GtkContainer *container,
	      GtkWidget	   *widget)
833
{
834
  GList *item_list;
835

836 837 838 839
  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_LIST (container));
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_LIST_ITEM (widget));
840

841 842 843 844
  item_list = g_list_alloc ();
  item_list->data = widget;
  
  gtk_list_append_items (GTK_LIST (container), item_list);
845 846
}

847 848 849
static void
gtk_list_remove (GtkContainer *container,
		 GtkWidget    *widget)
850
{
851 852 853 854 855 856 857 858 859 860 861 862 863 864
  GList *item_list;
  
  g_return_if_fail (container != NULL);
  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);
}
865

866 867 868 869 870
static void
gtk_list_forall (GtkContainer  *container,
		 gboolean       include_internals,
		 GtkCallback	callback,
		 gpointer	callback_data)
871
{
872 873 874
  GtkList *list;
  GtkWidget *child;
  GList *children;
875

876 877 878
  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_LIST (container));
  g_return_if_fail (callback != NULL);
879

880 881
  list = GTK_LIST (container);
  children = list->children;
882

883 884 885 886
  while (children)
    {
      child = children->data;
      children = children->next;
887

888 889 890
      (* callback) (child, callback_data);
    }
}
891

892 893 894 895
static GtkType
gtk_list_child_type (GtkContainer *container)
{
  return GTK_TYPE_LIST_ITEM;
896 897
}

898 899 900
static void
gtk_list_set_focus_child (GtkContainer *container,
			  GtkWidget    *child)
Elliot Lee's avatar
Elliot Lee committed
901
{
902
  GtkList *list;
903

904 905
  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_LIST (container));
906

907 908
  if (child)
    g_return_if_fail (GTK_IS_WIDGET (child));
909

910 911
  list = GTK_LIST (container);
  list->last_focus_child = container->focus_child;
912

913
  if (child != container->focus_child)
914
    {
915 916 917 918 919
      if (container->focus_child)
        gtk_widget_unref (container->focus_child);
      container->focus_child = child;
      if (container->focus_child)
        gtk_widget_ref (container->focus_child);
920 921
    }

922 923
  /* check for v adjustment */
  if (container->focus_child)
924
    {
925
      GtkAdjustment *adjustment;
926

927 928 929 930 931 932 933
      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));
934 935 936
    }

  switch (list->selection_mode)
937
    {
938
    case GTK_SELECTION_BROWSE:
939 940
      if (child)
	gtk_list_select_child (list, child);
941 942 943
      break;
    default:
      break;
944
    }
Elliot Lee's avatar
Elliot Lee committed
945 946 947
}

static gint
948 949
gtk_list_focus (GtkContainer     *container,
		GtkDirectionType  direction)
Elliot Lee's avatar
Elliot Lee committed
950
{
951
  gint return_val = FALSE;
Elliot Lee's avatar
Elliot Lee committed
952

953 954
  g_return_val_if_fail (container != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_LIST (container), FALSE);
Elliot Lee's avatar
Elliot Lee committed
955

956
  if (!GTK_WIDGET_IS_SENSITIVE (container))
957 958 959 960
    return_val = FALSE;
  else if (container->focus_child == NULL ||
      !GTK_WIDGET_HAS_FOCUS (container->focus_child))
    {
961
      if (GTK_CONTAINER_CLASS (parent_class)->focus)
962 963 964 965 966 967 968
	return_val = GTK_CONTAINER_CLASS (parent_class)->focus
	  (container, direction);
    }
  
  if (!return_val)
    {
      GtkList *list;
969

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

975 976
  return return_val;
}
Elliot Lee's avatar
Elliot Lee committed
977

978 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 1008 1009 1010 1011

/* 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 (list != NULL);
  g_return_if_fail (GTK_IS_LIST (list));

  if (!items)
    return;

  gtk_list_end_drag_selection (list);
  if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
    gtk_list_end_selection (list);

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

1016 1017 1018 1019 1020 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_widget_set_parent (widget, GTK_WIDGET (list));
      gtk_signal_connect (GTK_OBJECT (widget), "focus_out_event",
			  GTK_SIGNAL_FUNC (gtk_list_signal_focus_lost),
			  list);
      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);

      if (GTK_WIDGET_VISIBLE (widget->parent))
1061
	{
1062 1063 1064 1065 1066 1067 1068
	  if (GTK_WIDGET_REALIZED (widget->parent) &&
	      !GTK_WIDGET_REALIZED (widget))
	    gtk_widget_realize (widget);

	  if (GTK_WIDGET_MAPPED (widget->parent) &&
	      !GTK_WIDGET_MAPPED (widget))
	    gtk_widget_map (widget);
1069
	}
1070
    }
1071

1072 1073 1074 1075 1076 1077 1078
  nchildren = g_list_length (list->children);
  if ((position < 0) || (position > nchildren))
    position = nchildren;

  if (position == nchildren)
    {
      if (list->children)
1079
	{
1080 1081 1082
	  tmp_list = g_list_last (list->children);
	  tmp_list->next = items;
	  items->prev = tmp_list;
1083
	}
1084
      else
1085
	{
1086 1087 1088 1089 1090 1091 1092
	  list->children = items;
	}
    }
  else
    {
      tmp_list = g_list_nth (list->children, position);
      last = g_list_last (items);
1093

1094 1095 1096 1097 1098
      if (tmp_list->prev)
	tmp_list->prev->next = items;
      last->next = tmp_list;
      items->prev = tmp_list->prev;
      tmp_list->prev = last;
1099

1100 1101 1102
      if (tmp_list == list->children)