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 ð->columns[idx], ð->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 ð->columns[pos + 1], ð->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 }