evolution-3.6.4/widgets/table/e-table-header.c

No issues found

   1 /*
   2  * This program is free software; you can redistribute it and/or
   3  * modify it under the terms of the GNU Lesser General Public
   4  * License as published by the Free Software Foundation; either
   5  * version 2 of the License, or (at your option) version 3.
   6  *
   7  * This program is distributed in the hope that it will be useful,
   8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10  * Lesser General Public License for more details.
  11  *
  12  * You should have received a copy of the GNU Lesser General Public
  13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14  *
  15  *
  16  * Authors:
  17  *		Chris Lahey <clahey@ximian.com>
  18  *		Miguel de Icaza <miguel@ximian.com>
  19  *
  20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  21  *
  22  */
  23 
  24 #ifdef HAVE_CONFIG_H
  25 #include <config.h>
  26 #endif
  27 
  28 #include <string.h>
  29 
  30 #include <gtk/gtk.h>
  31 
  32 #include "e-util/e-util.h"
  33 
  34 #include "e-table-defines.h"
  35 #include "e-table-header.h"
  36 
  37 enum {
  38 	PROP_0,
  39 	PROP_SORT_INFO,
  40 	PROP_WIDTH,
  41 	PROP_WIDTH_EXTRAS
  42 };
  43 
  44 enum {
  45 	STRUCTURE_CHANGE,
  46 	DIMENSION_CHANGE,
  47 	EXPANSION_CHANGE,
  48 	REQUEST_WIDTH,
  49 	LAST_SIGNAL
  50 };
  51 
  52 static void eth_set_size (ETableHeader *eth, gint idx, gint size);
  53 static void eth_calc_widths (ETableHeader *eth);
  54 
  55 static guint eth_signals[LAST_SIGNAL] = { 0, };
  56 
  57 G_DEFINE_TYPE (ETableHeader, e_table_header, G_TYPE_OBJECT)
  58 
  59 struct two_ints {
  60 	gint column;
  61 	gint width;
  62 };
  63 
  64 static void
  65 eth_set_width (ETableHeader *eth,
  66                gint width)
  67 {
  68 	eth->width = width;
  69 }
  70 
  71 static void
  72 dequeue (ETableHeader *eth,
  73          gint *column,
  74          gint *width)
  75 {
  76 	GSList *head;
  77 	struct two_ints *store;
  78 	head = eth->change_queue;
  79 	eth->change_queue = eth->change_queue->next;
  80 	if (!eth->change_queue)
  81 		eth->change_tail = NULL;
  82 	store = head->data;
  83 	g_slist_free_1 (head);
  84 	if (column)
  85 		*column = store->column;
  86 	if (width)
  87 		*width = store->width;
  88 	g_free (store);
  89 }
  90 
  91 static gboolean
  92 dequeue_idle (ETableHeader *eth)
  93 {
  94 	gint column, width;
  95 
  96 	dequeue (eth, &column, &width);
  97 	while (eth->change_queue && ((struct two_ints *)
  98 		eth->change_queue->data)->column == column)
  99 		dequeue (eth, &column, &width);
 100 
 101 	if (column == -1)
 102 		eth_set_width (eth, width);
 103 	else if (column < eth->col_count)
 104 		eth_set_size (eth, column, width);
 105 	if (eth->change_queue)
 106 		return TRUE;
 107 	else {
 108 		eth_calc_widths (eth);
 109 		eth->idle = 0;
 110 		return FALSE;
 111 	}
 112 }
 113 
 114 static void
 115 enqueue (ETableHeader *eth,
 116          gint column,
 117          gint width)
 118 {
 119 	struct two_ints *store;
 120 	store = g_new (struct two_ints, 1);
 121 	store->column = column;
 122 	store->width = width;
 123 
 124 	eth->change_tail = g_slist_last (g_slist_append (eth->change_tail, store));
 125 	if (!eth->change_queue)
 126 		eth->change_queue = eth->change_tail;
 127 
 128 	if (!eth->idle) {
 129 		eth->idle = g_idle_add_full (
 130 			G_PRIORITY_LOW, (GSourceFunc)
 131 			dequeue_idle, eth, NULL);
 132 	}
 133 }
 134 
 135 void
 136 e_table_header_set_size (ETableHeader *eth,
 137                          gint idx,
 138                          gint size)
 139 {
 140 	g_return_if_fail (eth != NULL);
 141 	g_return_if_fail (E_IS_TABLE_HEADER (eth));
 142 
 143 	enqueue (eth, idx, size);
 144 }
 145 
 146 static void
 147 eth_do_remove (ETableHeader *eth,
 148                gint idx,
 149                gboolean do_unref)
 150 {
 151 	if (do_unref)
 152 		g_object_unref (eth->columns[idx]);
 153 
 154 	memmove (
 155 		&eth->columns[idx], &eth->columns[idx + 1],
 156 		sizeof (ETableCol *) * (eth->col_count - idx - 1));
 157 	eth->col_count--;
 158 }
 159 
 160 static void
 161 eth_finalize (GObject *object)
 162 {
 163 	ETableHeader *eth = E_TABLE_HEADER (object);
 164 	const gint cols = eth->col_count;
 165 	gint i;
 166 
 167 	if (eth->sort_info) {
 168 		if (eth->sort_info_group_change_id)
 169 			g_signal_handler_disconnect (
 170 				eth->sort_info,
 171 				eth->sort_info_group_change_id);
 172 		g_object_unref (eth->sort_info);
 173 		eth->sort_info = NULL;
 174 	}
 175 
 176 	if (eth->idle)
 177 		g_source_remove (eth->idle);
 178 	eth->idle = 0;
 179 
 180 	if (eth->change_queue) {
 181 		g_slist_foreach (eth->change_queue, (GFunc) g_free, NULL);
 182 		g_slist_free (eth->change_queue);
 183 		eth->change_queue = NULL;
 184 	}
 185 
 186 	/*
 187 	 * Destroy columns
 188 	 */
 189 	for (i = cols - 1; i >= 0; i--) {
 190 		eth_do_remove (eth, i, TRUE);
 191 	}
 192 	g_free (eth->columns);
 193 
 194 	eth->col_count = 0;
 195 	eth->columns = NULL;
 196 
 197 	/* Chain up to parent's finalize() method. */
 198 	G_OBJECT_CLASS (e_table_header_parent_class)->finalize (object);
 199 }
 200 
 201 static void
 202 eth_group_info_changed (ETableSortInfo *info,
 203                         ETableHeader *eth)
 204 {
 205 	enqueue (eth, -1, eth->nominal_width);
 206 }
 207 
 208 static void
 209 eth_set_property (GObject *object,
 210                   guint property_id,
 211                   const GValue *val,
 212                   GParamSpec *pspec)
 213 {
 214 	ETableHeader *eth = E_TABLE_HEADER (object);
 215 
 216 	switch (property_id) {
 217 	case PROP_WIDTH:
 218 		eth->nominal_width = g_value_get_double (val);
 219 		enqueue (eth, -1, eth->nominal_width);
 220 		break;
 221 	case PROP_WIDTH_EXTRAS:
 222 		eth->width_extras = g_value_get_double (val);
 223 		enqueue (eth, -1, eth->nominal_width);
 224 		break;
 225 	case PROP_SORT_INFO:
 226 		if (eth->sort_info) {
 227 			if (eth->sort_info_group_change_id)
 228 				g_signal_handler_disconnect (
 229 					eth->sort_info,
 230 					eth->sort_info_group_change_id);
 231 			g_object_unref (eth->sort_info);
 232 		}
 233 		eth->sort_info = E_TABLE_SORT_INFO (g_value_get_object (val));
 234 		if (eth->sort_info) {
 235 			g_object_ref (eth->sort_info);
 236 			eth->sort_info_group_change_id = g_signal_connect (
 237 				eth->sort_info, "group_info_changed",
 238 				G_CALLBACK (eth_group_info_changed), eth);
 239 		}
 240 		enqueue (eth, -1, eth->nominal_width);
 241 		break;
 242 	default:
 243 		break;
 244 	}
 245 }
 246 
 247 static void
 248 eth_get_property (GObject *object,
 249                   guint property_id,
 250                   GValue *val,
 251                   GParamSpec *pspec)
 252 {
 253 	ETableHeader *eth = E_TABLE_HEADER (object);
 254 
 255 	switch (property_id) {
 256 	case PROP_SORT_INFO:
 257 		g_value_set_object (val, eth->sort_info);
 258 		break;
 259 	case PROP_WIDTH:
 260 		g_value_set_double (val, eth->nominal_width);
 261 		break;
 262 	case PROP_WIDTH_EXTRAS:
 263 		g_value_set_double (val, eth->width_extras);
 264 		break;
 265 	default:
 266 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 267 		break;
 268 	}
 269 }
 270 
 271 static void
 272 e_table_header_class_init (ETableHeaderClass *class)
 273 {
 274 	GObjectClass *object_class = G_OBJECT_CLASS (class);
 275 
 276 	object_class->finalize = eth_finalize;
 277 	object_class->set_property = eth_set_property;
 278 	object_class->get_property = eth_get_property;
 279 
 280 	g_object_class_install_property (
 281 		object_class,
 282 		PROP_WIDTH,
 283 		g_param_spec_double (
 284 			"width", "Width", "Width",
 285 			0.0, G_MAXDOUBLE, 0.0,
 286 			G_PARAM_READWRITE));
 287 
 288 	g_object_class_install_property (
 289 		object_class,
 290 		PROP_WIDTH_EXTRAS,
 291 		g_param_spec_double (
 292 			"width_extras",
 293 			"Width of Extras",
 294 			"Width of Extras",
 295 			0.0, G_MAXDOUBLE, 0.0,
 296 			G_PARAM_READWRITE));
 297 
 298 	g_object_class_install_property (
 299 		object_class,
 300 		PROP_SORT_INFO,
 301 		g_param_spec_object (
 302 			"sort_info",
 303 			"Sort Info",
 304 			"Sort Info",
 305 			E_TYPE_TABLE_SORT_INFO,
 306 			G_PARAM_READWRITE));
 307 
 308 	eth_signals[STRUCTURE_CHANGE] = g_signal_new (
 309 		"structure_change",
 310 		G_TYPE_FROM_CLASS (object_class),
 311 		G_SIGNAL_RUN_LAST,
 312 		G_STRUCT_OFFSET (ETableHeaderClass, structure_change),
 313 		(GSignalAccumulator) NULL, NULL,
 314 		g_cclosure_marshal_VOID__VOID,
 315 		G_TYPE_NONE, 0);
 316 
 317 	eth_signals[DIMENSION_CHANGE] = g_signal_new (
 318 		"dimension_change",
 319 		G_TYPE_FROM_CLASS (object_class),
 320 		G_SIGNAL_RUN_LAST,
 321 		G_STRUCT_OFFSET (ETableHeaderClass, dimension_change),
 322 		(GSignalAccumulator) NULL, NULL,
 323 		g_cclosure_marshal_VOID__INT,
 324 		G_TYPE_NONE, 1,
 325 		G_TYPE_INT);
 326 
 327 	eth_signals[EXPANSION_CHANGE] = g_signal_new (
 328 		"expansion_change",
 329 		G_TYPE_FROM_CLASS (object_class),
 330 		G_SIGNAL_RUN_LAST,
 331 		G_STRUCT_OFFSET (ETableHeaderClass, expansion_change),
 332 		(GSignalAccumulator) NULL, NULL,
 333 		g_cclosure_marshal_VOID__VOID,
 334 		G_TYPE_NONE, 0);
 335 
 336 	eth_signals[REQUEST_WIDTH] = g_signal_new (
 337 		"request_width",
 338 		G_TYPE_FROM_CLASS (object_class),
 339 		G_SIGNAL_RUN_LAST,
 340 		G_STRUCT_OFFSET (ETableHeaderClass, request_width),
 341 		(GSignalAccumulator) NULL, NULL,
 342 		e_marshal_INT__INT,
 343 		G_TYPE_INT, 1,
 344 		G_TYPE_INT);
 345 
 346 	class->structure_change = NULL;
 347 	class->dimension_change = NULL;
 348 	class->expansion_change = NULL;
 349 	class->request_width = NULL;
 350 }
 351 
 352 static void
 353 e_table_header_init (ETableHeader *eth)
 354 {
 355 	eth->col_count                 = 0;
 356 	eth->width                     = 0;
 357 
 358 	eth->sort_info                 = NULL;
 359 	eth->sort_info_group_change_id = 0;
 360 
 361 	eth->columns                   = NULL;
 362 
 363 	eth->change_queue              = NULL;
 364 	eth->change_tail               = NULL;
 365 
 366 	eth->width_extras              = 0;
 367 }
 368 
 369 /**
 370  * e_table_header_new:
 371  *
 372  * Returns: A new @ETableHeader object.
 373  */
 374 ETableHeader *
 375 e_table_header_new (void)
 376 {
 377 
 378 	return g_object_new (E_TYPE_TABLE_HEADER, NULL);
 379 }
 380 
 381 static void
 382 eth_update_offsets (ETableHeader *eth)
 383 {
 384 	gint i;
 385 	gint x = 0;
 386 
 387 	for (i = 0; i < eth->col_count; i++) {
 388 		ETableCol *etc = eth->columns[i];
 389 
 390 		etc->x = x;
 391 		x += etc->width;
 392 	}
 393 }
 394 
 395 static void
 396 eth_do_insert (ETableHeader *eth,
 397                gint pos,
 398                ETableCol *val)
 399 {
 400 	memmove (
 401 		&eth->columns[pos + 1], &eth->columns[pos],
 402 		sizeof (ETableCol *) * (eth->col_count - pos));
 403 	eth->columns[pos] = val;
 404 	eth->col_count++;
 405 }
 406 
 407 /**
 408  * e_table_header_add_column:
 409  * @eth: the table header to add the column to.
 410  * @tc: the ETableCol definition
 411  * @pos: position where the ETableCol will go.
 412  *
 413  * This function adds the @tc ETableCol definition into the @eth ETableHeader
 414  * at position @pos.  This is the way you add new ETableCols to the
 415  * ETableHeader.  The header will assume ownership of the @tc; you should not
 416  * unref it after you add it.
 417  *
 418  * This function will emit the "structure_change" signal on the @eth object.
 419  * The ETableCol is assumed
 420  */
 421 void
 422 e_table_header_add_column (ETableHeader *eth,
 423                            ETableCol *tc,
 424                            gint pos)
 425 {
 426 	g_return_if_fail (eth != NULL);
 427 	g_return_if_fail (E_IS_TABLE_HEADER (eth));
 428 	g_return_if_fail (tc != NULL);
 429 	g_return_if_fail (E_IS_TABLE_COL (tc));
 430 	g_return_if_fail (pos >= -1 && pos <= eth->col_count);
 431 
 432 	if (pos == -1)
 433 		pos = eth->col_count;
 434 	eth->columns = g_realloc (
 435 		eth->columns, sizeof (ETableCol *) * (eth->col_count + 1));
 436 
 437 	/*
 438 	 * We are the primary owners of the column
 439 	 */
 440 	g_object_ref (tc);
 441 
 442 	eth_do_insert (eth, pos, tc);
 443 
 444 	enqueue (eth, -1, eth->nominal_width);
 445 	g_signal_emit (eth, eth_signals[STRUCTURE_CHANGE], 0);
 446 }
 447 
 448 /**
 449  * e_table_header_get_column:
 450  * @eth: the ETableHeader to query
 451  * @column: the column inside the @eth.
 452  *
 453  * Returns: The ETableCol at @column in the @eth object
 454  */
 455 ETableCol *
 456 e_table_header_get_column (ETableHeader *eth,
 457                            gint column)
 458 {
 459 	g_return_val_if_fail (eth != NULL, NULL);
 460 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), NULL);
 461 
 462 	if (column < 0)
 463 		return NULL;
 464 
 465 	if (column >= eth->col_count)
 466 		return NULL;
 467 
 468 	return eth->columns[column];
 469 }
 470 
 471 /**
 472  * e_table_header_get_column_by_col_id:
 473  * @eth: the ETableHeader to query
 474  * @col_id: the col_id to search for.
 475  *
 476  * Returns: The ETableCol with col_idx = @col_idx in the @eth object
 477  */
 478 ETableCol *
 479 e_table_header_get_column_by_col_idx (ETableHeader *eth,
 480                                       gint col_idx)
 481 {
 482 	gint i;
 483 	g_return_val_if_fail (eth != NULL, NULL);
 484 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), NULL);
 485 
 486 	for (i = 0; i < eth->col_count; i++) {
 487 		if (eth->columns[i]->col_idx == col_idx) {
 488 			return eth->columns[i];
 489 		}
 490 	}
 491 
 492 	return NULL;
 493 }
 494 
 495 /**
 496  * e_table_header_count:
 497  * @eth: the ETableHeader to query
 498  *
 499  * Returns: the number of columns in this ETableHeader.
 500  */
 501 gint
 502 e_table_header_count (ETableHeader *eth)
 503 {
 504 	g_return_val_if_fail (eth != NULL, 0);
 505 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), 0);
 506 
 507 	return eth->col_count;
 508 }
 509 
 510 /**
 511  * e_table_header_index:
 512  * @eth: the ETableHeader to query
 513  * @col: the column to fetch.
 514  *
 515  * ETableHeaders contain the visual list of columns that the user will
 516  * view.  The visible columns will typically map to different columns
 517  * in the ETableModel (because the user reordered the data for
 518  * example).
 519  *
 520  * Returns: the column in the model that the @col column
 521  * in the ETableHeader points to.  */
 522 gint
 523 e_table_header_index (ETableHeader *eth,
 524                       gint col)
 525 {
 526 	g_return_val_if_fail (eth != NULL, -1);
 527 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), -1);
 528 	g_return_val_if_fail (col >= 0 && col < eth->col_count, -1);
 529 
 530 	return eth->columns[col]->col_idx;
 531 }
 532 
 533 /**
 534  * e_table_header_get_index_at:
 535  * @eth: the ETableHeader to query
 536  * @x_offset: a pixel count from the beginning of the ETableHeader
 537  *
 538  * This will return the ETableHeader column that would contain
 539  * the @x_offset pixel.
 540  *
 541  * Returns: the column that contains pixel @x_offset, or -1
 542  * if no column inside this ETableHeader contains that pixel.
 543  */
 544 gint
 545 e_table_header_get_index_at (ETableHeader *eth,
 546                              gint x_offset)
 547 {
 548 	gint i, total;
 549 
 550 	g_return_val_if_fail (eth != NULL, 0);
 551 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), 0);
 552 
 553 	total = 0;
 554 	for (i = 0; i < eth->col_count; i++) {
 555 		total += eth->columns[i]->width;
 556 
 557 		if (x_offset < total)
 558 			return i;
 559 	}
 560 
 561 	return -1;
 562 }
 563 
 564 /**
 565  * e_table_header_get_columns:
 566  * @eth: The ETableHeader to query
 567  *
 568  * Returns: A NULL terminated array of the ETableCols
 569  * contained in the ETableHeader @eth.  Note that every
 570  * returned ETableCol in the array has been referenced, to release
 571  * this information you need to g_free the buffer returned
 572  * and you need to g_object_unref every element returned
 573  */
 574 ETableCol **
 575 e_table_header_get_columns (ETableHeader *eth)
 576 {
 577 	ETableCol **ret;
 578 	gint i;
 579 
 580 	g_return_val_if_fail (eth != NULL, NULL);
 581 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), NULL);
 582 
 583 	ret = g_new (ETableCol *, eth->col_count + 1);
 584 	memcpy (ret, eth->columns, sizeof (ETableCol *) * eth->col_count);
 585 	ret[eth->col_count] = NULL;
 586 
 587 	for (i = 0; i < eth->col_count; i++) {
 588 		g_object_ref (ret[i]);
 589 	}
 590 
 591 	return ret;
 592 }
 593 
 594 /**
 595  * e_table_header_get_selected:
 596  * @eth: The ETableHeader to query
 597  *
 598  * Returns: The number of selected columns in the @eth object.
 599  */
 600 gint
 601 e_table_header_get_selected (ETableHeader *eth)
 602 {
 603 	gint i;
 604 	gint selected = 0;
 605 
 606 	g_return_val_if_fail (eth != NULL, 0);
 607 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), 0);
 608 
 609 	for (i = 0; i < eth->col_count; i++) {
 610 		if (eth->columns[i]->selected)
 611 			selected++;
 612 	}
 613 
 614 	return selected;
 615 }
 616 
 617 /**
 618  * e_table_header_total_width:
 619  * @eth: The ETableHeader to query
 620  *
 621  * Returns: the number of pixels used by the @eth object
 622  * when rendered on screen
 623  */
 624 gint
 625 e_table_header_total_width (ETableHeader *eth)
 626 {
 627 	gint total, i;
 628 
 629 	g_return_val_if_fail (eth != NULL, 0);
 630 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), 0);
 631 
 632 	total = 0;
 633 	for (i = 0; i < eth->col_count; i++)
 634 		total += eth->columns[i]->width;
 635 
 636 	return total;
 637 }
 638 
 639 /**
 640  * e_table_header_min_width:
 641  * @eth: The ETableHeader to query
 642  *
 643  * Returns: the minimum number of pixels required by the @eth object.
 644  **/
 645 gint
 646 e_table_header_min_width (ETableHeader *eth)
 647 {
 648 	gint total, i;
 649 
 650 	g_return_val_if_fail (eth != NULL, 0);
 651 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), 0);
 652 
 653 	total = 0;
 654 	for (i = 0; i < eth->col_count; i++)
 655 		total += eth->columns[i]->min_width;
 656 
 657 	return total;
 658 }
 659 
 660 /**
 661  * e_table_header_move:
 662  * @eth: The ETableHeader to operate on.
 663  * @source_index: the source column to move.
 664  * @target_index: the target location for the column
 665  *
 666  * This function moves the column @source_index to @target_index
 667  * inside the @eth ETableHeader.  The signals "dimension_change"
 668  * and "structure_change" will be emmited
 669  */
 670 void
 671 e_table_header_move (ETableHeader *eth,
 672                      gint source_index,
 673                      gint target_index)
 674 {
 675 	ETableCol *old;
 676 
 677 	g_return_if_fail (eth != NULL);
 678 	g_return_if_fail (E_IS_TABLE_HEADER (eth));
 679 	g_return_if_fail (source_index >= 0);
 680 	g_return_if_fail (target_index >= 0);
 681 	g_return_if_fail (source_index < eth->col_count);
 682 
 683 	/* Can be moved beyond the last item. */
 684 	g_return_if_fail (target_index < eth->col_count + 1);
 685 
 686 	if (source_index < target_index)
 687 		target_index--;
 688 
 689 	old = eth->columns[source_index];
 690 	eth_do_remove (eth, source_index, FALSE);
 691 	eth_do_insert (eth, target_index, old);
 692 	eth_update_offsets (eth);
 693 
 694 	g_signal_emit (eth, eth_signals[DIMENSION_CHANGE], 0, eth->width);
 695 	g_signal_emit (eth, eth_signals[STRUCTURE_CHANGE], 0);
 696 }
 697 
 698 /**
 699  * e_table_header_remove:
 700  * @eth: The ETableHeader to operate on.
 701  * @idx: the index to the column to be removed.
 702  *
 703  * Removes the column at @idx position in the ETableHeader @eth.
 704  * This emmits the "structure_change" signal on the @eth object.
 705  */
 706 void
 707 e_table_header_remove (ETableHeader *eth,
 708                        gint idx)
 709 {
 710 	g_return_if_fail (eth != NULL);
 711 	g_return_if_fail (E_IS_TABLE_HEADER (eth));
 712 	g_return_if_fail (idx >= 0);
 713 	g_return_if_fail (idx < eth->col_count);
 714 
 715 	eth_do_remove (eth, idx, TRUE);
 716 	enqueue (eth, -1, eth->nominal_width);
 717 	g_signal_emit (eth, eth_signals[STRUCTURE_CHANGE], 0);
 718 }
 719 
 720 /*
 721  * FIXME: deprecated?
 722  */
 723 void
 724 e_table_header_set_selection (ETableHeader *eth,
 725                               gboolean allow_selection)
 726 {
 727 	g_return_if_fail (eth != NULL);
 728 	g_return_if_fail (E_IS_TABLE_HEADER (eth));
 729 }
 730 
 731 static void
 732 eth_set_size (ETableHeader *eth,
 733               gint idx,
 734               gint size)
 735 {
 736 	gdouble expansion;
 737 	gdouble old_expansion;
 738 	gint min_width;
 739 	gint left_width;
 740 	gint total_extra;
 741 	gint expandable_count;
 742 	gint usable_width;
 743 	gint i;
 744 	g_return_if_fail (eth != NULL);
 745 	g_return_if_fail (E_IS_TABLE_HEADER (eth));
 746 	g_return_if_fail (idx >= 0);
 747 	g_return_if_fail (idx < eth->col_count);
 748 
 749 	/* If this column is not resizable, don't do anything. */
 750 	if (!eth->columns[idx]->resizable)
 751 		return;
 752 
 753 	expansion = 0;
 754 	min_width = 0;
 755 	left_width = 0;
 756 	expandable_count = -1;
 757 
 758 	/* Calculate usable area. */
 759 	for (i = 0; i < idx; i++) {
 760 		left_width += eth->columns[i]->width;
 761 	}
 762 	/* - 1 to account for the last pixel border. */
 763 	usable_width = eth->width - left_width - 1;
 764 
 765 	if (eth->sort_info)
 766 		usable_width -= e_table_sort_info_grouping_get_count (
 767 			eth->sort_info) * GROUP_INDENT;
 768 
 769 	/* Calculate minimum_width of stuff on the right as well as
 770 	 * total usable expansion on the right.
 771 	 */
 772 	for (; i < eth->col_count; i++) {
 773 		min_width += eth->columns[i]->min_width + eth->width_extras;
 774 		if (eth->columns[i]->resizable) {
 775 			expansion += eth->columns[i]->expansion;
 776 			expandable_count++;
 777 		}
 778 	}
 779 	/* If there's no room for anything, don't change. */
 780 	if (expansion == 0)
 781 		return;
 782 
 783 	/* (1) If none of the columns to the right are expandable, use
 784 	 * all the expansion space in this column.
 785 	 */
 786 	if (expandable_count == 0) {
 787 		eth->columns[idx]->expansion = expansion;
 788 		for (i = idx + 1; i < eth->col_count; i++) {
 789 			eth->columns[i]->expansion = 0;
 790 		}
 791 
 792 		g_signal_emit (eth, eth_signals[EXPANSION_CHANGE], 0);
 793 		return;
 794 	}
 795 
 796 	total_extra = usable_width - min_width;
 797 	/* If there's no extra space, set all expansions to 0. */
 798 	if (total_extra <= 0) {
 799 		for (i = idx; i < eth->col_count; i++) {
 800 			eth->columns[i]->expansion = 0;
 801 		}
 802 		g_signal_emit (eth, eth_signals[EXPANSION_CHANGE], 0);
 803 		return;
 804 	}
 805 
 806 	/* If you try to resize smaller than the minimum width, it
 807 	 * uses the minimum. */
 808 	if (size < eth->columns[idx]->min_width + eth->width_extras)
 809 		size = eth->columns[idx]->min_width + eth->width_extras;
 810 
 811 	/* If all the extra space will be used up in this column, use
 812 	 * all the expansion and set all others to 0.
 813 	 */
 814 	if (size >= total_extra + eth->columns[idx]->min_width + eth->width_extras) {
 815 		eth->columns[idx]->expansion = expansion;
 816 		for (i = idx + 1; i < eth->col_count; i++) {
 817 			eth->columns[i]->expansion = 0;
 818 		}
 819 		g_signal_emit (eth, eth_signals[EXPANSION_CHANGE], 0);
 820 		return;
 821 	}
 822 
 823 	/* The old_expansion used by columns to the right. */
 824 	old_expansion = expansion;
 825 	old_expansion -= eth->columns[idx]->expansion;
 826 	/* Set the new expansion so that it will generate the desired size. */
 827 	eth->columns[idx]->expansion =
 828 		expansion * (((gdouble)(size - (eth->columns[idx]->min_width +
 829 		eth->width_extras))) / ((gdouble) total_extra));
 830 	/* The expansion left for the columns on the right. */
 831 	expansion -= eth->columns[idx]->expansion;
 832 
 833 	/* (2) If the old columns to the right didn't have any
 834 	 * expansion before, expand them evenly.  old_expansion > 0 by
 835 	 * expansion = SUM(i=idx to col_count -1,
 836 	 * columns[i]->min_width) - columns[idx]->min_width) =
 837 	 * SUM(non-negatives).
 838 	 */
 839 	if (old_expansion == 0) {
 840 		for (i = idx + 1; i < eth->col_count; i++) {
 841 			if (eth->columns[idx]->resizable) {
 842 				/* expandable_count != 0 by (1) */
 843 				eth->columns[i]->expansion = expansion / expandable_count;
 844 			}
 845 		}
 846 		g_signal_emit (eth, eth_signals[EXPANSION_CHANGE], 0);
 847 		return;
 848 	}
 849 
 850 	for (i = idx + 1; i < eth->col_count; i++) {
 851 		if (eth->columns[idx]->resizable) {
 852 			/* old_expansion != 0 by (2) */
 853 			eth->columns[i]->expansion *= expansion / old_expansion;
 854 		}
 855 	}
 856 	g_signal_emit (eth, eth_signals[EXPANSION_CHANGE], 0);
 857 }
 858 
 859 /**
 860  * e_table_header_col_diff:
 861  * @eth: the ETableHeader to query.
 862  * @start_col: the starting column
 863  * @end_col: the ending column.
 864  *
 865  * Computes the number of pixels between the columns @start_col and
 866  * @end_col.
 867  *
 868  * Returns: the number of pixels between @start_col and @end_col on the
 869  * @eth ETableHeader object
 870  */
 871 gint
 872 e_table_header_col_diff (ETableHeader *eth,
 873                          gint start_col,
 874                          gint end_col)
 875 {
 876 	gint total, col;
 877 
 878 	g_return_val_if_fail (eth != NULL, 0);
 879 	g_return_val_if_fail (E_IS_TABLE_HEADER (eth), 0);
 880 
 881 	if (start_col < 0)
 882 		start_col = 0;
 883 	if (end_col > eth->col_count)
 884 		end_col = eth->col_count;
 885 
 886 	total = 0;
 887 	for (col = start_col; col < end_col; col++) {
 888 
 889 		total += eth->columns[col]->width;
 890 	}
 891 
 892 	return total;
 893 }
 894 
 895 static void
 896 eth_calc_widths (ETableHeader *eth)
 897 {
 898 	gint i;
 899 	gint extra;
 900 	gdouble expansion;
 901 	gint last_position = 0;
 902 	gdouble next_position = 0;
 903 	gint last_resizable = -1;
 904 	gint *widths;
 905 	gboolean changed;
 906 
 907 	widths = g_new (int, eth->col_count);
 908 
 909 	/* - 1 to account for the last pixel border. */
 910 	extra = eth->width - 1;
 911 	expansion = 0;
 912 	for (i = 0; i < eth->col_count; i++) {
 913 		extra -= eth->columns[i]->min_width + eth->width_extras;
 914 		if (eth->columns[i]->resizable && eth->columns[i]->expansion > 0)
 915 			last_resizable = i;
 916 		expansion += eth->columns[i]->resizable ? eth->columns[i]->expansion : 0;
 917 		widths[i] = eth->columns[i]->min_width + eth->width_extras;
 918 	}
 919 	if (eth->sort_info)
 920 		extra -= e_table_sort_info_grouping_get_count (eth->sort_info)
 921 			* GROUP_INDENT;
 922 	if (expansion != 0 && extra > 0) {
 923 		for (i = 0; i < last_resizable; i++) {
 924 			next_position +=
 925 				extra * (eth->columns[i]->resizable ?
 926 				eth->columns[i]->expansion : 0) / expansion;
 927 			widths[i] += next_position - last_position;
 928 			last_position = next_position;
 929 		}
 930 		widths[i] += extra - last_position;
 931 	}
 932 
 933 	changed = FALSE;
 934 
 935 	for (i = 0; i < eth->col_count; i++) {
 936 		if (eth->columns[i]->width != widths[i]) {
 937 			changed = TRUE;
 938 			eth->columns[i]->width = widths[i];
 939 		}
 940 	}
 941 	g_free (widths);
 942 	if (changed)
 943 		g_signal_emit (eth, eth_signals[DIMENSION_CHANGE], 0, eth->width);
 944 	eth_update_offsets (eth);
 945 }
 946 
 947 void
 948 e_table_header_update_horizontal (ETableHeader *eth)
 949 {
 950 	gint i;
 951 	gint cols;
 952 
 953 	cols = eth->col_count;
 954 
 955 	for (i = 0; i < cols; i++) {
 956 		gint width = 0;
 957 
 958 		g_signal_emit_by_name (
 959 			eth, "request_width", i, &width);
 960 		eth->columns[i]->min_width = width + 10;
 961 		eth->columns[i]->expansion = 1;
 962 	}
 963 	enqueue (eth, -1, eth->nominal_width);
 964 	g_signal_emit (eth, eth_signals[EXPANSION_CHANGE], 0);
 965 }
 966 
 967 gint
 968 e_table_header_prioritized_column (ETableHeader *eth)
 969 {
 970 	gint best_model_col = 0;
 971 	gint best_priority;
 972 	gint i;
 973 	gint count;
 974 
 975 	count = e_table_header_count (eth);
 976 	if (count == 0)
 977 		return -1;
 978 	best_priority = e_table_header_get_column (eth, 0)->priority;
 979 	best_model_col = e_table_header_get_column (eth, 0)->col_idx;
 980 	for (i = 1; i < count; i++) {
 981 		gint priority = e_table_header_get_column (eth, i)->priority;
 982 		if (priority > best_priority) {
 983 			best_priority = priority;
 984 			best_model_col = e_table_header_get_column (eth, i)->col_idx;
 985 		}
 986 	}
 987 	return best_model_col;
 988 }
 989 
 990 ETableCol *
 991 e_table_header_prioritized_column_selected (ETableHeader *eth,
 992                                             ETableColCheckFunc check_func,
 993                                             gpointer user_data)
 994 {
 995 	ETableCol *best_col = NULL;
 996 	gint best_priority = G_MININT;
 997 	gint i;
 998 	gint count;
 999 
1000 	count = e_table_header_count (eth);
1001 	if (count == 0)
1002 		return NULL;
1003 	for (i = 1; i < count; i++) {
1004 		ETableCol *col = e_table_header_get_column (eth, i);
1005 		if (col) {
1006 			if ((best_col == NULL || col->priority > best_priority)
1007 			   && check_func (col, user_data)) {
1008 				best_priority = col->priority;
1009 				best_col = col;
1010 			}
1011 		}
1012 	}
1013 	return best_col;
1014 }