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