gtktreeitem.c 27.9 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 11
 * 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
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
#include <config.h>
Elliot Lee's avatar
Elliot Lee committed
30
#include "gtklabel.h"
31 32 33
#include "gtkeventbox.h"
#include "gtkpixmap.h"
#include "gtkmain.h"
34
#include "gtkmarshalers.h"
35
#include "gtksignal.h"
36 37 38
#define GTK_ENABLE_BROKEN
#include "gtktree.h"
#include "gtktreeitem.h"
Elliot Lee's avatar
Elliot Lee committed
39

40 41
#include "tree_plus.xpm"
#include "tree_minus.xpm"
42 43 44 45 46 47 48 49 50

#define DEFAULT_DELTA 9

enum {
  COLLAPSE_TREE,
  EXPAND_TREE,
  LAST_SIGNAL
};

51 52 53 54 55 56 57 58 59 60 61 62 63 64
typedef struct _GtkTreePixmaps GtkTreePixmaps;

struct _GtkTreePixmaps {
  gint refcount;
  GdkColormap *colormap;
  
  GdkPixmap *pixmap_plus;
  GdkPixmap *pixmap_minus;
  GdkBitmap *mask_plus;
  GdkBitmap *mask_minus;
};

static GList *pixmaps = NULL;

Elliot Lee's avatar
Elliot Lee committed
65 66
static void gtk_tree_item_class_init (GtkTreeItemClass *klass);
static void gtk_tree_item_init       (GtkTreeItem      *tree_item);
67 68 69 70 71
static void gtk_tree_item_realize       (GtkWidget        *widget);
static void gtk_tree_item_size_request  (GtkWidget        *widget,
					 GtkRequisition   *requisition);
static void gtk_tree_item_size_allocate (GtkWidget        *widget,
					 GtkAllocation    *allocation);
72 73
static void gtk_tree_item_paint         (GtkWidget        *widget,
					 GdkRectangle     *area);
74 75 76 77
static gint gtk_tree_item_button_press  (GtkWidget        *widget,
					 GdkEventButton   *event);
static gint gtk_tree_item_expose        (GtkWidget        *widget,
					 GdkEventExpose   *event);
78 79 80 81 82
static void gtk_tree_item_forall        (GtkContainer    *container,
					 gboolean         include_internals,
					 GtkCallback      callback,
					 gpointer         callback_data);

83 84 85 86 87 88 89 90
static void gtk_real_tree_item_select   (GtkItem          *item);
static void gtk_real_tree_item_deselect (GtkItem          *item);
static void gtk_real_tree_item_toggle   (GtkItem          *item);
static void gtk_real_tree_item_expand   (GtkTreeItem      *item);
static void gtk_real_tree_item_collapse (GtkTreeItem      *item);
static void gtk_real_tree_item_expand   (GtkTreeItem      *item);
static void gtk_real_tree_item_collapse (GtkTreeItem      *item);
static void gtk_tree_item_destroy        (GtkObject *object);
91
static gint gtk_tree_item_subtree_button_click (GtkWidget *widget);
92
static void gtk_tree_item_subtree_button_changed_state (GtkWidget *widget);
Elliot Lee's avatar
Elliot Lee committed
93

94 95 96
static void gtk_tree_item_map(GtkWidget*);
static void gtk_tree_item_unmap(GtkWidget*);

97 98 99
static void gtk_tree_item_add_pixmaps    (GtkTreeItem       *tree_item);
static void gtk_tree_item_remove_pixmaps (GtkTreeItem       *tree_item);

100
static GtkItemClass *parent_class = NULL;
101
static guint tree_item_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
102

103
GtkType
104
gtk_tree_item_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
105
{
106
  static GtkType tree_item_type = 0;
Elliot Lee's avatar
Elliot Lee committed
107 108 109

  if (!tree_item_type)
    {
110
      static const GtkTypeInfo tree_item_info =
Elliot Lee's avatar
Elliot Lee committed
111 112 113 114 115 116
      {
	"GtkTreeItem",
	sizeof (GtkTreeItem),
	sizeof (GtkTreeItemClass),
	(GtkClassInitFunc) gtk_tree_item_class_init,
	(GtkObjectInitFunc) gtk_tree_item_init,
117 118
	/* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
119
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
120 121 122 123 124 125 126 127 128 129 130
      };

      tree_item_type = gtk_type_unique (gtk_item_get_type (), &tree_item_info);
    }

  return tree_item_type;
}

static void
gtk_tree_item_class_init (GtkTreeItemClass *class)
{
131 132
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
133
  GtkContainerClass *container_class;
134 135
  GtkItemClass *item_class;

136 137
  parent_class = gtk_type_class (GTK_TYPE_ITEM);
  
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
  item_class = (GtkItemClass*) class;
  container_class = (GtkContainerClass*) class;

  object_class->destroy = gtk_tree_item_destroy;

  widget_class->realize = gtk_tree_item_realize;
  widget_class->size_request = gtk_tree_item_size_request;
  widget_class->size_allocate = gtk_tree_item_size_allocate;
  widget_class->button_press_event = gtk_tree_item_button_press;
  widget_class->expose_event = gtk_tree_item_expose;
  widget_class->map = gtk_tree_item_map;
  widget_class->unmap = gtk_tree_item_unmap;

153 154
  container_class->forall = gtk_tree_item_forall;

155 156 157 158 159 160
  item_class->select = gtk_real_tree_item_select;
  item_class->deselect = gtk_real_tree_item_deselect;
  item_class->toggle = gtk_real_tree_item_toggle;

  class->expand = gtk_real_tree_item_expand;
  class->collapse = gtk_real_tree_item_collapse;
161 162 163 164 165 166

  tree_item_signals[EXPAND_TREE] =
    gtk_signal_new ("expand",
		    GTK_RUN_FIRST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GtkTreeItemClass, expand),
167
		    _gtk_marshal_VOID__VOID,
168 169 170 171 172 173
		    GTK_TYPE_NONE, 0);
  tree_item_signals[COLLAPSE_TREE] =
    gtk_signal_new ("collapse",
		    GTK_RUN_FIRST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GtkTreeItemClass, collapse),
174
		    _gtk_marshal_VOID__VOID,
175
		    GTK_TYPE_NONE, 0);
176 177 178
}

/* callback for event box mouse event */
179
static gint
180 181 182
gtk_tree_item_subtree_button_click (GtkWidget *widget)
{
  GtkTreeItem* item;
183
  
184
  g_return_val_if_fail (GTK_IS_EVENT_BOX (widget), FALSE);
185 186 187
  
  item = (GtkTreeItem*) gtk_object_get_user_data (GTK_OBJECT (widget));
  if (!GTK_WIDGET_IS_SENSITIVE (item))
188
    return FALSE;
189 190 191
  
  if (item->expanded)
    gtk_tree_item_collapse (item);
192
  else
193
    gtk_tree_item_expand (item);
194 195

  return TRUE;
196 197 198 199
}

/* callback for event box state changed */
static void
200
gtk_tree_item_subtree_button_changed_state (GtkWidget *widget)
201
{
202 203 204 205 206 207 208 209 210 211 212 213 214 215
  g_return_if_fail (GTK_IS_EVENT_BOX (widget));
  
  if (GTK_WIDGET_VISIBLE (widget))
    {
      
      if (widget->state == GTK_STATE_NORMAL)
	gdk_window_set_background (widget->window, &widget->style->base[widget->state]);
      else
	gdk_window_set_background (widget->window, &widget->style->bg[widget->state]);
      
      if (GTK_WIDGET_DRAWABLE (widget))
	gdk_window_clear_area (widget->window, 0, 0, 
			       widget->allocation.width, widget->allocation.height);
    }
Elliot Lee's avatar
Elliot Lee committed
216 217 218 219 220
}

static void
gtk_tree_item_init (GtkTreeItem *tree_item)
{
221
  GtkWidget *eventbox, *pixmapwid;
Tim Janik's avatar
Tim Janik committed
222
  
223 224
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));

225 226 227 228
  tree_item->expanded = FALSE;
  tree_item->subtree = NULL;
  GTK_WIDGET_SET_FLAGS (tree_item, GTK_CAN_FOCUS);
  
Tim Janik's avatar
Tim Janik committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
  /* create an event box containing one pixmaps */
  eventbox = gtk_event_box_new();
  gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK);
  gtk_signal_connect(GTK_OBJECT(eventbox), "state_changed",
		     (GtkSignalFunc)gtk_tree_item_subtree_button_changed_state, 
		     (gpointer)NULL);
  gtk_signal_connect(GTK_OBJECT(eventbox), "realize",
		     (GtkSignalFunc)gtk_tree_item_subtree_button_changed_state, 
		     (gpointer)NULL);
  gtk_signal_connect(GTK_OBJECT(eventbox), "button_press_event",
		     (GtkSignalFunc)gtk_tree_item_subtree_button_click,
		     (gpointer)NULL);
  gtk_object_set_user_data(GTK_OBJECT(eventbox), tree_item);
  tree_item->pixmaps_box = eventbox;

  /* create pixmap for button '+' */
  pixmapwid = gtk_type_new (gtk_pixmap_get_type ());
  if (!tree_item->expanded) 
    gtk_container_add (GTK_CONTAINER (eventbox), pixmapwid);
  gtk_widget_show (pixmapwid);
  tree_item->plus_pix_widget = pixmapwid;
  gtk_widget_ref (tree_item->plus_pix_widget);
  gtk_object_sink (GTK_OBJECT (tree_item->plus_pix_widget));
  
  /* create pixmap for button '-' */
  pixmapwid = gtk_type_new (gtk_pixmap_get_type ());
  if (tree_item->expanded) 
    gtk_container_add (GTK_CONTAINER (eventbox), pixmapwid);
  gtk_widget_show (pixmapwid);
  tree_item->minus_pix_widget = pixmapwid;
  gtk_widget_ref (tree_item->minus_pix_widget);
  gtk_object_sink (GTK_OBJECT (tree_item->minus_pix_widget));
  
  gtk_widget_set_parent (eventbox, GTK_WIDGET (tree_item));
Elliot Lee's avatar
Elliot Lee committed
263 264 265 266
}


GtkWidget*
267
gtk_tree_item_new (void)
Elliot Lee's avatar
Elliot Lee committed
268
{
269 270 271 272 273
  GtkWidget *tree_item;

  tree_item = GTK_WIDGET (gtk_type_new (gtk_tree_item_get_type ()));

  return tree_item;
Elliot Lee's avatar
Elliot Lee committed
274 275 276
}

GtkWidget*
Owen Taylor's avatar
Owen Taylor committed
277
gtk_tree_item_new_with_label (const gchar *label)
Elliot Lee's avatar
Elliot Lee committed
278 279 280 281 282 283 284 285 286 287 288
{
  GtkWidget *tree_item;
  GtkWidget *label_widget;

  tree_item = gtk_tree_item_new ();
  label_widget = gtk_label_new (label);
  gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);

  gtk_container_add (GTK_CONTAINER (tree_item), label_widget);
  gtk_widget_show (label_widget);

289

Elliot Lee's avatar
Elliot Lee committed
290 291 292 293 294 295 296 297
  return tree_item;
}

void
gtk_tree_item_set_subtree (GtkTreeItem *tree_item,
			   GtkWidget   *subtree)
{
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));
298
  g_return_if_fail (GTK_IS_TREE (subtree));
299

300 301 302 303 304
  if (tree_item->subtree)
    {
      g_warning("there is already a subtree for this tree item\n");
      return;
    }
305 306

  tree_item->subtree = subtree; 
307
  GTK_TREE (subtree)->tree_owner = GTK_WIDGET (tree_item);
308 309

  /* show subtree button */
Tim Janik's avatar
Tim Janik committed
310
  if (tree_item->pixmaps_box)
311
    gtk_widget_show (tree_item->pixmaps_box);
312

313 314 315 316
  if (tree_item->expanded)
    gtk_widget_show (subtree);
  else
    gtk_widget_hide (subtree);
317

318
  gtk_widget_set_parent (subtree, GTK_WIDGET (tree_item)->parent);
Elliot Lee's avatar
Elliot Lee committed
319 320 321 322 323
}

void
gtk_tree_item_select (GtkTreeItem *tree_item)
{
324
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));
325 326

  gtk_item_select (GTK_ITEM (tree_item));
Elliot Lee's avatar
Elliot Lee committed
327 328 329 330 331
}

void
gtk_tree_item_deselect (GtkTreeItem *tree_item)
{
332
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));
333 334

  gtk_item_deselect (GTK_ITEM (tree_item));
Elliot Lee's avatar
Elliot Lee committed
335 336 337 338 339
}

void
gtk_tree_item_expand (GtkTreeItem *tree_item)
{
340
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));
341 342

  gtk_signal_emit (GTK_OBJECT (tree_item), tree_item_signals[EXPAND_TREE], NULL);
Elliot Lee's avatar
Elliot Lee committed
343 344 345 346 347
}

void
gtk_tree_item_collapse (GtkTreeItem *tree_item)
{
348
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));
349 350 351 352

  gtk_signal_emit (GTK_OBJECT (tree_item), tree_item_signals[COLLAPSE_TREE], NULL);
}

353 354
static void
gtk_tree_item_add_pixmaps (GtkTreeItem *tree_item)
Tim Janik's avatar
Tim Janik committed
355
{
356 357 358 359
  GList *tmp_list;
  GdkColormap *colormap;
  GtkTreePixmaps *pixmap_node = NULL;

360 361
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
  if (tree_item->pixmaps)
    return;

  colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_item));

  tmp_list = pixmaps;
  while (tmp_list)
    {
      pixmap_node = (GtkTreePixmaps *)tmp_list->data;

      if (pixmap_node->colormap == colormap)
	break;
      
      tmp_list = tmp_list->next;
    }

  if (tmp_list)
    {
      pixmap_node->refcount++;
      tree_item->pixmaps = tmp_list;
    }
  else
Tim Janik's avatar
Tim Janik committed
384
    {
385 386 387
      pixmap_node = g_new (GtkTreePixmaps, 1);

      pixmap_node->colormap = colormap;
Manish Singh's avatar
Manish Singh committed
388
      g_object_ref (colormap);
389

390 391
      pixmap_node->refcount = 1;

Tim Janik's avatar
Tim Janik committed
392
      /* create pixmaps for plus icon */
393 394 395 396
      pixmap_node->pixmap_plus = 
	gdk_pixmap_create_from_xpm_d (GTK_WIDGET (tree_item)->window,
				      &pixmap_node->mask_plus,
				      NULL,
397
				      (gchar **)tree_plus);
Tim Janik's avatar
Tim Janik committed
398 399
      
      /* create pixmaps for minus icon */
400 401 402 403
      pixmap_node->pixmap_minus = 
	gdk_pixmap_create_from_xpm_d (GTK_WIDGET (tree_item)->window,
				      &pixmap_node->mask_minus,
				      NULL,
404
				      (gchar **)tree_minus);
405 406

      tree_item->pixmaps = pixmaps = g_list_prepend (pixmaps, pixmap_node);
Tim Janik's avatar
Tim Janik committed
407
    }
408 409 410 411 412 413
  
  gtk_pixmap_set (GTK_PIXMAP (tree_item->plus_pix_widget), 
		  pixmap_node->pixmap_plus, pixmap_node->mask_plus);
  gtk_pixmap_set (GTK_PIXMAP (tree_item->minus_pix_widget), 
		  pixmap_node->pixmap_minus, pixmap_node->mask_minus);
}
Tim Janik's avatar
Tim Janik committed
414

415 416 417
static void
gtk_tree_item_remove_pixmaps (GtkTreeItem *tree_item)
{
418 419
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));

420 421 422 423 424 425 426 427
  if (tree_item->pixmaps)
    {
      GtkTreePixmaps *pixmap_node = (GtkTreePixmaps *)tree_item->pixmaps->data;
      
      g_assert (pixmap_node->refcount > 0);
      
      if (--pixmap_node->refcount == 0)
	{
Manish Singh's avatar
Manish Singh committed
428 429 430 431 432
	  g_object_unref (pixmap_node->colormap);
	  g_object_unref (pixmap_node->pixmap_plus);
	  g_object_unref (pixmap_node->mask_plus);
	  g_object_unref (pixmap_node->pixmap_minus);
	  g_object_unref (pixmap_node->mask_minus);
433 434 435 436 437 438 439 440
	  
	  pixmaps = g_list_remove_link (pixmaps, tree_item->pixmaps);
	  g_list_free_1 (tree_item->pixmaps);
	  g_free (pixmap_node);
	}

      tree_item->pixmaps = NULL;
    }
Tim Janik's avatar
Tim Janik committed
441 442
}

443 444 445 446 447 448 449 450
static void
gtk_tree_item_realize (GtkWidget *widget)
{    
  g_return_if_fail (GTK_IS_TREE_ITEM (widget));

  if (GTK_WIDGET_CLASS (parent_class)->realize)
    (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
  
451 452
  gdk_window_set_background (widget->window, 
			     &widget->style->base[GTK_STATE_NORMAL]);
Tim Janik's avatar
Tim Janik committed
453

454
  gtk_tree_item_add_pixmaps (GTK_TREE_ITEM (widget));
455 456 457 458 459 460 461 462
}

static void
gtk_tree_item_size_request (GtkWidget      *widget,
			    GtkRequisition *requisition)
{
  GtkBin *bin;
  GtkTreeItem* item;
463
  GtkRequisition child_requisition;
464 465 466 467 468 469 470 471

  g_return_if_fail (GTK_IS_TREE_ITEM (widget));
  g_return_if_fail (requisition != NULL);

  bin = GTK_BIN (widget);
  item = GTK_TREE_ITEM(widget);

  requisition->width = (GTK_CONTAINER (widget)->border_width +
472
			widget->style->xthickness) * 2;
473 474 475 476
  requisition->height = GTK_CONTAINER (widget)->border_width * 2;

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
477 478
      GtkRequisition pix_requisition;
      
479
      gtk_widget_size_request (bin->child, &child_requisition);
480

481
      requisition->width += child_requisition.width;
482 483

      gtk_widget_size_request (item->pixmaps_box, 
484 485 486
			       &pix_requisition);
      requisition->width += pix_requisition.width + DEFAULT_DELTA + 
	GTK_TREE (widget->parent)->current_indent;
487

488
      requisition->height += MAX (child_requisition.height,
489
				  pix_requisition.height);
490 491 492 493 494 495 496 497 498 499
    }
}

static void
gtk_tree_item_size_allocate (GtkWidget     *widget,
			     GtkAllocation *allocation)
{
  GtkBin *bin;
  GtkTreeItem* item;
  GtkAllocation child_allocation;
500
  gint border_width;
501
  int temp;
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

  g_return_if_fail (GTK_IS_TREE_ITEM (widget));
  g_return_if_fail (allocation != NULL);

  widget->allocation = *allocation;
  if (GTK_WIDGET_REALIZED (widget))
    gdk_window_move_resize (widget->window,
			    allocation->x, allocation->y,
			    allocation->width, allocation->height);

  bin = GTK_BIN (widget);
  item = GTK_TREE_ITEM(widget);

  if (bin->child)
    {
      border_width = (GTK_CONTAINER (widget)->border_width +
518
		      widget->style->xthickness);
519 520 521 522

      child_allocation.x = border_width + GTK_TREE(widget->parent)->current_indent;
      child_allocation.y = GTK_CONTAINER (widget)->border_width;

523 524 525 526 527 528 529 530 531
      child_allocation.width = item->pixmaps_box->requisition.width;
      child_allocation.height = item->pixmaps_box->requisition.height;
      
      temp = allocation->height - child_allocation.height;
      child_allocation.y += ( temp / 2 ) + ( temp % 2 );

      gtk_widget_size_allocate (item->pixmaps_box, &child_allocation);

      child_allocation.y = GTK_CONTAINER (widget)->border_width;
532
      child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
533 534 535
      child_allocation.x += item->pixmaps_box->requisition.width+DEFAULT_DELTA;

      child_allocation.width = 
536
	MAX (1, (gint)allocation->width - ((gint)child_allocation.x + border_width));
537 538 539 540 541

      gtk_widget_size_allocate (bin->child, &child_allocation);
    }
}

542
static void 
543
gtk_tree_item_draw_lines (GtkWidget *widget) 
544 545 546 547
{
  GtkTreeItem* item;
  GtkTree* tree;
  guint lx1, ly1, lx2, ly2;
548
  GdkGC* gc;
549

550 551
  g_return_if_fail (GTK_IS_TREE_ITEM (widget));

552 553 554
  item = GTK_TREE_ITEM(widget);
  tree = GTK_TREE(widget->parent);

555 556 557
  if (!tree->view_line)
    return;

558 559
  gc = widget->style->text_gc[GTK_STATE_NORMAL];

560 561
  /* draw vertical line */
  lx1 = item->pixmaps_box->allocation.width;
562 563
  lx1 = lx2 = ((lx1 / 2) + (lx1 % 2) + 
	       GTK_CONTAINER (widget)->border_width + 1 + tree->current_indent);
564 565 566
  ly1 = 0;
  ly2 = widget->allocation.height;

567
  if (g_list_last (tree->children)->data == widget)
568 569
    ly2 = (ly2 / 2) + (ly2 % 2);

570
  if (tree != tree->root_tree)
571
    gdk_draw_line (widget->window, gc, lx1, ly1, lx2, ly2);
572 573 574 575 576 577 578

  /* draw vertical line for subtree connecting */
  if(g_list_last(tree->children)->data != (gpointer)widget)
    ly2 = (ly2 / 2) + (ly2 % 2);
  
  lx2 += DEFAULT_DELTA;

579
  if (item->subtree && item->expanded)
580
    gdk_draw_line (widget->window, gc,
581
		   lx2, ly2, lx2, widget->allocation.height);
582 583 584 585 586

  /* draw horizontal line */
  ly1 = ly2;
  lx2 += 2;

587
  gdk_draw_line (widget->window, gc, lx1, ly1, lx2, ly2);
588 589 590 591 592

  lx2 -= DEFAULT_DELTA+2;
  ly1 = 0;
  ly2 = widget->allocation.height;

593
  if (tree != tree->root_tree)
594
    {
595 596 597 598 599 600 601
      item = GTK_TREE_ITEM (tree->tree_owner);
      tree = GTK_TREE (GTK_WIDGET (tree)->parent);
      while (tree != tree->root_tree)
	{
	  lx1 = lx2 -= tree->indent_value;
	  
	  if (g_list_last (tree->children)->data != item)
602
	    gdk_draw_line (widget->window, gc, lx1, ly1, lx2, ly2);
603 604 605
	  item = GTK_TREE_ITEM (tree->tree_owner);
	  tree = GTK_TREE (GTK_WIDGET (tree)->parent);
	} 
606 607 608
    }
}

609
static void
610 611
gtk_tree_item_paint (GtkWidget    *widget,
		     GdkRectangle *area)
612 613 614 615 616 617 618 619
{
  GtkBin *bin;
  GdkRectangle child_area, item_area;
  GtkTreeItem* tree_item;

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

620 621 622 623 624 625
  /* FIXME: We should honor tree->view_mode, here - I think
   * the desired effect is that when the mode is VIEW_ITEM,
   * only the subitem is drawn as selected, not the entire
   * line. (Like the way that the tree in Windows Explorer
   * works).
   */
626 627 628 629 630
  if (GTK_WIDGET_DRAWABLE (widget))
    {
      bin = GTK_BIN (widget);
      tree_item = GTK_TREE_ITEM(widget);

631 632 633 634 635 636 637 638 639
      if (widget->state == GTK_STATE_NORMAL)
	{
	  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
	  gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
	}
      else 
	{
	  if (!GTK_WIDGET_IS_SENSITIVE (widget)) 
	    gtk_paint_flat_box(widget->style, widget->window,
640
			       widget->state, GTK_SHADOW_NONE,
641 642 643 644 645 646 647 648 649
			       area, widget, "treeitem",
			       0, 0, -1, -1);
	  else
	    gtk_paint_flat_box(widget->style, widget->window,
			       widget->state, GTK_SHADOW_ETCHED_OUT,
			       area, widget, "treeitem",
			       0, 0, -1, -1);
	}

650
      /* draw left size of tree item */
651 652 653 654
      item_area.x = 0;
      item_area.y = 0;
      item_area.width = (tree_item->pixmaps_box->allocation.width + DEFAULT_DELTA +
			 GTK_TREE (widget->parent)->current_indent + 2);
655 656
      item_area.height = widget->allocation.height;

657

658
      if (gdk_rectangle_intersect(&item_area, area, &child_area)) 
659 660
	{
	  
661 662
	  gtk_tree_item_draw_lines(widget);

663 664 665 666 667
	  if (tree_item->pixmaps_box && 
	      GTK_WIDGET_VISIBLE(tree_item->pixmaps_box) &&
	      gtk_widget_intersect (tree_item->pixmaps_box, area, &child_area))
	    gtk_widget_draw (tree_item->pixmaps_box, &child_area);
	}
668 669

      if (GTK_WIDGET_HAS_FOCUS (widget))
670
	gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
671 672
			 NULL, widget, "treeitem",
			 0, 0,
673 674
			 widget->allocation.width,
			 widget->allocation.height);
675
      
676 677 678 679 680 681 682 683 684 685 686
    }
}

static gint
gtk_tree_item_button_press (GtkWidget      *widget,
			    GdkEventButton *event)
{

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

687 688 689
  if (event->type == GDK_BUTTON_PRESS
	&& GTK_WIDGET_IS_SENSITIVE(widget)
     	&& !GTK_WIDGET_HAS_FOCUS (widget))
690 691
      gtk_widget_grab_focus (widget);

692
  return (event->type == GDK_BUTTON_PRESS && GTK_WIDGET_IS_SENSITIVE(widget));
693 694
}

695 696 697 698 699 700 701 702 703 704 705 706 707
static void
gtk_tree_item_expose_child (GtkWidget *child,
                            gpointer   client_data)
{
  struct {
    GtkWidget *container;
    GdkEventExpose *event;
  } *data = client_data;

  if (GTK_WIDGET_DRAWABLE (child) &&
      GTK_WIDGET_NO_WINDOW (child) &&
      (child->window == data->event->window))
    {
708 709 710
      GdkEvent *child_event = gdk_event_new (GDK_EXPOSE);
      child_event->expose = *data->event;
      g_object_ref (child_event->expose.window);
711

712 713 714
      child_event->expose.region = gtk_widget_region_intersect (child,
								data->event->region);
      if (!gdk_region_empty (child_event->expose.region))
715
        {
716 717
          gdk_region_get_clipbox (child_event->expose.region, &child_event->expose.area);
          gtk_widget_send_expose (child, child_event);
718
	}
719
      gdk_event_free (child_event);
720 721 722
    }
}

723 724 725 726
static gint
gtk_tree_item_expose (GtkWidget      *widget,
		      GdkEventExpose *event)
{
727 728 729 730 731
  struct {
    GtkWidget *container;
    GdkEventExpose *event;
  } data;
  
732 733 734 735
  g_return_val_if_fail (GTK_IS_TREE_ITEM (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (GTK_WIDGET_DRAWABLE (widget))
736 737 738
    {
      gtk_tree_item_paint (widget, &event->area);

739 740 741 742 743 744
      data.container = widget;
      data.event = event;

      gtk_container_forall (GTK_CONTAINER (widget),
                            gtk_tree_item_expose_child,
                            &data);
745
   }
746 747 748 749 750 751

  return FALSE;
}

static void
gtk_real_tree_item_select (GtkItem *item)
752
{    
753 754 755
  GtkTreeItem *tree_item;
  GtkWidget *widget;

756 757
  g_return_if_fail (GTK_IS_TREE_ITEM (item));

758 759
  tree_item = GTK_TREE_ITEM (item);
  widget = GTK_WIDGET (item);
760 761 762

  gtk_widget_set_state (GTK_WIDGET (item), GTK_STATE_SELECTED);

763 764
  if (!widget->parent || GTK_TREE (widget->parent)->view_mode == GTK_TREE_VIEW_LINE)
    gtk_widget_set_state (GTK_TREE_ITEM (item)->pixmaps_box, GTK_STATE_SELECTED);
765 766 767 768 769
}

static void
gtk_real_tree_item_deselect (GtkItem *item)
{
770 771 772
  GtkTreeItem *tree_item;
  GtkWidget *widget;

773 774
  g_return_if_fail (GTK_IS_TREE_ITEM (item));

775 776
  tree_item = GTK_TREE_ITEM (item);
  widget = GTK_WIDGET (item);
777

778
  gtk_widget_set_state (widget, GTK_STATE_NORMAL);
779

780 781
  if (!widget->parent || GTK_TREE (widget->parent)->view_mode == GTK_TREE_VIEW_LINE)
    gtk_widget_set_state (tree_item->pixmaps_box, GTK_STATE_NORMAL);
782 783 784 785 786 787 788
}

static void
gtk_real_tree_item_toggle (GtkItem *item)
{
  g_return_if_fail (GTK_IS_TREE_ITEM (item));

789 790 791
  if(!GTK_WIDGET_IS_SENSITIVE(item))
    return;

792 793 794 795 796 797 798
  if (GTK_WIDGET (item)->parent && GTK_IS_TREE (GTK_WIDGET (item)->parent))
    gtk_tree_select_child (GTK_TREE (GTK_WIDGET (item)->parent),
			   GTK_WIDGET (item));
  else
    {
      /* Should we really bother with this bit? A listitem not in a list?
       * -Johannes Keukelaar
799
       * yes, always be on the safe side!
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
       * -timj
       */
      if (GTK_WIDGET (item)->state == GTK_STATE_SELECTED)
	gtk_widget_set_state (GTK_WIDGET (item), GTK_STATE_NORMAL);
      else
	gtk_widget_set_state (GTK_WIDGET (item), GTK_STATE_SELECTED);
    }
}

static void
gtk_real_tree_item_expand (GtkTreeItem *tree_item)
{
  GtkTree* tree;
  
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));
815 816
  
  if (tree_item->subtree && !tree_item->expanded)
817
    {
818 819
      tree = GTK_TREE (GTK_WIDGET (tree_item)->parent); 
      
820
      /* hide subtree widget */
821 822
      gtk_widget_show (tree_item->subtree);
      
823
      /* hide button '+' and show button '-' */
824 825 826 827 828 829 830 831 832
      if (tree_item->pixmaps_box)
	{
	  gtk_container_remove (GTK_CONTAINER (tree_item->pixmaps_box), 
				tree_item->plus_pix_widget);
	  gtk_container_add (GTK_CONTAINER (tree_item->pixmaps_box), 
			     tree_item->minus_pix_widget);
	}
      if (tree->root_tree)
	gtk_widget_queue_resize (GTK_WIDGET (tree->root_tree));
833 834 835 836 837 838 839 840
      tree_item->expanded = TRUE;
    }
}

static void
gtk_real_tree_item_collapse (GtkTreeItem *tree_item)
{
  GtkTree* tree;
841
  
842
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));
843 844
  
  if (tree_item->subtree && tree_item->expanded) 
845
    {
846 847
      tree = GTK_TREE (GTK_WIDGET (tree_item)->parent);
      
848
      /* hide subtree widget */
849 850
      gtk_widget_hide (tree_item->subtree);
      
851
      /* hide button '-' and show button '+' */
852 853 854 855 856 857 858 859 860
      if (tree_item->pixmaps_box)
	{
	  gtk_container_remove (GTK_CONTAINER (tree_item->pixmaps_box), 
				tree_item->minus_pix_widget);
	  gtk_container_add (GTK_CONTAINER (tree_item->pixmaps_box), 
			     tree_item->plus_pix_widget);
	}
      if (tree->root_tree)
	gtk_widget_queue_resize (GTK_WIDGET (tree->root_tree));
861 862 863 864 865 866 867 868 869 870 871 872
      tree_item->expanded = FALSE;
    }
}

static void
gtk_tree_item_destroy (GtkObject *object)
{
  GtkTreeItem* item;
  GtkWidget* child;

  g_return_if_fail (GTK_IS_TREE_ITEM (object));

873
#ifdef TREE_DEBUG
874
  g_message("+ gtk_tree_item_destroy [object %#x]\n", (int)object);
875 876
#endif /* TREE_DEBUG */

877 878 879
  item = GTK_TREE_ITEM(object);

  /* free sub tree if it exist */
880 881
  child = item->subtree;
  if (child)
882
    {
883 884
      gtk_widget_ref (child);
      gtk_widget_unparent (child);
885
      gtk_widget_destroy (child);
886 887
      gtk_widget_unref (child);
      item->subtree = NULL;
888
    }
889
  
890
  /* free pixmaps box */
891 892
  child = item->pixmaps_box;
  if (child)
893
    {
894 895
      gtk_widget_ref (child);
      gtk_widget_unparent (child);
896
      gtk_widget_destroy (child);
897 898 899
      gtk_widget_unref (child);
      item->pixmaps_box = NULL;
    }
900 901
  
  
902 903 904 905 906 907
  /* destroy plus pixmap */
  if (item->plus_pix_widget)
    {
      gtk_widget_destroy (item->plus_pix_widget);
      gtk_widget_unref (item->plus_pix_widget);
      item->plus_pix_widget = NULL;
908
    }
909
  
910 911 912 913 914 915 916
  /* destroy minus pixmap */
  if (item->minus_pix_widget)
    {
      gtk_widget_destroy (item->minus_pix_widget);
      gtk_widget_unref (item->minus_pix_widget);
      item->minus_pix_widget = NULL;
    }
917
  
918 919 920 921
  /* By removing the pixmaps here, and not in unrealize, we depend on
   * the fact that a widget can never change colormap or visual.
   */
  gtk_tree_item_remove_pixmaps (item);
922
  
923 924
  GTK_OBJECT_CLASS (parent_class)->destroy (object);
  
925
#ifdef TREE_DEBUG
926
  g_message("- gtk_tree_item_destroy\n");
927
#endif /* TREE_DEBUG */
928 929 930 931 932
}

void
gtk_tree_item_remove_subtree (GtkTreeItem* item) 
{
933 934 935 936
  g_return_if_fail (GTK_IS_TREE_ITEM(item));
  g_return_if_fail (item->subtree != NULL);
  
  if (GTK_TREE (item->subtree)->children)
937 938 939 940 941 942 943 944 945
    {
      /* The following call will remove the children and call
       * gtk_tree_item_remove_subtree() again. So we are done.
       */
      gtk_tree_remove_items (GTK_TREE (item->subtree), 
			     GTK_TREE (item->subtree)->children);
      return;
    }

946 947
  if (GTK_WIDGET_MAPPED (item->subtree))
    gtk_widget_unmap (item->subtree);
948
      
949
  gtk_widget_unparent (item->subtree);
950 951 952 953
  
  if (item->pixmaps_box)
    gtk_widget_hide (item->pixmaps_box);
  
954
  item->subtree = NULL;
955 956

  if (item->expanded)
957
    {
958 959 960 961 962 963 964 965
      item->expanded = FALSE;
      if (item->pixmaps_box)
	{
	  gtk_container_remove (GTK_CONTAINER (item->pixmaps_box), 
				item->minus_pix_widget);
	  gtk_container_add (GTK_CONTAINER (item->pixmaps_box), 
			     item->plus_pix_widget);
	}
966
    }
967 968 969 970 971 972 973 974 975 976 977 978 979
}

static void
gtk_tree_item_map (GtkWidget *widget)
{
  GtkBin *bin;
  GtkTreeItem* item;

  g_return_if_fail (GTK_IS_TREE_ITEM (widget));

  bin = GTK_BIN (widget);
  item = GTK_TREE_ITEM(widget);

980 981
  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);

982 983 984 985 986 987 988 989 990
  if(item->pixmaps_box &&
     GTK_WIDGET_VISIBLE (item->pixmaps_box) &&
     !GTK_WIDGET_MAPPED (item->pixmaps_box))
    gtk_widget_map (item->pixmaps_box);

  if (bin->child &&
      GTK_WIDGET_VISIBLE (bin->child) &&
      !GTK_WIDGET_MAPPED (bin->child))
    gtk_widget_map (bin->child);
991 992

  gdk_window_show (widget->window);
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
}

static void
gtk_tree_item_unmap (GtkWidget *widget)
{
  GtkBin *bin;
  GtkTreeItem* item;

  g_return_if_fail (GTK_IS_TREE_ITEM (widget));

  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
  bin = GTK_BIN (widget);
  item = GTK_TREE_ITEM(widget);

1007
  gdk_window_hide (widget->window);
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017

  if(item->pixmaps_box &&
     GTK_WIDGET_VISIBLE (item->pixmaps_box) &&
     GTK_WIDGET_MAPPED (item->pixmaps_box))
    gtk_widget_unmap (bin->child);

  if (bin->child &&
      GTK_WIDGET_VISIBLE (bin->child) &&
      GTK_WIDGET_MAPPED (bin->child))
    gtk_widget_unmap (bin->child);
Elliot Lee's avatar
Elliot Lee committed
1018
}
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039

static void
gtk_tree_item_forall (GtkContainer *container,
		      gboolean      include_internals,
		      GtkCallback   callback,
		      gpointer      callback_data)
{
  GtkBin *bin;
  GtkTreeItem *tree_item;

  g_return_if_fail (GTK_IS_TREE_ITEM (container));
  g_return_if_fail (callback != NULL);

  bin = GTK_BIN (container);
  tree_item = GTK_TREE_ITEM (container);

  if (bin->child)
    (* callback) (bin->child, callback_data);
  if (include_internals && tree_item->subtree)
    (* callback) (tree_item->subtree, callback_data);
}