evolution-3.6.4/widgets/text/e-reflow.c

Location Tool Test ID Function Issue
e-reflow.c:378:35 clang-analyzer Access to field 'data' results in a dereference of a null pointer (loaded from variable 'list')
e-reflow.c:378:35 clang-analyzer Access to field 'data' results in a dereference of a null pointer (loaded from variable 'list')
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
   2 /*
   3  * This program is free software; you can redistribute it and/or
   4  * modify it under the terms of the GNU Lesser General Public
   5  * License as published by the Free Software Foundation; either
   6  * version 2 of the License, or (at your option) version 3.
   7  *
   8  * This program is distributed in the hope that it will be useful,
   9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11  * Lesser General Public License for more details.
  12  *
  13  * You should have received a copy of the GNU Lesser General Public
  14  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  15  *
  16  *
  17  * Authors:
  18  *              Chris Lahey <clahey@ximian.com>
  19  *
  20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include <config.h>
  25 #endif
  26 
  27 #include <math.h>
  28 #include <string.h>
  29 
  30 #include <gdk/gdkkeysyms.h>
  31 #include <gtk/gtk.h>
  32 
  33 #include "text/e-text.h"
  34 #include <glib/gi18n.h>
  35 #include "e-util/e-util.h"
  36 #include "e-util/e-unicode.h"
  37 
  38 #include "misc/e-canvas.h"
  39 #include "misc/e-canvas-utils.h"
  40 #include "e-reflow.h"
  41 #include "misc/e-selection-model-simple.h"
  42 
  43 static gboolean e_reflow_event (GnomeCanvasItem *item, GdkEvent *event);
  44 static void e_reflow_realize (GnomeCanvasItem *item);
  45 static void e_reflow_unrealize (GnomeCanvasItem *item);
  46 static void e_reflow_draw (GnomeCanvasItem *item, cairo_t *cr,
  47 				    gint x, gint y, gint width, gint height);
  48 static void e_reflow_update (GnomeCanvasItem *item, const cairo_matrix_t *i2c, gint flags);
  49 static GnomeCanvasItem *e_reflow_point (GnomeCanvasItem *item, gdouble x, gdouble y, gint cx, gint cy);
  50 static void e_reflow_reflow (GnomeCanvasItem *item, gint flags);
  51 static void set_empty (EReflow *reflow);
  52 
  53 static void e_reflow_resize_children (GnomeCanvasItem *item);
  54 
  55 #define E_REFLOW_DIVIDER_WIDTH 2
  56 #define E_REFLOW_BORDER_WIDTH 7
  57 #define E_REFLOW_FULL_GUTTER (E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH * 2)
  58 
  59 G_DEFINE_TYPE (EReflow, e_reflow, GNOME_TYPE_CANVAS_GROUP)
  60 
  61 enum {
  62 	PROP_0,
  63 	PROP_MINIMUM_WIDTH,
  64 	PROP_WIDTH,
  65 	PROP_HEIGHT,
  66 	PROP_EMPTY_MESSAGE,
  67 	PROP_MODEL,
  68 	PROP_COLUMN_WIDTH
  69 };
  70 
  71 enum {
  72 	SELECTION_EVENT,
  73 	COLUMN_WIDTH_CHANGED,
  74 	LAST_SIGNAL
  75 };
  76 
  77 static guint signals[LAST_SIGNAL] = {0, };
  78 
  79 static GHashTable *
  80 er_create_cmp_cache (gpointer user_data)
  81 {
  82 	EReflow *reflow = user_data;
  83 	return e_reflow_model_create_cmp_cache (reflow->model);
  84 }
  85 
  86 static gint
  87 er_compare (gint i1,
  88             gint i2,
  89             GHashTable *cmp_cache,
  90             gpointer user_data)
  91 {
  92 	EReflow *reflow = user_data;
  93 	return e_reflow_model_compare (reflow->model, i1, i2, cmp_cache);
  94 }
  95 
  96 static gint
  97 e_reflow_pick_line (EReflow *reflow,
  98                     gdouble x)
  99 {
 100 	x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH;
 101 	x /= reflow->column_width + E_REFLOW_FULL_GUTTER;
 102 	return x;
 103 }
 104 
 105 static gint
 106 er_find_item (EReflow *reflow,
 107               GnomeCanvasItem *item)
 108 {
 109 	gint i;
 110 	for (i = 0; i < reflow->count; i++) {
 111 		if (reflow->items[i] == item)
 112 			return i;
 113 	}
 114 	return -1;
 115 }
 116 
 117 static void
 118 e_reflow_resize_children (GnomeCanvasItem *item)
 119 {
 120 	EReflow *reflow;
 121 	gint i;
 122 	gint count;
 123 
 124 	reflow = E_REFLOW (item);
 125 
 126 	count = reflow->count;
 127 	for (i = 0; i < count; i++) {
 128 		if (reflow->items[i])
 129 			gnome_canvas_item_set (
 130 				reflow->items[i],
 131 				"width", (gdouble) reflow->column_width,
 132 				NULL);
 133 	}
 134 }
 135 
 136 static inline void
 137 e_reflow_update_selection_row (EReflow *reflow,
 138                                gint row)
 139 {
 140 	if (reflow->items[row]) {
 141 		g_object_set (
 142 			reflow->items[row],
 143 			"selected", e_selection_model_is_row_selected (E_SELECTION_MODEL (reflow->selection), row),
 144 			NULL);
 145 	} else if (e_selection_model_is_row_selected (E_SELECTION_MODEL (reflow->selection), row)) {
 146 		reflow->items[row] = e_reflow_model_incarnate (reflow->model, row, GNOME_CANVAS_GROUP (reflow));
 147 		g_object_set (
 148 			reflow->items[row],
 149 			"selected", e_selection_model_is_row_selected (E_SELECTION_MODEL (reflow->selection), row),
 150 			"width", (gdouble) reflow->column_width,
 151 			NULL);
 152 	}
 153 }
 154 
 155 static void
 156 e_reflow_update_selection (EReflow *reflow)
 157 {
 158 	gint i;
 159 	gint count;
 160 
 161 	count = reflow->count;
 162 	for (i = 0; i < count; i++) {
 163 		e_reflow_update_selection_row (reflow, i);
 164 	}
 165 }
 166 
 167 static void
 168 selection_changed (ESelectionModel *selection,
 169                    EReflow *reflow)
 170 {
 171 	e_reflow_update_selection (reflow);
 172 }
 173 
 174 static void
 175 selection_row_changed (ESelectionModel *selection,
 176                        gint row,
 177                        EReflow *reflow)
 178 {
 179 	e_reflow_update_selection_row (reflow, row);
 180 }
 181 
 182 static gboolean
 183 do_adjustment (gpointer user_data)
 184 {
 185 	gint row;
 186 	GtkLayout *layout;
 187 	GtkAdjustment *adjustment;
 188 	gdouble page_size;
 189 	gdouble value, min_value, max_value;
 190 	EReflow *reflow = user_data;
 191 
 192 	row = reflow->cursor_row;
 193 	if (row == -1)
 194 		return FALSE;
 195 
 196 	layout = GTK_LAYOUT (GNOME_CANVAS_ITEM (reflow)->canvas);
 197 	adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout));
 198 
 199 	value = gtk_adjustment_get_value (adjustment);
 200 	page_size = gtk_adjustment_get_page_size (adjustment);
 201 
 202 	if ((!reflow->items) || (!reflow->items[row]))
 203 		return TRUE;
 204 	min_value = reflow->items[row]->x2 - page_size;
 205 	max_value = reflow->items[row]->x1;
 206 
 207 	if (value < min_value)
 208 		value = min_value;
 209 
 210 	if (value > max_value)
 211 		value = max_value;
 212 
 213 	if (value != gtk_adjustment_get_value (adjustment))
 214 		gtk_adjustment_set_value (adjustment, value);
 215 
 216 	reflow->do_adjustment_idle_id = 0;
 217 
 218 	return FALSE;
 219 }
 220 
 221 static void
 222 cursor_changed (ESelectionModel *selection,
 223                 gint row,
 224                 gint col,
 225                 EReflow *reflow)
 226 {
 227 	gint count = reflow->count;
 228 	gint old_cursor = reflow->cursor_row;
 229 
 230 	if (old_cursor < count && old_cursor >= 0) {
 231 		if (reflow->items[old_cursor]) {
 232 			g_object_set (
 233 				reflow->items[old_cursor],
 234 				"has_cursor", FALSE,
 235 				NULL);
 236 		}
 237 	}
 238 
 239 	reflow->cursor_row = row;
 240 
 241 	if (row < count && row >= 0) {
 242 		if (reflow->items[row]) {
 243 			g_object_set (
 244 				reflow->items[row],
 245 				"has_cursor", TRUE,
 246 				NULL);
 247 		} else {
 248 			reflow->items[row] = e_reflow_model_incarnate (reflow->model, row, GNOME_CANVAS_GROUP (reflow));
 249 			g_object_set (
 250 				reflow->items[row],
 251 				"has_cursor", TRUE,
 252 				"width", (gdouble) reflow->column_width,
 253 				NULL);
 254 		}
 255 	}
 256 
 257 	if (reflow->do_adjustment_idle_id == 0)
 258 		reflow->do_adjustment_idle_id = g_idle_add (do_adjustment, reflow);
 259 
 260 }
 261 
 262 static void
 263 incarnate (EReflow *reflow)
 264 {
 265 	gint column_width;
 266 	gint first_column;
 267 	gint last_column;
 268 	gint first_cell;
 269 	gint last_cell;
 270 	gint i;
 271 	GtkLayout *layout;
 272 	GtkAdjustment *adjustment;
 273 	gdouble value;
 274 	gdouble page_size;
 275 
 276 	layout = GTK_LAYOUT (GNOME_CANVAS_ITEM (reflow)->canvas);
 277 	adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout));
 278 
 279 	value = gtk_adjustment_get_value (adjustment);
 280 	page_size = gtk_adjustment_get_page_size (adjustment);
 281 
 282 	column_width = reflow->column_width;
 283 
 284 	first_column = value - 1 + E_REFLOW_BORDER_WIDTH;
 285 	first_column /= column_width + E_REFLOW_FULL_GUTTER;
 286 
 287 	last_column = value + page_size + 1 - E_REFLOW_BORDER_WIDTH - E_REFLOW_DIVIDER_WIDTH;
 288 	last_column /= column_width + E_REFLOW_FULL_GUTTER;
 289 	last_column++;
 290 
 291 	if (first_column >= 0 && first_column < reflow->column_count)
 292 		first_cell = reflow->columns[first_column];
 293 	else
 294 		first_cell = 0;
 295 
 296 	if (last_column >= 0 && last_column < reflow->column_count)
 297 		last_cell = reflow->columns[last_column];
 298 	else
 299 		last_cell = reflow->count;
 300 
 301 	for (i = first_cell; i < last_cell; i++) {
 302 		gint unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i);
 303 		if (reflow->items[unsorted] == NULL) {
 304 			if (reflow->model) {
 305 				reflow->items[unsorted] = e_reflow_model_incarnate (reflow->model, unsorted, GNOME_CANVAS_GROUP (reflow));
 306 				g_object_set (
 307 					reflow->items[unsorted],
 308 					"selected", e_selection_model_is_row_selected (E_SELECTION_MODEL (reflow->selection), unsorted),
 309 					"width", (gdouble) reflow->column_width,
 310 					NULL);
 311 			}
 312 		}
 313 	}
 314 	reflow->incarnate_idle_id = 0;
 315 }
 316 
 317 static gboolean
 318 invoke_incarnate (gpointer user_data)
 319 {
 320 	EReflow *reflow = user_data;
 321 	incarnate (reflow);
 322 	return FALSE;
 323 }
 324 
 325 static void
 326 queue_incarnate (EReflow *reflow)
 327 {
 328 	if (reflow->incarnate_idle_id == 0)
 329 		reflow->incarnate_idle_id =
 330 			g_idle_add_full (25, invoke_incarnate, reflow, NULL);
 331 }
 332 
 333 static void
 334 reflow_columns (EReflow *reflow)
 335 {
 336 	GSList *list;
 337 	gint count;
 338 	gint start;
 339 	gint i;
 340 	gint column_count, column_start;
 341 	gdouble running_height;
 342 
 343 	if (reflow->reflow_from_column <= 1) {
 344 		start = 0;
 345 		column_count = 1;
 346 		column_start = 0;
 347 	}
 348 	else {
 349 		/* we start one column before the earliest new entry,
 350 		 * so we can handle the case where the new entry is
 351 		 * inserted at the start of the column */
 352 		column_start = reflow->reflow_from_column - 1;
 353 		start = reflow->columns[column_start];
 354 		column_count = column_start + 1;
 355 	}
 356 
 357 	list = NULL;
 358 
 359 	running_height = E_REFLOW_BORDER_WIDTH;
 360 
 361 	count = reflow->count - start;
 362 	for (i = start; i < count; i++) {
 363 		gint unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i);
 364 		if (i != 0 && running_height + reflow->heights[unsorted] + E_REFLOW_BORDER_WIDTH > reflow->height) {
 365 			list = g_slist_prepend (list, GINT_TO_POINTER (i));
 366 			column_count++;
 367 			running_height = E_REFLOW_BORDER_WIDTH * 2 + reflow->heights[unsorted];
 368 		} else
 369 			running_height += reflow->heights[unsorted] + E_REFLOW_BORDER_WIDTH;
 370 	}
 371 
 372 	reflow->column_count = column_count;
 373 	reflow->columns = g_renew (int, reflow->columns, column_count);
 374 	column_count--;
 375 
 376 	for (; column_count > column_start; column_count--) {
 377 		GSList *to_free;
 378 		reflow->columns[column_count] = GPOINTER_TO_INT (list->data);
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'list')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'data' results in a dereference of a null pointer (loaded from variable 'list')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

379 to_free = list; 380 list = list->next; 381 g_slist_free_1 (to_free); 382 } 383 reflow->columns[column_start] = start; 384 385 queue_incarnate (reflow); 386 387 reflow->need_reflow_columns = FALSE; 388 reflow->reflow_from_column = -1; 389 } 390 391 static void 392 item_changed (EReflowModel *model, 393 gint i, 394 EReflow *reflow) 395 { 396 if (i < 0 || i >= reflow->count) 397 return; 398 399 reflow->heights[i] = e_reflow_model_height (reflow->model, i, GNOME_CANVAS_GROUP (reflow)); 400 if (reflow->items[i] != NULL) 401 e_reflow_model_reincarnate (model, i, reflow->items[i]); 402 e_sorter_array_clean (reflow->sorter); 403 reflow->reflow_from_column = -1; 404 reflow->need_reflow_columns = TRUE; 405 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (reflow)); 406 } 407 408 static void 409 item_removed (EReflowModel *model, 410 gint i, 411 EReflow *reflow) 412 { 413 gint c; 414 gint sorted; 415 416 if (i < 0 || i >= reflow->count) 417 return; 418 419 sorted = e_sorter_model_to_sorted (E_SORTER (reflow->sorter), i); 420 for (c = reflow->column_count - 1; c >= 0; c--) { 421 gint start_of_column = reflow->columns[c]; 422 423 if (start_of_column <= sorted) { 424 if (reflow->reflow_from_column == -1 425 || reflow->reflow_from_column > c) { 426 reflow->reflow_from_column = c; 427 } 428 break; 429 } 430 } 431 432 if (reflow->items[i]) 433 g_object_run_dispose (G_OBJECT (reflow->items[i])); 434 435 memmove (reflow->heights + i, reflow->heights + i + 1, (reflow->count - i - 1) * sizeof (gint)); 436 memmove (reflow->items + i, reflow->items + i + 1, (reflow->count - i - 1) * sizeof (GnomeCanvasItem *)); 437 438 reflow->count--; 439 440 reflow->heights[reflow->count] = 0; 441 reflow->items[reflow->count] = NULL; 442 443 reflow->need_reflow_columns = TRUE; 444 set_empty (reflow); 445 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (reflow)); 446 447 e_sorter_array_set_count (reflow->sorter, reflow->count); 448 449 e_selection_model_simple_delete_rows (E_SELECTION_MODEL_SIMPLE (reflow->selection), i, 1); 450 } 451 452 static void 453 items_inserted (EReflowModel *model, 454 gint position, 455 gint count, 456 EReflow *reflow) 457 { 458 gint i, oldcount; 459 460 if (position < 0 || position > reflow->count) 461 return; 462 463 oldcount = reflow->count; 464 465 reflow->count += count; 466 467 if (reflow->count > reflow->allocated_count) { 468 while (reflow->count > reflow->allocated_count) 469 reflow->allocated_count += 256; 470 reflow->heights = g_renew (int, reflow->heights, reflow->allocated_count); 471 reflow->items = g_renew (GnomeCanvasItem *, reflow->items, reflow->allocated_count); 472 } 473 memmove (reflow->heights + position + count, reflow->heights + position, (reflow->count - position - count) * sizeof (gint)); 474 memmove (reflow->items + position + count, reflow->items + position, (reflow->count - position - count) * sizeof (GnomeCanvasItem *)); 475 for (i = position; i < position + count; i++) { 476 reflow->items[i] = NULL; 477 reflow->heights[i] = e_reflow_model_height (reflow->model, i, GNOME_CANVAS_GROUP (reflow)); 478 } 479 480 e_selection_model_simple_set_row_count (E_SELECTION_MODEL_SIMPLE (reflow->selection), reflow->count); 481 if (position == oldcount) 482 e_sorter_array_append (reflow->sorter, count); 483 else 484 e_sorter_array_set_count (reflow->sorter, reflow->count); 485 486 for (i = position; i < position + count; i++) { 487 gint sorted = e_sorter_model_to_sorted (E_SORTER (reflow->sorter), i); 488 gint c; 489 490 for (c = reflow->column_count - 1; c >= 0; c--) { 491 gint start_of_column = reflow->columns[c]; 492 493 if (start_of_column <= sorted) { 494 if (reflow->reflow_from_column == -1 495 || reflow->reflow_from_column > c) { 496 reflow->reflow_from_column = c; 497 } 498 break; 499 } 500 } 501 } 502 503 reflow->need_reflow_columns = TRUE; 504 set_empty (reflow); 505 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (reflow)); 506 } 507 508 static void 509 model_changed (EReflowModel *model, 510 EReflow *reflow) 511 { 512 gint i; 513 gint count; 514 gint oldcount; 515 516 count = reflow->count; 517 oldcount = count; 518 519 for (i = 0; i < count; i++) { 520 if (reflow->items[i]) 521 g_object_run_dispose (G_OBJECT (reflow->items[i])); 522 } 523 g_free (reflow->items); 524 g_free (reflow->heights); 525 reflow->count = e_reflow_model_count (model); 526 reflow->allocated_count = reflow->count; 527 reflow->items = g_new (GnomeCanvasItem *, reflow->count); 528 reflow->heights = g_new (int, reflow->count); 529 530 count = reflow->count; 531 for (i = 0; i < count; i++) { 532 reflow->items[i] = NULL; 533 reflow->heights[i] = e_reflow_model_height (reflow->model, i, GNOME_CANVAS_GROUP (reflow)); 534 } 535 536 e_selection_model_simple_set_row_count (E_SELECTION_MODEL_SIMPLE (reflow->selection), count); 537 e_sorter_array_set_count (reflow->sorter, reflow->count); 538 539 reflow->need_reflow_columns = TRUE; 540 if (oldcount > reflow->count) 541 reflow_columns (reflow); 542 set_empty (reflow); 543 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (reflow)); 544 } 545 546 static void 547 comparison_changed (EReflowModel *model, 548 EReflow *reflow) 549 { 550 e_sorter_array_clean (reflow->sorter); 551 reflow->reflow_from_column = -1; 552 reflow->need_reflow_columns = TRUE; 553 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (reflow)); 554 } 555 556 static void 557 set_empty (EReflow *reflow) 558 { 559 if (reflow->count == 0) { 560 if (reflow->empty_text) { 561 if (reflow->empty_message) { 562 gnome_canvas_item_set ( 563 reflow->empty_text, 564 "width", reflow->minimum_width, 565 "text", reflow->empty_message, 566 NULL); 567 e_canvas_item_move_absolute ( 568 reflow->empty_text, 569 reflow->minimum_width / 2, 570 0); 571 } else { 572 g_object_run_dispose (G_OBJECT (reflow->empty_text)); 573 reflow->empty_text = NULL; 574 } 575 } else { 576 if (reflow->empty_message) { 577 reflow->empty_text = gnome_canvas_item_new ( 578 GNOME_CANVAS_GROUP (reflow), 579 e_text_get_type (), 580 "width", reflow->minimum_width, 581 "clip", TRUE, 582 "use_ellipsis", TRUE, 583 "justification", GTK_JUSTIFY_CENTER, 584 "text", reflow->empty_message, 585 NULL); 586 e_canvas_item_move_absolute ( 587 reflow->empty_text, 588 reflow->minimum_width / 2, 589 0); 590 } 591 } 592 } else { 593 if (reflow->empty_text) { 594 g_object_run_dispose (G_OBJECT (reflow->empty_text)); 595 reflow->empty_text = NULL; 596 } 597 } 598 } 599 600 static void 601 disconnect_model (EReflow *reflow) 602 { 603 if (reflow->model == NULL) 604 return; 605 606 g_signal_handler_disconnect ( 607 reflow->model, 608 reflow->model_changed_id); 609 g_signal_handler_disconnect ( 610 reflow->model, 611 reflow->comparison_changed_id); 612 g_signal_handler_disconnect ( 613 reflow->model, 614 reflow->model_items_inserted_id); 615 g_signal_handler_disconnect ( 616 reflow->model, 617 reflow->model_item_removed_id); 618 g_signal_handler_disconnect ( 619 reflow->model, 620 reflow->model_item_changed_id); 621 g_object_unref (reflow->model); 622 623 reflow->model_changed_id = 0; 624 reflow->comparison_changed_id = 0; 625 reflow->model_items_inserted_id = 0; 626 reflow->model_item_removed_id = 0; 627 reflow->model_item_changed_id = 0; 628 reflow->model = NULL; 629 } 630 631 static void 632 disconnect_selection (EReflow *reflow) 633 { 634 if (reflow->selection == NULL) 635 return; 636 637 g_signal_handler_disconnect ( 638 reflow->selection, 639 reflow->selection_changed_id); 640 g_signal_handler_disconnect ( 641 reflow->selection, 642 reflow->selection_row_changed_id); 643 g_signal_handler_disconnect ( 644 reflow->selection, 645 reflow->cursor_changed_id); 646 g_object_unref (reflow->selection); 647 648 reflow->selection_changed_id = 0; 649 reflow->selection_row_changed_id = 0; 650 reflow->cursor_changed_id = 0; 651 reflow->selection = NULL; 652 } 653 654 static void 655 connect_model (EReflow *reflow, 656 EReflowModel *model) 657 { 658 if (reflow->model != NULL) 659 disconnect_model (reflow); 660 661 if (model == NULL) 662 return; 663 664 reflow->model = g_object_ref (model); 665 666 reflow->model_changed_id = g_signal_connect ( 667 reflow->model, "model_changed", 668 G_CALLBACK (model_changed), reflow); 669 670 reflow->comparison_changed_id = g_signal_connect ( 671 reflow->model, "comparison_changed", 672 G_CALLBACK (comparison_changed), reflow); 673 674 reflow->model_items_inserted_id = g_signal_connect ( 675 reflow->model, "model_items_inserted", 676 G_CALLBACK (items_inserted), reflow); 677 678 reflow->model_item_removed_id = g_signal_connect ( 679 reflow->model, "model_item_removed", 680 G_CALLBACK (item_removed), reflow); 681 682 reflow->model_item_changed_id = g_signal_connect ( 683 reflow->model, "model_item_changed", 684 G_CALLBACK (item_changed), reflow); 685 686 model_changed (model, reflow); 687 } 688 689 static void 690 adjustment_changed (GtkAdjustment *adjustment, 691 EReflow *reflow) 692 { 693 queue_incarnate (reflow); 694 } 695 696 static void 697 disconnect_adjustment (EReflow *reflow) 698 { 699 if (reflow->adjustment == NULL) 700 return; 701 702 g_signal_handler_disconnect ( 703 reflow->adjustment, 704 reflow->adjustment_changed_id); 705 g_signal_handler_disconnect ( 706 reflow->adjustment, 707 reflow->adjustment_value_changed_id); 708 709 g_object_unref (reflow->adjustment); 710 711 reflow->adjustment_changed_id = 0; 712 reflow->adjustment_value_changed_id = 0; 713 reflow->adjustment = NULL; 714 } 715 716 static void 717 connect_adjustment (EReflow *reflow, 718 GtkAdjustment *adjustment) 719 { 720 if (reflow->adjustment != NULL) 721 disconnect_adjustment (reflow); 722 723 if (adjustment == NULL) 724 return; 725 726 reflow->adjustment = g_object_ref (adjustment); 727 728 reflow->adjustment_changed_id = g_signal_connect ( 729 adjustment, "changed", 730 G_CALLBACK (adjustment_changed), reflow); 731 732 reflow->adjustment_value_changed_id = g_signal_connect ( 733 adjustment, "value_changed", 734 G_CALLBACK (adjustment_changed), reflow); 735 } 736 737 #if 0 738 static void 739 set_scroll_adjustments (GtkLayout *layout, 740 GtkAdjustment *hadj, 741 GtkAdjustment *vadj, 742 EReflow *reflow) 743 { 744 connect_adjustment (reflow, hadj); 745 } 746 747 static void 748 connect_set_adjustment (EReflow *reflow) 749 { 750 reflow->set_scroll_adjustments_id = g_signal_connect ( 751 GNOME_CANVAS_ITEM (reflow)->canvas, "set_scroll_adjustments", 752 G_CALLBACK (set_scroll_adjustments), reflow); 753 } 754 #endif 755 756 static void 757 disconnect_set_adjustment (EReflow *reflow) 758 { 759 if (reflow->set_scroll_adjustments_id != 0) { 760 g_signal_handler_disconnect ( 761 GNOME_CANVAS_ITEM (reflow)->canvas, 762 reflow->set_scroll_adjustments_id); 763 reflow->set_scroll_adjustments_id = 0; 764 } 765 } 766 767 static void 768 column_width_changed (EReflow *reflow) 769 { 770 g_signal_emit (reflow, signals[COLUMN_WIDTH_CHANGED], 0, reflow->column_width); 771 } 772 773 /* Virtual functions */ 774 static void 775 e_reflow_set_property (GObject *object, 776 guint property_id, 777 const GValue *value, 778 GParamSpec *pspec) 779 { 780 GnomeCanvasItem *item; 781 EReflow *reflow; 782 783 item = GNOME_CANVAS_ITEM (object); 784 reflow = E_REFLOW (object); 785 786 switch (property_id) { 787 case PROP_HEIGHT: 788 reflow->height = g_value_get_double (value); 789 reflow->need_reflow_columns = TRUE; 790 e_canvas_item_request_reflow (item); 791 break; 792 case PROP_MINIMUM_WIDTH: 793 reflow->minimum_width = g_value_get_double (value); 794 if (item->flags & GNOME_CANVAS_ITEM_REALIZED) 795 set_empty (reflow); 796 e_canvas_item_request_reflow (item); 797 break; 798 case PROP_EMPTY_MESSAGE: 799 g_free (reflow->empty_message); 800 reflow->empty_message = g_strdup (g_value_get_string (value)); 801 if (item->flags & GNOME_CANVAS_ITEM_REALIZED) 802 set_empty (reflow); 803 break; 804 case PROP_MODEL: 805 connect_model (reflow, (EReflowModel *) g_value_get_object (value)); 806 break; 807 case PROP_COLUMN_WIDTH: 808 if (reflow->column_width != g_value_get_double (value)) { 809 GtkLayout *layout; 810 GtkAdjustment *adjustment; 811 gdouble old_width = reflow->column_width; 812 gdouble step_increment; 813 gdouble page_size; 814 815 layout = GTK_LAYOUT (item->canvas); 816 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); 817 page_size = gtk_adjustment_get_page_size (adjustment); 818 819 reflow->column_width = g_value_get_double (value); 820 step_increment = (reflow->column_width + 821 E_REFLOW_FULL_GUTTER) / 2; 822 gtk_adjustment_set_step_increment ( 823 adjustment, step_increment); 824 gtk_adjustment_set_page_increment ( 825 adjustment, page_size - step_increment); 826 e_reflow_resize_children (item); 827 e_canvas_item_request_reflow (item); 828 829 reflow->need_column_resize = TRUE; 830 gnome_canvas_item_request_update (item); 831 832 if (old_width != reflow->column_width) 833 column_width_changed (reflow); 834 } 835 break; 836 } 837 } 838 839 static void 840 e_reflow_get_property (GObject *object, 841 guint property_id, 842 GValue *value, 843 GParamSpec *pspec) 844 { 845 EReflow *reflow; 846 847 reflow = E_REFLOW (object); 848 849 switch (property_id) { 850 case PROP_MINIMUM_WIDTH: 851 g_value_set_double (value, reflow->minimum_width); 852 break; 853 case PROP_WIDTH: 854 g_value_set_double (value, reflow->width); 855 break; 856 case PROP_HEIGHT: 857 g_value_set_double (value, reflow->height); 858 break; 859 case PROP_EMPTY_MESSAGE: 860 g_value_set_string (value, reflow->empty_message); 861 break; 862 case PROP_MODEL: 863 g_value_set_object (value, reflow->model); 864 break; 865 case PROP_COLUMN_WIDTH: 866 g_value_set_double (value, reflow->column_width); 867 break; 868 default: 869 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 870 break; 871 } 872 } 873 874 static void 875 e_reflow_dispose (GObject *object) 876 { 877 EReflow *reflow = E_REFLOW (object); 878 879 g_free (reflow->items); 880 g_free (reflow->heights); 881 g_free (reflow->columns); 882 883 reflow->items = NULL; 884 reflow->heights = NULL; 885 reflow->columns = NULL; 886 reflow->count = 0; 887 reflow->allocated_count = 0; 888 889 if (reflow->incarnate_idle_id) 890 g_source_remove (reflow->incarnate_idle_id); 891 reflow->incarnate_idle_id = 0; 892 893 if (reflow->do_adjustment_idle_id) 894 g_source_remove (reflow->do_adjustment_idle_id); 895 reflow->do_adjustment_idle_id = 0; 896 897 disconnect_model (reflow); 898 disconnect_selection (reflow); 899 900 g_free (reflow->empty_message); 901 reflow->empty_message = NULL; 902 903 if (reflow->sorter) { 904 g_object_unref (reflow->sorter); 905 reflow->sorter = NULL; 906 } 907 908 G_OBJECT_CLASS (e_reflow_parent_class)->dispose (object); 909 } 910 911 static void 912 e_reflow_realize (GnomeCanvasItem *item) 913 { 914 EReflow *reflow; 915 GtkAdjustment *adjustment; 916 gdouble page_increment; 917 gdouble step_increment; 918 gdouble page_size; 919 gint count; 920 gint i; 921 922 reflow = E_REFLOW (item); 923 924 if (GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->realize) 925 (* GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->realize) (item); 926 927 reflow->arrow_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); 928 reflow->default_cursor = gdk_cursor_new (GDK_LEFT_PTR); 929 930 count = reflow->count; 931 for (i = 0; i < count; i++) { 932 if (reflow->items[i]) 933 gnome_canvas_item_set ( 934 reflow->items[i], 935 "width", reflow->column_width, 936 NULL); 937 } 938 939 set_empty (reflow); 940 941 reflow->need_reflow_columns = TRUE; 942 e_canvas_item_request_reflow (item); 943 944 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (item->canvas)); 945 946 #if 0 947 connect_set_adjustment (reflow); 948 #endif 949 connect_adjustment (reflow, adjustment); 950 951 page_size = gtk_adjustment_get_page_size (adjustment); 952 step_increment = (reflow->column_width + E_REFLOW_FULL_GUTTER) / 2; 953 page_increment = page_size - step_increment; 954 gtk_adjustment_set_step_increment (adjustment, step_increment); 955 gtk_adjustment_set_page_increment (adjustment, page_increment); 956 } 957 958 static void 959 e_reflow_unrealize (GnomeCanvasItem *item) 960 { 961 EReflow *reflow; 962 963 reflow = E_REFLOW (item); 964 965 g_object_unref (reflow->arrow_cursor); 966 g_object_unref (reflow->default_cursor); 967 reflow->arrow_cursor = NULL; 968 reflow->default_cursor = NULL; 969 970 g_free (reflow->columns); 971 reflow->columns = NULL; 972 973 disconnect_set_adjustment (reflow); 974 disconnect_adjustment (reflow); 975 976 if (GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->unrealize) 977 (* GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->unrealize) (item); 978 } 979 980 static gboolean 981 e_reflow_event (GnomeCanvasItem *item, 982 GdkEvent *event) 983 { 984 EReflow *reflow; 985 gint return_val = FALSE; 986 987 reflow = E_REFLOW (item); 988 989 switch (event->type) 990 { 991 case GDK_KEY_PRESS: 992 return_val = e_selection_model_key_press (reflow->selection, (GdkEventKey *) event); 993 break; 994 #if 0 995 if (event->key.keyval == GDK_Tab || 996 event->key.keyval == GDK_KEY_KP_Tab || 997 event->key.keyval == GDK_ISO_Left_Tab) { 998 gint i; 999 gint count; 1000 count = reflow->count; 1001 for (i = 0; i < count; i++) { 1002 gint unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i); 1003 GnomeCanvasItem *item = reflow->items[unsorted]; 1004 EFocus has_focus; 1005 if (item) { 1006 g_object_get ( 1007 item, 1008 "has_focus", &has_focus, 1009 NULL); 1010 if (has_focus) { 1011 if (event->key.state & GDK_SHIFT_MASK) { 1012 if (i == 0) 1013 return FALSE; 1014 i--; 1015 } else { 1016 if (i == count - 1) 1017 return FALSE; 1018 i++; 1019 } 1020 1021 unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i); 1022 if (reflow->items[unsorted] == NULL) { 1023 reflow->items[unsorted] = e_reflow_model_incarnate (reflow->model, unsorted, GNOME_CANVAS_GROUP (reflow)); 1024 } 1025 1026 item = reflow->items[unsorted]; 1027 gnome_canvas_item_set ( 1028 item, 1029 "has_focus", (event->key.state & GDK_SHIFT_MASK) ? E_FOCUS_END : E_FOCUS_START, 1030 NULL); 1031 return TRUE; 1032 } 1033 } 1034 } 1035 } 1036 #endif 1037 case GDK_BUTTON_PRESS: 1038 switch (event->button.button) 1039 { 1040 case 1: 1041 { 1042 GdkEventButton *button = (GdkEventButton *) event; 1043 gdouble n_x; 1044 n_x = button->x; 1045 n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; 1046 n_x = fmod (n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER)); 1047 1048 if (button->y >= E_REFLOW_BORDER_WIDTH && button->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER) { 1049 /* don't allow to drag the first line*/ 1050 if (e_reflow_pick_line (reflow, button->x) == 0) 1051 return TRUE; 1052 reflow->which_column_dragged = e_reflow_pick_line (reflow, button->x); 1053 reflow->start_x = reflow->which_column_dragged * (reflow->column_width + E_REFLOW_FULL_GUTTER) - E_REFLOW_DIVIDER_WIDTH / 2; 1054 reflow->temp_column_width = reflow->column_width; 1055 reflow->column_drag = TRUE; 1056 1057 gnome_canvas_item_grab ( 1058 item, 1059 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK, 1060 reflow->arrow_cursor, 1061 button->time); 1062 1063 reflow->previous_temp_column_width = -1; 1064 reflow->need_column_resize = TRUE; 1065 gnome_canvas_item_request_update (item); 1066 return TRUE; 1067 } 1068 } 1069 break; 1070 case 4: 1071 { 1072 GtkLayout *layout; 1073 GtkAdjustment *adjustment; 1074 gdouble new_value; 1075 1076 layout = GTK_LAYOUT (item->canvas); 1077 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); 1078 new_value = gtk_adjustment_get_value (adjustment); 1079 new_value -= gtk_adjustment_get_step_increment (adjustment); 1080 gtk_adjustment_set_value (adjustment, new_value); 1081 } 1082 break; 1083 case 5: 1084 { 1085 GtkLayout *layout; 1086 GtkAdjustment *adjustment; 1087 gdouble new_value; 1088 gdouble page_size; 1089 gdouble upper; 1090 1091 layout = GTK_LAYOUT (item->canvas); 1092 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); 1093 new_value = gtk_adjustment_get_value (adjustment); 1094 new_value += gtk_adjustment_get_step_increment (adjustment); 1095 upper = gtk_adjustment_get_upper (adjustment); 1096 page_size = gtk_adjustment_get_page_size (adjustment); 1097 if (new_value > upper - page_size) 1098 new_value = upper - page_size; 1099 gtk_adjustment_set_value (adjustment, new_value); 1100 } 1101 break; 1102 } 1103 break; 1104 case GDK_BUTTON_RELEASE: 1105 if (reflow->column_drag) { 1106 gdouble old_width = reflow->column_width; 1107 GdkEventButton *button = (GdkEventButton *) event; 1108 GtkAdjustment *adjustment; 1109 GtkLayout *layout; 1110 gdouble value; 1111 1112 layout = GTK_LAYOUT (item->canvas); 1113 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); 1114 value = gtk_adjustment_get_value (adjustment); 1115 1116 reflow->temp_column_width = reflow->column_width + 1117 (button->x - reflow->start_x) / (reflow->which_column_dragged - e_reflow_pick_line (reflow, value)); 1118 if (reflow->temp_column_width < 50) 1119 reflow->temp_column_width = 50; 1120 reflow->column_drag = FALSE; 1121 if (old_width != reflow->temp_column_width) { 1122 gdouble page_increment; 1123 gdouble step_increment; 1124 gdouble page_size; 1125 1126 page_size = gtk_adjustment_get_page_size (adjustment); 1127 gtk_adjustment_set_value (adjustment, value + e_reflow_pick_line (reflow, value) * (reflow->temp_column_width - reflow->column_width)); 1128 reflow->column_width = reflow->temp_column_width; 1129 step_increment = (reflow->column_width + E_REFLOW_FULL_GUTTER) / 2; 1130 page_increment = page_size - step_increment; 1131 gtk_adjustment_set_step_increment (adjustment, step_increment); 1132 gtk_adjustment_set_page_increment (adjustment, page_increment); 1133 e_reflow_resize_children (item); 1134 e_canvas_item_request_reflow (item); 1135 gnome_canvas_request_redraw (item->canvas, 0, 0, reflow->width, reflow->height); 1136 column_width_changed (reflow); 1137 } 1138 reflow->need_column_resize = TRUE; 1139 gnome_canvas_item_request_update (item); 1140 gnome_canvas_item_ungrab (item, button->time); 1141 return TRUE; 1142 } 1143 break; 1144 case GDK_MOTION_NOTIFY: 1145 if (reflow->column_drag) { 1146 gdouble old_width = reflow->temp_column_width; 1147 GdkEventMotion *motion = (GdkEventMotion *) event; 1148 GtkAdjustment *adjustment; 1149 GtkLayout *layout; 1150 gdouble value; 1151 1152 layout = GTK_LAYOUT (item->canvas); 1153 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); 1154 value = gtk_adjustment_get_value (adjustment); 1155 1156 reflow->temp_column_width = reflow->column_width + 1157 (motion->x - reflow->start_x) / (reflow->which_column_dragged - e_reflow_pick_line (reflow, value)); 1158 if (reflow->temp_column_width < 50) 1159 reflow->temp_column_width = 50; 1160 if (old_width != reflow->temp_column_width) { 1161 reflow->need_column_resize = TRUE; 1162 gnome_canvas_item_request_update (item); 1163 } 1164 return TRUE; 1165 } else { 1166 GdkEventMotion *motion = (GdkEventMotion *) event; 1167 GdkWindow *window; 1168 gdouble n_x; 1169 1170 n_x = motion->x; 1171 n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; 1172 n_x = fmod (n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER)); 1173 1174 window = gtk_widget_get_window (GTK_WIDGET (item->canvas)); 1175 1176 if (motion->y >= E_REFLOW_BORDER_WIDTH && motion->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER) { 1177 if (reflow->default_cursor_shown) { 1178 gdk_window_set_cursor (window, reflow->arrow_cursor); 1179 reflow->default_cursor_shown = FALSE; 1180 } 1181 } else 1182 if (!reflow->default_cursor_shown) { 1183 gdk_window_set_cursor (window, reflow->default_cursor); 1184 reflow->default_cursor_shown = TRUE; 1185 } 1186 1187 } 1188 break; 1189 case GDK_ENTER_NOTIFY: 1190 if (!reflow->column_drag) { 1191 GdkEventCrossing *crossing = (GdkEventCrossing *) event; 1192 GdkWindow *window; 1193 gdouble n_x; 1194 1195 n_x = crossing->x; 1196 n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; 1197 n_x = fmod (n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER)); 1198 1199 window = gtk_widget_get_window (GTK_WIDGET (item->canvas)); 1200 1201 if (crossing->y >= E_REFLOW_BORDER_WIDTH && crossing->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER) { 1202 if (reflow->default_cursor_shown) { 1203 gdk_window_set_cursor (window, reflow->arrow_cursor); 1204 reflow->default_cursor_shown = FALSE; 1205 } 1206 } 1207 } 1208 break; 1209 case GDK_LEAVE_NOTIFY: 1210 if (!reflow->column_drag) { 1211 GdkEventCrossing *crossing = (GdkEventCrossing *) event; 1212 GdkWindow *window; 1213 gdouble n_x; 1214 1215 n_x = crossing->x; 1216 n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; 1217 n_x = fmod (n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER)); 1218 1219 window = gtk_widget_get_window (GTK_WIDGET (item->canvas)); 1220 1221 if (!(crossing->y >= E_REFLOW_BORDER_WIDTH && crossing->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER)) { 1222 if (!reflow->default_cursor_shown) { 1223 gdk_window_set_cursor (window, reflow->default_cursor); 1224 reflow->default_cursor_shown = TRUE; 1225 } 1226 } 1227 } 1228 break; 1229 default: 1230 break; 1231 } 1232 if (return_val) 1233 return return_val; 1234 else if (GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->event) 1235 return (* GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->event) (item, event); 1236 else 1237 return FALSE; 1238 } 1239 1240 static void 1241 e_reflow_draw (GnomeCanvasItem *item, 1242 cairo_t *cr, 1243 gint x, 1244 gint y, 1245 gint width, 1246 gint height) 1247 { 1248 GtkStyle *style; 1249 gint x_rect, y_rect, width_rect, height_rect; 1250 gdouble running_width; 1251 EReflow *reflow = E_REFLOW (item); 1252 gint i; 1253 gdouble column_width; 1254 1255 if (GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->draw) 1256 GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->draw (item, cr, x, y, width, height); 1257 column_width = reflow->column_width; 1258 running_width = E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; 1259 y_rect = E_REFLOW_BORDER_WIDTH; 1260 width_rect = E_REFLOW_DIVIDER_WIDTH; 1261 height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2); 1262 1263 /* Compute first column to draw. */ 1264 i = x; 1265 i /= column_width + E_REFLOW_FULL_GUTTER; 1266 running_width += i * (column_width + E_REFLOW_FULL_GUTTER); 1267 1268 style = gtk_widget_get_style (GTK_WIDGET (item->canvas)); 1269 1270 for (; i < reflow->column_count; i++) { 1271 if (running_width > x + width) 1272 break; 1273 x_rect = running_width; 1274 gtk_paint_flat_box ( 1275 style, 1276 cr, 1277 GTK_STATE_ACTIVE, 1278 GTK_SHADOW_NONE, 1279 GTK_WIDGET (item->canvas), 1280 "reflow", 1281 x_rect - x, 1282 y_rect - y, 1283 width_rect, 1284 height_rect); 1285 running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; 1286 } 1287 if (reflow->column_drag) { 1288 GtkAdjustment *adjustment; 1289 GtkLayout *layout; 1290 gdouble value; 1291 gint start_line; 1292 1293 layout = GTK_LAYOUT (item->canvas); 1294 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); 1295 value = gtk_adjustment_get_value (adjustment); 1296 1297 start_line = e_reflow_pick_line (reflow, value); 1298 i = x - start_line * (column_width + E_REFLOW_FULL_GUTTER); 1299 running_width = start_line * (column_width + E_REFLOW_FULL_GUTTER); 1300 column_width = reflow->temp_column_width; 1301 running_width -= start_line * (column_width + E_REFLOW_FULL_GUTTER); 1302 i += start_line * (column_width + E_REFLOW_FULL_GUTTER); 1303 running_width += E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; 1304 y_rect = E_REFLOW_BORDER_WIDTH; 1305 width_rect = E_REFLOW_DIVIDER_WIDTH; 1306 height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2); 1307 1308 /* Compute first column to draw. */ 1309 i /= column_width + E_REFLOW_FULL_GUTTER; 1310 running_width += i * (column_width + E_REFLOW_FULL_GUTTER); 1311 1312 cairo_save (cr); 1313 gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]); 1314 1315 for (; i < reflow->column_count; i++) { 1316 if (running_width > x + width) 1317 break; 1318 x_rect = running_width; 1319 cairo_rectangle ( 1320 cr, 1321 x_rect - x, 1322 y_rect - y, 1323 width_rect - 1, 1324 height_rect - 1); 1325 cairo_fill (cr); 1326 running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; 1327 } 1328 cairo_restore (cr); 1329 } 1330 } 1331 1332 static void 1333 e_reflow_update (GnomeCanvasItem *item, 1334 const cairo_matrix_t *i2c, 1335 gint flags) 1336 { 1337 EReflow *reflow; 1338 gdouble x0, x1, y0, y1; 1339 1340 reflow = E_REFLOW (item); 1341 1342 if (GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->update) 1343 GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->update (item, i2c, flags); 1344 1345 x0 = item->x1; 1346 y0 = item->y1; 1347 x1 = item->x2; 1348 y1 = item->y2; 1349 if (x1 < x0 + reflow->width) 1350 x1 = x0 + reflow->width; 1351 if (y1 < y0 + reflow->height) 1352 y1 = y0 + reflow->height; 1353 item->x2 = x1; 1354 item->y2 = y1; 1355 1356 if (reflow->need_height_update) { 1357 x0 = item->x1; 1358 y0 = item->y1; 1359 x1 = item->x2; 1360 y1 = item->y2; 1361 if (x0 > 0) 1362 x0 = 0; 1363 if (y0 > 0) 1364 y0 = 0; 1365 if (x1 < E_REFLOW (item)->width) 1366 x1 = E_REFLOW (item)->width; 1367 if (x1 < E_REFLOW (item)->height) 1368 x1 = E_REFLOW (item)->height; 1369 1370 gnome_canvas_request_redraw (item->canvas, x0, y0, x1, y1); 1371 reflow->need_height_update = FALSE; 1372 } else if (reflow->need_column_resize) { 1373 GtkLayout *layout; 1374 GtkAdjustment *adjustment; 1375 gint x_rect, y_rect, width_rect, height_rect; 1376 gint start_line; 1377 gdouble running_width; 1378 gint i; 1379 gdouble column_width; 1380 gdouble value; 1381 1382 layout = GTK_LAYOUT (item->canvas); 1383 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); 1384 value = gtk_adjustment_get_value (adjustment); 1385 start_line = e_reflow_pick_line (reflow, value); 1386 1387 if (reflow->previous_temp_column_width != -1) { 1388 running_width = start_line * (reflow->column_width + E_REFLOW_FULL_GUTTER); 1389 column_width = reflow->previous_temp_column_width; 1390 running_width -= start_line * (column_width + E_REFLOW_FULL_GUTTER); 1391 running_width += E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; 1392 y_rect = E_REFLOW_BORDER_WIDTH; 1393 width_rect = E_REFLOW_DIVIDER_WIDTH; 1394 height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2); 1395 1396 for (i = 0; i < reflow->column_count; i++) { 1397 x_rect = running_width; 1398 gnome_canvas_request_redraw (item->canvas, x_rect, y_rect, x_rect + width_rect, y_rect + height_rect); 1399 running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; 1400 } 1401 } 1402 1403 if (reflow->temp_column_width != -1) { 1404 running_width = start_line * (reflow->column_width + E_REFLOW_FULL_GUTTER); 1405 column_width = reflow->temp_column_width; 1406 running_width -= start_line * (column_width + E_REFLOW_FULL_GUTTER); 1407 running_width += E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; 1408 y_rect = E_REFLOW_BORDER_WIDTH; 1409 width_rect = E_REFLOW_DIVIDER_WIDTH; 1410 height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2); 1411 1412 for (i = 0; i < reflow->column_count; i++) { 1413 x_rect = running_width; 1414 gnome_canvas_request_redraw (item->canvas, x_rect, y_rect, x_rect + width_rect, y_rect + height_rect); 1415 running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; 1416 } 1417 } 1418 1419 reflow->previous_temp_column_width = reflow->temp_column_width; 1420 reflow->need_column_resize = FALSE; 1421 } 1422 } 1423 1424 static GnomeCanvasItem * 1425 e_reflow_point (GnomeCanvasItem *item, 1426 gdouble x, 1427 gdouble y, 1428 gint cx, 1429 gint cy) 1430 { 1431 GnomeCanvasItem *child = NULL; 1432 1433 if (GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->point) 1434 child = GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->point (item, x, y, cx, cy); 1435 1436 return child ? child : item; 1437 #if 0 1438 if (y >= E_REFLOW_BORDER_WIDTH && y <= reflow->height - E_REFLOW_BORDER_WIDTH) { 1439 gfloat n_x; 1440 n_x = x; 1441 n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; 1442 n_x = fmod (n_x, (reflow->column_width + E_REFLOW_FULL_GUTTER)); 1443 if (n_x < E_REFLOW_FULL_GUTTER) { 1444 *actual_item = item; 1445 return 0; 1446 } 1447 } 1448 return distance; 1449 #endif 1450 } 1451 1452 static void 1453 e_reflow_reflow (GnomeCanvasItem *item, 1454 gint flags) 1455 { 1456 EReflow *reflow = E_REFLOW (item); 1457 gdouble old_width; 1458 gdouble running_width; 1459 gdouble running_height; 1460 gint next_column; 1461 gint i; 1462 1463 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) 1464 return; 1465 1466 if (reflow->need_reflow_columns) { 1467 reflow_columns (reflow); 1468 } 1469 1470 old_width = reflow->width; 1471 1472 running_width = E_REFLOW_BORDER_WIDTH; 1473 running_height = E_REFLOW_BORDER_WIDTH; 1474 1475 next_column = 1; 1476 1477 for (i = 0; i < reflow->count; i++) { 1478 gint unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i); 1479 if (next_column < reflow->column_count && i == reflow->columns[next_column]) { 1480 running_height = E_REFLOW_BORDER_WIDTH; 1481 running_width += reflow->column_width + E_REFLOW_FULL_GUTTER; 1482 next_column++; 1483 } 1484 1485 if (unsorted >= 0 && reflow->items[unsorted]) { 1486 e_canvas_item_move_absolute ( 1487 GNOME_CANVAS_ITEM (reflow->items[unsorted]), 1488 (gdouble) running_width, 1489 (gdouble) running_height); 1490 running_height += reflow->heights[unsorted] + E_REFLOW_BORDER_WIDTH; 1491 } 1492 } 1493 reflow->width = running_width + reflow->column_width + E_REFLOW_BORDER_WIDTH; 1494 if (reflow->width < reflow->minimum_width) 1495 reflow->width = reflow->minimum_width; 1496 if (old_width != reflow->width) 1497 e_canvas_item_request_parent_reflow (item); 1498 } 1499 1500 static gint 1501 e_reflow_selection_event_real (EReflow *reflow, 1502 GnomeCanvasItem *item, 1503 GdkEvent *event) 1504 { 1505 gint row; 1506 gint return_val = TRUE; 1507 switch (event->type) { 1508 case GDK_BUTTON_PRESS: 1509 switch (event->button.button) { 1510 case 1: /* Fall through. */ 1511 case 2: 1512 row = er_find_item (reflow, item); 1513 if (event->button.button == 1) { 1514 reflow->maybe_did_something = 1515 e_selection_model_maybe_do_something (reflow->selection, row, 0, event->button.state); 1516 reflow->maybe_in_drag = TRUE; 1517 } else { 1518 e_selection_model_do_something (reflow->selection, row, 0, event->button.state); 1519 } 1520 break; 1521 case 3: 1522 row = er_find_item (reflow, item); 1523 e_selection_model_right_click_down (reflow->selection, row, 0, 0); 1524 break; 1525 default: 1526 return_val = FALSE; 1527 break; 1528 } 1529 break; 1530 case GDK_BUTTON_RELEASE: 1531 if (event->button.button == 1) { 1532 if (reflow->maybe_in_drag) { 1533 reflow->maybe_in_drag = FALSE; 1534 if (!reflow->maybe_did_something) { 1535 row = er_find_item (reflow, item); 1536 e_selection_model_do_something (reflow->selection, row, 0, event->button.state); 1537 } 1538 } 1539 } 1540 break; 1541 case GDK_KEY_PRESS: 1542 return_val = e_selection_model_key_press (reflow->selection, (GdkEventKey *) event); 1543 break; 1544 default: 1545 return_val = FALSE; 1546 break; 1547 } 1548 1549 return return_val; 1550 } 1551 1552 static void 1553 e_reflow_class_init (EReflowClass *class) 1554 { 1555 GObjectClass *object_class; 1556 GnomeCanvasItemClass *item_class; 1557 1558 object_class = (GObjectClass *) class; 1559 item_class = (GnomeCanvasItemClass *) class; 1560 1561 object_class->set_property = e_reflow_set_property; 1562 object_class->get_property = e_reflow_get_property; 1563 object_class->dispose = e_reflow_dispose; 1564 1565 /* GnomeCanvasItem method overrides */ 1566 item_class->event = e_reflow_event; 1567 item_class->realize = e_reflow_realize; 1568 item_class->unrealize = e_reflow_unrealize; 1569 item_class->draw = e_reflow_draw; 1570 item_class->update = e_reflow_update; 1571 item_class->point = e_reflow_point; 1572 1573 class->selection_event = e_reflow_selection_event_real; 1574 class->column_width_changed = NULL; 1575 1576 g_object_class_install_property ( 1577 object_class, 1578 PROP_MINIMUM_WIDTH, 1579 g_param_spec_double ( 1580 "minimum_width", 1581 "Minimum width", 1582 "Minimum Width", 1583 0.0, G_MAXDOUBLE, 0.0, 1584 G_PARAM_READWRITE)); 1585 1586 g_object_class_install_property ( 1587 object_class, 1588 PROP_WIDTH, 1589 g_param_spec_double ( 1590 "width", 1591 "Width", 1592 "Width", 1593 0.0, G_MAXDOUBLE, 0.0, 1594 G_PARAM_READABLE)); 1595 1596 g_object_class_install_property ( 1597 object_class, 1598 PROP_HEIGHT, 1599 g_param_spec_double ( 1600 "height", 1601 "Height", 1602 "Height", 1603 0.0, G_MAXDOUBLE, 0.0, 1604 G_PARAM_READWRITE)); 1605 1606 g_object_class_install_property ( 1607 object_class, 1608 PROP_EMPTY_MESSAGE, 1609 g_param_spec_string ( 1610 "empty_message", 1611 "Empty message", 1612 "Empty message", 1613 NULL, 1614 G_PARAM_READWRITE)); 1615 1616 g_object_class_install_property ( 1617 object_class, 1618 PROP_MODEL, 1619 g_param_spec_object ( 1620 "model", 1621 "Reflow model", 1622 "Reflow model", 1623 E_TYPE_REFLOW_MODEL, 1624 G_PARAM_READWRITE)); 1625 1626 g_object_class_install_property ( 1627 object_class, 1628 PROP_COLUMN_WIDTH, 1629 g_param_spec_double ( 1630 "column_width", 1631 "Column width", 1632 "Column width", 1633 0.0, G_MAXDOUBLE, 150.0, 1634 G_PARAM_READWRITE)); 1635 1636 signals[SELECTION_EVENT] = g_signal_new ( 1637 "selection_event", 1638 G_OBJECT_CLASS_TYPE (object_class), 1639 G_SIGNAL_RUN_LAST, 1640 G_STRUCT_OFFSET (EReflowClass, selection_event), 1641 NULL, NULL, 1642 e_marshal_INT__OBJECT_BOXED, 1643 G_TYPE_INT, 2, 1644 G_TYPE_OBJECT, 1645 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); 1646 1647 signals[COLUMN_WIDTH_CHANGED] = g_signal_new ( 1648 "column_width_changed", 1649 G_OBJECT_CLASS_TYPE (object_class), 1650 G_SIGNAL_RUN_LAST, 1651 G_STRUCT_OFFSET (EReflowClass, column_width_changed), 1652 NULL, NULL, 1653 g_cclosure_marshal_VOID__DOUBLE, 1654 G_TYPE_NONE, 1, 1655 G_TYPE_DOUBLE); 1656 } 1657 1658 static void 1659 e_reflow_init (EReflow *reflow) 1660 { 1661 reflow->model = NULL; 1662 reflow->items = NULL; 1663 reflow->heights = NULL; 1664 reflow->count = 0; 1665 1666 reflow->columns = NULL; 1667 reflow->column_count = 0; 1668 1669 reflow->empty_text = NULL; 1670 reflow->empty_message = NULL; 1671 1672 reflow->minimum_width = 10; 1673 reflow->width = 10; 1674 reflow->height = 10; 1675 1676 reflow->column_width = 150; 1677 1678 reflow->column_drag = FALSE; 1679 1680 reflow->need_height_update = FALSE; 1681 reflow->need_column_resize = FALSE; 1682 reflow->need_reflow_columns = FALSE; 1683 1684 reflow->maybe_did_something = FALSE; 1685 reflow->maybe_in_drag = FALSE; 1686 1687 reflow->default_cursor_shown = TRUE; 1688 reflow->arrow_cursor = NULL; 1689 reflow->default_cursor = NULL; 1690 1691 reflow->cursor_row = -1; 1692 1693 reflow->incarnate_idle_id = 0; 1694 reflow->do_adjustment_idle_id = 0; 1695 reflow->set_scroll_adjustments_id = 0; 1696 1697 reflow->selection = E_SELECTION_MODEL (e_selection_model_simple_new ()); 1698 reflow->sorter = e_sorter_array_new (er_create_cmp_cache, er_compare, reflow); 1699 1700 g_object_set ( 1701 reflow->selection, 1702 "sorter", reflow->sorter, 1703 NULL); 1704 1705 reflow->selection_changed_id = g_signal_connect ( 1706 reflow->selection, "selection_changed", 1707 G_CALLBACK (selection_changed), reflow); 1708 1709 reflow->selection_row_changed_id = g_signal_connect ( 1710 reflow->selection, "selection_row_changed", 1711 G_CALLBACK (selection_row_changed), reflow); 1712 1713 reflow->cursor_changed_id = g_signal_connect ( 1714 reflow->selection, "cursor_changed", 1715 G_CALLBACK (cursor_changed), reflow); 1716 1717 e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (reflow), e_reflow_reflow); 1718 }