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

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

#define DEFAULT_DELTA 9

enum {
  COLLAPSE_TREE,
  EXPAND_TREE,
  LAST_SIGNAL
};

52 53 54 55 56 57 58 59 60 61 62 63 64 65
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
66 67
static void gtk_tree_item_class_init (GtkTreeItemClass *klass);
static void gtk_tree_item_init       (GtkTreeItem      *tree_item);
68 69 70 71 72
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);
73 74
static void gtk_tree_item_paint         (GtkWidget        *widget,
					 GdkRectangle     *area);
75 76 77 78
static gint gtk_tree_item_button_press  (GtkWidget        *widget,
					 GdkEventButton   *event);
static gint gtk_tree_item_expose        (GtkWidget        *widget,
					 GdkEventExpose   *event);
79 80 81 82 83
static void gtk_tree_item_forall        (GtkContainer    *container,
					 gboolean         include_internals,
					 GtkCallback      callback,
					 gpointer         callback_data);

84 85 86 87 88 89 90 91
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);
92
static gint gtk_tree_item_subtree_button_click (GtkWidget *widget);
93
static void gtk_tree_item_subtree_button_changed_state (GtkWidget *widget);
Elliot Lee's avatar
Elliot Lee committed
94

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

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

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

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

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

      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)
{
132 133
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
134
  GtkContainerClass *container_class;
135 136
  GtkItemClass *item_class;

137 138
  parent_class = gtk_type_class (GTK_TYPE_ITEM);
  
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
  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;

154 155
  container_class->forall = gtk_tree_item_forall;

156 157 158 159 160 161
  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;
162 163 164 165 166 167

  tree_item_signals[EXPAND_TREE] =
    gtk_signal_new ("expand",
		    GTK_RUN_FIRST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GtkTreeItemClass, expand),
168
		    _gtk_marshal_VOID__VOID,
169 170 171 172 173 174
		    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),
175
		    _gtk_marshal_VOID__VOID,
176
		    GTK_TYPE_NONE, 0);
177 178 179
}

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

  return TRUE;
197 198 199 200
}

/* callback for event box state changed */
static void
201
gtk_tree_item_subtree_button_changed_state (GtkWidget *widget)
202
{
203 204 205 206 207 208 209 210 211 212 213 214 215 216
  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
217 218 219 220 221
}

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

226 227 228 229
  tree_item->expanded = FALSE;
  tree_item->subtree = NULL;
  GTK_WIDGET_SET_FLAGS (tree_item, GTK_CAN_FOCUS);
  
Tim Janik's avatar
Tim Janik committed
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 263
  /* 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
264 265 266 267
}


GtkWidget*
268
gtk_tree_item_new (void)
Elliot Lee's avatar
Elliot Lee committed
269
{
270 271 272 273 274
  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
275 276 277
}

GtkWidget*
Owen Taylor's avatar
Owen Taylor committed
278
gtk_tree_item_new_with_label (const gchar *label)
Elliot Lee's avatar
Elliot Lee committed
279 280 281 282 283 284 285 286 287 288 289
{
  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);

290

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

361 362
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
  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
385
    {
386 387 388
      pixmap_node = g_new (GtkTreePixmaps, 1);

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

391 392
      pixmap_node->refcount = 1;

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

      tree_item->pixmaps = pixmaps = g_list_prepend (pixmaps, pixmap_node);
Tim Janik's avatar
Tim Janik committed
408
    }
409 410 411 412 413 414
  
  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
415

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

421 422 423 424 425 426 427 428
  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
429 430 431 432 433
	  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);
434 435 436 437 438 439 440 441
	  
	  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
442 443
}

444 445 446 447 448 449 450 451
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);
  
452 453
  gdk_window_set_background (widget->window, 
			     &widget->style->base[GTK_STATE_NORMAL]);
Tim Janik's avatar
Tim Janik committed
454

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

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

  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 +
473
			widget->style->xthickness) * 2;
474 475 476 477
  requisition->height = GTK_CONTAINER (widget)->border_width * 2;

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

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

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

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

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

  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 +
519
		      widget->style->xthickness);
520 521 522 523

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

524 525 526 527 528 529 530 531 532
      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;
533
      child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
534 535 536
      child_allocation.x += item->pixmaps_box->requisition.width+DEFAULT_DELTA;

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

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

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

551 552
  g_return_if_fail (GTK_IS_TREE_ITEM (widget));

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

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

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

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

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

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

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

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

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

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

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

594
  if (tree != tree->root_tree)
595
    {
596 597 598 599 600 601 602
      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)
603
	    gdk_draw_line (widget->window, gc, lx1, ly1, lx2, ly2);
604 605 606
	  item = GTK_TREE_ITEM (tree->tree_owner);
	  tree = GTK_TREE (GTK_WIDGET (tree)->parent);
	} 
607 608 609
    }
}

610
static void
611 612
gtk_tree_item_paint (GtkWidget    *widget,
		     GdkRectangle *area)
613 614 615 616 617 618 619 620
{
  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);

621 622 623 624 625 626
  /* 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).
   */
627 628 629 630 631
  if (GTK_WIDGET_DRAWABLE (widget))
    {
      bin = GTK_BIN (widget);
      tree_item = GTK_TREE_ITEM(widget);

632 633 634 635 636 637 638 639 640
      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,
641
			       widget->state, GTK_SHADOW_NONE,
642 643 644 645 646 647 648 649 650
			       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);
	}

651
      /* draw left size of tree item */
652 653 654 655
      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);
656 657
      item_area.height = widget->allocation.height;

658

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

664 665 666 667 668
	  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);
	}
669 670

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

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);

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

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

696 697 698 699 700 701 702 703 704 705 706 707 708
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))
    {
709 710 711
      GdkEvent *child_event = gdk_event_new (GDK_EXPOSE);
      child_event->expose = *data->event;
      g_object_ref (child_event->expose.window);
712

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

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

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

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

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

  return FALSE;
}

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

757 758
  g_return_if_fail (GTK_IS_TREE_ITEM (item));

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

  gtk_widget_set_state (GTK_WIDGET (item), GTK_STATE_SELECTED);

764 765
  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);
766 767 768 769 770
}

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

774 775
  g_return_if_fail (GTK_IS_TREE_ITEM (item));

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

779
  gtk_widget_set_state (widget, GTK_STATE_NORMAL);
780

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

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

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

793 794 795 796 797 798 799
  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
800
       * yes, always be on the safe side!
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
       * -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));
816 817
  
  if (tree_item->subtree && !tree_item->expanded)
818
    {
819 820
      tree = GTK_TREE (GTK_WIDGET (tree_item)->parent); 
      
821
      /* hide subtree widget */
822 823
      gtk_widget_show (tree_item->subtree);
      
824
      /* hide button '+' and show button '-' */
825 826 827 828 829 830 831 832 833
      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));
834 835 836 837 838 839 840 841
      tree_item->expanded = TRUE;
    }
}

static void
gtk_real_tree_item_collapse (GtkTreeItem *tree_item)
{
  GtkTree* tree;
842
  
843
  g_return_if_fail (GTK_IS_TREE_ITEM (tree_item));
844 845
  
  if (tree_item->subtree && tree_item->expanded) 
846
    {
847 848
      tree = GTK_TREE (GTK_WIDGET (tree_item)->parent);
      
849
      /* hide subtree widget */
850 851
      gtk_widget_hide (tree_item->subtree);
      
852
      /* hide button '-' and show button '+' */
853 854 855 856 857 858 859 860 861
      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));
862 863 864 865 866 867 868 869 870 871 872 873
      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));

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

878 879 880
  item = GTK_TREE_ITEM(object);

  /* free sub tree if it exist */
881 882
  child = item->subtree;
  if (child)
883
    {
884 885
      gtk_widget_ref (child);
      gtk_widget_unparent (child);
886
      gtk_widget_destroy (child);
887 888
      gtk_widget_unref (child);
      item->subtree = NULL;
889
    }
890
  
891
  /* free pixmaps box */
892 893
  child = item->pixmaps_box;
  if (child)
894
    {
895 896
      gtk_widget_ref (child);
      gtk_widget_unparent (child);
897
      gtk_widget_destroy (child);
898 899 900
      gtk_widget_unref (child);
      item->pixmaps_box = NULL;
    }
901 902
  
  
903 904 905 906 907 908
  /* 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;
909
    }
910
  
911 912 913 914 915 916 917
  /* 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;
    }
918
  
919 920 921 922
  /* 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);
923
  
924 925
  GTK_OBJECT_CLASS (parent_class)->destroy (object);
  
926
#ifdef TREE_DEBUG
927
  g_message("- gtk_tree_item_destroy\n");
928
#endif /* TREE_DEBUG */
929 930 931 932 933
}

void
gtk_tree_item_remove_subtree (GtkTreeItem* item) 
{
934 935 936 937
  g_return_if_fail (GTK_IS_TREE_ITEM(item));
  g_return_if_fail (item->subtree != NULL);
  
  if (GTK_TREE (item->subtree)->children)
938 939 940 941 942 943 944 945 946
    {
      /* 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;
    }

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

  if (item->expanded)
958
    {
959 960 961 962 963 964 965 966
      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);
	}
967
    }
968 969 970 971 972 973 974 975 976 977 978 979 980
}

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);

981 982
  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);

983 984 985 986 987 988 989 990 991
  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);
992 993

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

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);

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

  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
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);
1040 1041
  if (include_internals && tree_item->pixmaps_box)
    (* callback) (tree_item->pixmaps_box, callback_data);
1042
}