1 /********************************************************************\
2  * This program is free software; you can redistribute it and/or    *
3  * modify it under the terms of the GNU General Public License as   *
4  * published by the Free Software Foundation; either version 2 of   *
5  * the License, or (at your option) any later version.              *
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    *
10  * GNU General Public License for more details.                     *
11  *                                                                  *
12  * You should have received a copy of the GNU General Public License*
13  * along with this program; if not, contact:                        *
14  *                                                                  *
15  * Free Software Foundation           Voice:  +1-617-542-5942       *
16  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
17  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
18  *                                                                  *
19 \********************************************************************/
20 
21 /*
22  *  configure the cursor styles
23  */
24 
25 #include <config.h>
26 #include "gnucash-color.h"
27 #include "gnucash-item-edit.h"
28 #include "gnucash-sheet.h"
29 #include "gnucash-sheetP.h"
30 #include "gnucash-style.h"
31 #include "gnc-engine.h"     // For debugging, e.g. ENTER(), LEAVE()
32 
33 /** GLOBALS *********************************************************/
34 /* This static indicates the debugging module that this .o belongs to.  */
35 #define DEFAULT_STYLE_WIDTH 680
36 
37 
38 /** Static Globals *****************************************************/
39 
40 /* This static indicates the debugging module that this .o belongs to. */
41 static QofLogModule log_module = GNC_MOD_REGISTER;
42 
43 
44 /** Implementation *****************************************************/
45 
46 static gpointer
style_get_key(SheetBlockStyle * style)47 style_get_key (SheetBlockStyle *style)
48 {
49     static gint key;
50 
51     key = style->cursor->num_rows;
52 
53     return &key;
54 }
55 
56 static gpointer
style_create_key(SheetBlockStyle * style)57 style_create_key (SheetBlockStyle *style)
58 {
59     static gint key;
60     gpointer new_key;
61 
62     key = style->cursor->num_rows;
63     new_key = g_malloc(sizeof(key));
64     new_key = memcpy(new_key, &key, sizeof(key));
65 
66     return new_key;
67 }
68 
69 static void
cell_dimensions_construct(gpointer _cd,gpointer user_data)70 cell_dimensions_construct (gpointer _cd, gpointer user_data)
71 {
72     CellDimensions *cd = _cd;
73 
74     cd->pixel_width = -1;
75     cd->can_span_over = TRUE;
76 }
77 
78 
79 static BlockDimensions *
style_dimensions_new(SheetBlockStyle * style)80 style_dimensions_new (SheetBlockStyle *style)
81 {
82     BlockDimensions *dimensions;
83 
84     dimensions = g_new0 (BlockDimensions, 1);
85 
86     dimensions->nrows = style->nrows;
87     dimensions->ncols = style->ncols;
88 
89     dimensions->cell_dimensions = g_table_new (sizeof (CellDimensions),
90                                   cell_dimensions_construct,
91                                   NULL, NULL);
92 
93     g_table_resize (dimensions->cell_dimensions,
94                     style->nrows, style->ncols);
95 
96     return dimensions;
97 }
98 
99 static void
style_dimensions_destroy(BlockDimensions * dimensions)100 style_dimensions_destroy (BlockDimensions *dimensions)
101 {
102     if (dimensions == NULL)
103         return;
104 
105     dimensions->refcount--;
106 
107     if (dimensions->refcount == 0)
108     {
109         g_table_destroy (dimensions->cell_dimensions);
110         dimensions->cell_dimensions = NULL;
111 
112         g_free(dimensions);
113     }
114 }
115 
116 
117 static void
gnucash_style_dimensions_init(GnucashSheet * sheet,SheetBlockStyle * style)118 gnucash_style_dimensions_init (GnucashSheet *sheet, SheetBlockStyle *style)
119 {
120     BlockDimensions *dimensions;
121 
122     dimensions = g_hash_table_lookup (sheet->dimensions_hash_table,
123                                       style_get_key (style));
124 
125     if (!dimensions)
126     {
127         dimensions = style_dimensions_new (style);
128         g_hash_table_insert (sheet->dimensions_hash_table,
129                              style_create_key (style), dimensions);
130     }
131 
132     dimensions->refcount++;
133 
134     style->dimensions = dimensions;
135 }
136 
137 
138 CellDimensions *
gnucash_style_get_cell_dimensions(SheetBlockStyle * style,int row,int col)139 gnucash_style_get_cell_dimensions (SheetBlockStyle *style, int row, int col)
140 {
141     if (style == NULL)
142         return NULL;
143     if (style->dimensions == NULL)
144         return NULL;
145     if (style->dimensions->cell_dimensions == NULL)
146         return NULL;
147 
148     return g_table_index (style->dimensions->cell_dimensions, row, col);
149 }
150 
151 static int
compute_row_width(BlockDimensions * dimensions,int row,int col1,int col2)152 compute_row_width (BlockDimensions *dimensions, int row, int col1, int col2)
153 {
154     int j;
155     int width = 0;
156 
157     col1 = MAX(0, col1);
158     col2 = MIN(col2, dimensions->ncols - 1);
159 
160     for (j = col1; j <= col2; j++)
161     {
162         CellDimensions *cd;
163         cd = g_table_index (dimensions->cell_dimensions, row, j);
164 
165         if (!cd)
166             continue;
167 
168         width += cd->pixel_width;
169     }
170 
171     return width;
172 }
173 
174 
175 /* This sets the initial sizes of the cells, based on the sample_text */
176 static void
set_dimensions_pass_one(GnucashSheet * sheet,CellBlock * cursor,BlockDimensions * dimensions)177 set_dimensions_pass_one (GnucashSheet *sheet, CellBlock *cursor,
178                          BlockDimensions *dimensions)
179 {
180     CellDimensions *cd;
181     int row, col;
182     gint max_height = -1;
183     PangoLayout *layout;
184     GncItemEdit *item_edit = GNC_ITEM_EDIT(sheet->item_editor);
185 
186     /* g_return_if_fail (font != NULL); */
187 
188     for (row = 0; row < cursor->num_rows; row++)
189     {
190         for (col = 0; col < cursor->num_cols; col++)
191         {
192             int width;
193             char *text;
194             BasicCell *cell;
195 
196             cd = g_table_index (dimensions->cell_dimensions,
197                                 row, col);
198 
199             cell = gnc_cellblock_get_cell (cursor, row, col);
200             if (!cell || !cd)
201                 continue;
202 
203             text = cell->sample_text;
204             if (text)
205                 cd->can_span_over = FALSE;
206 
207             if (text)
208             {
209                 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), text);
210                 pango_layout_get_pixel_size (layout, &width, &cd->pixel_height);
211                 g_object_unref (layout);
212                 width += gnc_item_edit_get_margin (item_edit, left_right) +
213                          gnc_item_edit_get_padding_border (item_edit, left_right);
214 
215                 // This is used on new popup cells to get the default
216                 // width of text plus toggle button.
217                 if (cell && cell->is_popup)
218                     width += gnc_item_edit_get_button_width (item_edit) + 2;  // + 2 for the button margin
219 
220                 cd->pixel_height += gnc_item_edit_get_margin (item_edit, top_bottom) +
221                                     gnc_item_edit_get_padding_border (item_edit, top_bottom);
222             }
223             else
224             {
225                 width = 0;
226                 cd->pixel_height = gnc_item_edit_get_margin (item_edit, top_bottom) +
227                                    gnc_item_edit_get_padding_border (item_edit, top_bottom);
228             }
229             // add 1 to cd->pixel_height to allow for a cell border
230             max_height = MAX(max_height, cd->pixel_height + 1);
231 
232             if (cd->pixel_width > 0)
233                 continue;
234 
235             cd->pixel_width = MAX (cd->pixel_width, width);
236         }
237 
238         dimensions->height += max_height;
239     }
240 
241     for (row = 0; row < cursor->num_rows; row++)
242     {
243         for (col = 0; col < cursor->num_cols; col++)
244         {
245             cd = g_table_index (dimensions->cell_dimensions,
246                                 row, col);
247             if (!cd)
248                 continue;
249 
250             cd->pixel_height = max_height;
251         }
252     }
253 }
254 
255 
256 /* Now adjust things to make everything even. This code assumes that
257  * all cursors have the same number of columns!!! */
258 static void
set_dimensions_pass_two(GnucashSheet * sheet,int default_width)259 set_dimensions_pass_two (GnucashSheet *sheet, int default_width)
260 {
261     SheetBlockStyle *style;
262     GncItemEdit *item_edit = GNC_ITEM_EDIT(sheet->item_editor);
263     BlockDimensions *dimensions;
264     CellDimensions *cd;
265     GTable *cd_table;
266     CellBlock *cursor;
267     GList *cursors;
268     GList *node;
269 
270     int num_cols;
271     int *widths;
272     int width;
273     int row, col;
274 
275     style = gnucash_sheet_get_style_from_cursor (sheet, CURSOR_HEADER);
276     dimensions = style->dimensions;
277     cd_table = dimensions->cell_dimensions;
278     cursor = style->cursor;
279 
280     width = 0;
281     num_cols = cursor->num_cols;
282     widths = g_new (int, num_cols);
283 
284     /* find header widths */
285     for (col = 0; col < num_cols; col++)
286     {
287         cd = g_table_index (cd_table, 0, col);
288 
289         if (!cd)
290             continue;
291 
292         widths[col] = cd->pixel_width;
293         width += cd->pixel_width;
294     }
295 
296     if (width < default_width)
297         for (col = 0; col < num_cols; col++)
298         {
299             BasicCell *cell;
300 
301             cell = gnc_cellblock_get_cell (cursor, 0, col);
302 
303             if (!cell || !cell->expandable)
304                 continue;
305 
306             cd = g_table_index (cd_table, 0, col);
307 
308             if (!cd)
309                 continue;
310 
311             cd->pixel_width += (default_width - width);
312             widths[col] = cd->pixel_width;
313 
314             break;
315         }
316     else if (width > default_width && width == sheet->window_width)
317     {
318         for (col = 0; col < num_cols; col++)
319         {
320             BasicCell *cell;
321             const char *text;
322             int sample_width;
323             int old_width;
324             PangoLayout *layout;
325 
326             cell = gnc_cellblock_get_cell (cursor, 0, col);
327 
328             if (!cell || !cell->expandable)
329                 continue;
330 
331             cd = g_table_index (cd_table, 0, col);
332 
333             if (!cd)
334                 continue;
335 
336             cd->pixel_width += (default_width - width);
337 
338             text = cell->sample_text;
339             if (text)
340             {
341                 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), text);
342                 pango_layout_get_pixel_size (layout, &sample_width, NULL);
343                 g_object_unref (layout);
344                 /*sample_width = gdk_string_width (font, text);*/
345                 sample_width += gnc_item_edit_get_margin (item_edit, left_right) +
346                                 gnc_item_edit_get_padding_border (item_edit, left_right);
347             }
348             else
349                 sample_width = 0;
350 
351             cd->pixel_width = MAX (cd->pixel_width, sample_width);
352 
353             widths[col] = cd->pixel_width;
354 
355             break;
356         }
357     }
358 
359     cursors = gnc_table_layout_get_cursors (sheet->table->layout);
360 
361     /* adjust widths to be consistent */
362     for (node = cursors; node; node = node->next)
363     {
364         cursor = node->data;
365         style = gnucash_sheet_get_style_from_cursor
366                 (sheet, cursor->cursor_name);
367         dimensions = style->dimensions;
368         cd_table = dimensions->cell_dimensions;
369 
370         for (row = 0; row < cursor->num_rows; row++)
371             for (col = 0; col < num_cols; col++)
372             {
373                 cd = g_table_index (cd_table, row, col);
374 
375                 if (!cd)
376                     continue;
377 
378                 cd->pixel_width = widths[col];
379             }
380     }
381 
382     /* now expand spanning cells */
383     for (node = cursors; node; node = node->next)
384     {
385         CellDimensions *cd_span;
386 
387         cursor = node->data;
388         style = gnucash_sheet_get_style_from_cursor
389                 (sheet, cursor->cursor_name);
390         dimensions = style->dimensions;
391         cd_table = dimensions->cell_dimensions;
392 
393         for (row = 0; row < cursor->num_rows; row++)
394         {
395             cd_span = NULL;
396 
397             for (col = 0; col < num_cols; col++)
398             {
399                 BasicCell *cell;
400 
401                 cell = gnc_cellblock_get_cell (cursor,
402                                                row, col);
403                 if (!cell)
404                     continue;
405 
406                 cd = g_table_index (cd_table, row, col);
407 
408                 if (cell->span)
409                 {
410                     cd_span = cd;
411                     continue;
412                 }
413 
414                 if (!cd || !cd->can_span_over)
415                     continue;
416 
417                 if (cd_span == NULL)
418                     continue;
419 
420                 if (cell->sample_text != NULL)
421                 {
422                     cd_span = NULL;
423                     continue;
424                 }
425 
426                 if (cd->pixel_width <= 0)
427                     continue;
428 
429                 cd_span->pixel_width += cd->pixel_width;
430                 cd->pixel_width = 0;
431             }
432         }
433     }
434 
435     g_free (widths);
436 }
437 
438 gint
gnucash_style_row_width(SheetBlockStyle * style,int row)439 gnucash_style_row_width(SheetBlockStyle *style, int row)
440 {
441     BlockDimensions *dimensions;
442 
443     dimensions = style->dimensions;
444 
445     return compute_row_width(dimensions, row, 0, dimensions->ncols - 1);
446 }
447 
448 static void
compute_cell_origins_x(BlockDimensions * dimensions)449 compute_cell_origins_x (BlockDimensions *dimensions)
450 {
451     int x;
452     int i, j;
453 
454     for (i = 0; i < dimensions->nrows; i++)
455     {
456         x = 0;
457 
458         for (j = 0; j < dimensions->ncols; j++)
459         {
460             CellDimensions *cd;
461 
462             cd = g_table_index (dimensions->cell_dimensions, i, j);
463 
464             if (!cd)
465                 continue;
466 
467             cd->origin_x = x;
468             x += cd->pixel_width;
469         }
470     }
471 }
472 
473 static void
compute_cell_origins_y(BlockDimensions * dimensions)474 compute_cell_origins_y (BlockDimensions *dimensions)
475 {
476     CellDimensions *cd;
477     int y = 0;
478     int i, j;
479 
480     for (i = 0; i < dimensions->nrows; i++)
481     {
482         for (j = 0; j < dimensions->ncols; j++)
483         {
484             cd = g_table_index (dimensions->cell_dimensions, i, j);
485 
486             if (!cd)
487                 continue;
488 
489             cd->origin_y = y;
490         }
491         cd = g_table_index (dimensions->cell_dimensions, i, 0);
492 
493         if (!cd)
494             continue;
495 
496         y += cd->pixel_height;
497     }
498 }
499 
500 /* Calculate the widths and offsets */
501 static void
set_dimensions_pass_three(GnucashSheet * sheet)502 set_dimensions_pass_three (GnucashSheet *sheet)
503 {
504     GList *cursors;
505     GList *node;
506 
507     cursors = gnc_table_layout_get_cursors (sheet->table->layout);
508 
509     for (node = cursors; node; node = node->next)
510     {
511         CellBlock *cursor = node->data;
512 
513         SheetBlockStyle *style;
514         BlockDimensions *dimensions;
515 
516         style = gnucash_sheet_get_style_from_cursor
517                 (sheet, cursor->cursor_name);
518         dimensions = style->dimensions;
519 
520         dimensions->width = compute_row_width (dimensions, 0, 0,
521                                                dimensions->ncols - 1);
522 
523         compute_cell_origins_x (dimensions);
524         compute_cell_origins_y (dimensions);
525     }
526 }
527 
528 static void
styles_recompute_layout_dimensions(GnucashSheet * sheet,int default_width)529 styles_recompute_layout_dimensions (GnucashSheet *sheet, int default_width)
530 {
531     CellBlock *cursor;
532     SheetBlockStyle *style;
533     BlockDimensions *dimensions;
534     GList *cursors;
535     GList *node;
536 
537     cursors = gnc_table_layout_get_cursors (sheet->table->layout);
538 
539     for (node = cursors; node; node = node->next)
540     {
541         cursor = node->data;
542 
543         style = gnucash_sheet_get_style_from_cursor
544                 (sheet, cursor->cursor_name);
545 
546         dimensions = style->dimensions;
547 
548         dimensions->height = 0;
549         dimensions->width = default_width;
550 
551         set_dimensions_pass_one (sheet, cursor, dimensions);
552     }
553 
554     set_dimensions_pass_two (sheet, default_width);
555     set_dimensions_pass_three (sheet);
556 }
557 
558 void
gnucash_sheet_styles_set_dimensions(GnucashSheet * sheet,int default_width)559 gnucash_sheet_styles_set_dimensions (GnucashSheet *sheet, int default_width)
560 {
561     g_return_if_fail (sheet != NULL);
562     g_return_if_fail (GNUCASH_IS_SHEET (sheet));
563 
564     styles_recompute_layout_dimensions (sheet, default_width);
565 }
566 
567 gint
gnucash_style_col_is_resizable(SheetBlockStyle * style,int col)568 gnucash_style_col_is_resizable (SheetBlockStyle *style, int col)
569 {
570     if (col < 0 || col >= style->ncols)
571         return FALSE;
572 
573     return TRUE;
574 }
575 
576 void
gnucash_sheet_set_col_width(GnucashSheet * sheet,int col,int width)577 gnucash_sheet_set_col_width (GnucashSheet *sheet, int col, int width)
578 {
579     CellDimensions *cd;
580     SheetBlockStyle *style;
581     int total;
582     int diff;
583 
584     g_return_if_fail (sheet != NULL);
585     g_return_if_fail (GNUCASH_IS_SHEET(sheet));
586     g_return_if_fail (col >= 0);
587 
588     if (width < 0)
589         return;
590 
591     style = gnucash_sheet_get_style_from_cursor (sheet, CURSOR_HEADER);
592 
593     g_return_if_fail (col < style->ncols);
594 
595     cd = gnucash_style_get_cell_dimensions (style, 0, col);
596     if (!cd) return;
597 
598     /* adjust the overall width of this style */
599     diff = cd->pixel_width - width;
600     cd->pixel_width = width;
601 
602     total = MAX (sheet->window_width, sheet->width - diff);
603 
604     set_dimensions_pass_two (sheet, total);
605     set_dimensions_pass_three (sheet);
606 }
607 
608 
609 void
gnucash_sheet_styles_recompile(GnucashSheet * sheet)610 gnucash_sheet_styles_recompile(GnucashSheet *sheet)
611 {
612 }
613 
614 
615 void
gnucash_sheet_get_borders(GnucashSheet * sheet,VirtualLocation virt_loc,PhysicalCellBorders * borders)616 gnucash_sheet_get_borders (GnucashSheet *sheet, VirtualLocation virt_loc,
617                            PhysicalCellBorders *borders)
618 {
619     SheetBlockStyle *style;
620     PhysicalCellBorderLineStyle line_style;
621 
622     g_return_if_fail (sheet != NULL);
623     g_return_if_fail (GNUCASH_IS_SHEET (sheet));
624 
625     line_style = sheet->use_horizontal_lines ?
626                  CELL_BORDER_LINE_NORMAL : CELL_BORDER_LINE_NONE;
627 
628     borders->top    = line_style;
629     borders->bottom = line_style;
630 
631     line_style = sheet->use_vertical_lines ?
632                  CELL_BORDER_LINE_NORMAL : CELL_BORDER_LINE_NONE;
633 
634     borders->left  = line_style;
635     borders->right = line_style;
636 
637     style = gnucash_sheet_get_style_from_cursor (sheet, CURSOR_HEADER);
638     if (style)
639         if (virt_loc.phys_col_offset == (style->ncols - 1))
640             borders->right = CELL_BORDER_LINE_NORMAL;
641 
642     if (virt_cell_loc_equal (virt_loc.vcell_loc,
643                              sheet->table->current_cursor_loc.vcell_loc))
644     {
645         borders->top    = CELL_BORDER_LINE_NORMAL;
646         borders->bottom = CELL_BORDER_LINE_NORMAL;
647     }
648 
649     gnc_table_get_borders (sheet->table, virt_loc, borders);
650 }
651 
652 
653 static SheetBlockStyle *
gnucash_sheet_style_new(GnucashSheet * sheet,CellBlock * cursor)654 gnucash_sheet_style_new (GnucashSheet *sheet, CellBlock *cursor)
655 {
656     SheetBlockStyle *style;
657 
658     g_return_val_if_fail (sheet != NULL, NULL);
659     g_return_val_if_fail (GNUCASH_IS_SHEET (sheet), NULL);
660     g_return_val_if_fail (cursor != NULL, NULL);
661 
662     style = g_new0 (SheetBlockStyle, 1);
663 
664     style->cursor = cursor;
665 
666     style->nrows = cursor->num_rows;
667     style->ncols = cursor->num_cols;
668 
669     gnucash_style_dimensions_init (sheet, style);
670 
671     return style;
672 }
673 
674 static void
destroy_style_helper(gpointer key,gpointer value,gpointer user_data)675 destroy_style_helper (gpointer key, gpointer value, gpointer user_data)
676 {
677     char *cursor_name = key;
678     SheetBlockStyle *style = value;
679     GnucashSheet *sheet = user_data;
680 
681     gnucash_sheet_style_unref (sheet, style);
682     g_free (cursor_name);
683 }
684 
685 void
gnucash_sheet_clear_styles(GnucashSheet * sheet)686 gnucash_sheet_clear_styles (GnucashSheet *sheet)
687 {
688     g_return_if_fail (sheet != NULL);
689     g_return_if_fail (GNUCASH_IS_SHEET (sheet));
690 
691     g_hash_table_foreach (sheet->cursor_styles,
692                           destroy_style_helper, sheet);
693 }
694 
695 void
gnucash_sheet_create_styles(GnucashSheet * sheet)696 gnucash_sheet_create_styles (GnucashSheet *sheet)
697 {
698     GList *cursors;
699     GList *node;
700 
701     g_return_if_fail (sheet != NULL);
702     g_return_if_fail (GNUCASH_IS_SHEET (sheet));
703 
704     gnucash_sheet_clear_styles (sheet);
705 
706     cursors = gnc_table_layout_get_cursors (sheet->table->layout);
707 
708     for (node = cursors; node; node = node->next)
709     {
710         CellBlock *cursor = node->data;
711         SheetBlockStyle *style = gnucash_sheet_style_new (sheet, cursor);
712 
713         gnucash_sheet_style_ref (sheet, style);
714         g_hash_table_insert (sheet->cursor_styles,
715                              g_strdup (cursor->cursor_name),
716                              style);
717     }
718 }
719 
720 void
gnucash_sheet_compile_styles(GnucashSheet * sheet)721 gnucash_sheet_compile_styles (GnucashSheet *sheet)
722 {
723     g_return_if_fail (sheet != NULL);
724     g_return_if_fail (GNUCASH_IS_SHEET (sheet));
725 
726     ENTER("sheet=%p", sheet);
727 
728     gnucash_sheet_styles_set_dimensions (sheet, DEFAULT_STYLE_WIDTH);
729 
730     LEAVE(" ");
731 }
732 
733 void
gnucash_sheet_style_destroy(GnucashSheet * sheet,SheetBlockStyle * style)734 gnucash_sheet_style_destroy (GnucashSheet *sheet, SheetBlockStyle *style)
735 {
736     if (sheet == NULL)
737         return;
738     if (style == NULL)
739         return;
740 
741     style->dimensions->refcount--;
742 
743     if (style->dimensions->refcount == 0)
744     {
745         style_dimensions_destroy (style->dimensions);
746         g_hash_table_remove (sheet->dimensions_hash_table,
747                              style_get_key (style));
748     }
749 
750     g_free (style);
751 }
752 
753 
754 void
gnucash_sheet_style_get_cell_pixel_rel_coords(SheetBlockStyle * style,gint cell_row,gint cell_col,gint * x,gint * y,gint * w,gint * h)755 gnucash_sheet_style_get_cell_pixel_rel_coords (SheetBlockStyle *style,
756         gint cell_row, gint cell_col,
757         gint *x, gint *y,
758         gint *w, gint *h)
759 {
760     CellDimensions *cd;
761 
762     g_return_if_fail (style != NULL);
763     g_return_if_fail (cell_row >= 0 && cell_row <= style->nrows);
764     g_return_if_fail (cell_col >= 0 && cell_col <= style->ncols);
765 
766     cd = gnucash_style_get_cell_dimensions (style, cell_row, cell_col);
767     if (!cd) return;
768 
769     *x = cd->origin_x;
770     *y = cd->origin_y;
771     *h = cd->pixel_height;
772     *w = cd->pixel_width;
773 }
774 
775 
776 SheetBlockStyle *
gnucash_sheet_get_style(GnucashSheet * sheet,VirtualCellLocation vcell_loc)777 gnucash_sheet_get_style (GnucashSheet *sheet, VirtualCellLocation vcell_loc)
778 {
779     SheetBlock *block;
780 
781     g_return_val_if_fail (sheet != NULL, NULL);
782     g_return_val_if_fail (GNUCASH_IS_SHEET(sheet), NULL);
783 
784     block = gnucash_sheet_get_block (sheet, vcell_loc);
785 
786     if (block)
787         return block->style;
788     else
789         return NULL;
790 }
791 
792 
793 SheetBlockStyle *
gnucash_sheet_get_style_from_table(GnucashSheet * sheet,VirtualCellLocation vcell_loc)794 gnucash_sheet_get_style_from_table (GnucashSheet *sheet,
795                                     VirtualCellLocation vcell_loc)
796 {
797     Table *table;
798     VirtualCell *vcell;
799     CellBlock *cursor;
800     SheetBlockStyle *style;
801 
802     g_return_val_if_fail (sheet != NULL, NULL);
803     g_return_val_if_fail (GNUCASH_IS_SHEET(sheet), NULL);
804 
805     table = sheet->table;
806 
807     vcell = gnc_table_get_virtual_cell (table, vcell_loc);
808 
809     if (!vcell)
810         return NULL;
811 
812     cursor = vcell->cellblock;
813 
814     style = gnucash_sheet_get_style_from_cursor (sheet,
815             cursor->cursor_name);
816     if (style)
817         return style;
818 
819     return gnucash_sheet_get_style_from_cursor (sheet, CURSOR_HEADER);
820 }
821 
822 SheetBlockStyle *
gnucash_sheet_get_style_from_cursor(GnucashSheet * sheet,const char * cursor_name)823 gnucash_sheet_get_style_from_cursor (GnucashSheet *sheet,
824                                      const char *cursor_name)
825 {
826     g_return_val_if_fail (sheet != NULL, NULL);
827     g_return_val_if_fail (GNUCASH_IS_SHEET (sheet), NULL);
828 
829     if (!cursor_name)
830         return NULL;
831 
832     return g_hash_table_lookup (sheet->cursor_styles, cursor_name);
833 }
834 
835 /*
836  * For now, refcounting doesn't do much, but later we may want to
837  * destroy styles
838  */
839 
840 void
gnucash_sheet_style_ref(GnucashSheet * sheet,SheetBlockStyle * style)841 gnucash_sheet_style_ref (GnucashSheet *sheet, SheetBlockStyle *style)
842 {
843     g_return_if_fail (style != NULL);
844 
845     style->refcount++;
846 }
847 
848 
849 void
gnucash_sheet_style_unref(GnucashSheet * sheet,SheetBlockStyle * style)850 gnucash_sheet_style_unref (GnucashSheet *sheet, SheetBlockStyle *style)
851 {
852     g_return_if_fail (style != NULL);
853 
854     style->refcount--;
855 
856     if (style->refcount == 0)
857         gnucash_sheet_style_destroy (sheet, style);
858 }
859 
860 typedef struct
861 {
862     char *cell_name;
863     int width;
864 } WidthNode;
865 
866 GNCHeaderWidths
gnc_header_widths_new(void)867 gnc_header_widths_new (void)
868 {
869     return g_hash_table_new (g_str_hash, g_str_equal);
870 }
871 
872 static void
header_width_destroy_helper(gpointer key,gpointer value,gpointer user_data)873 header_width_destroy_helper (gpointer key, gpointer value, gpointer user_data)
874 {
875     WidthNode *wn = value;
876 
877     g_free (wn->cell_name);
878     wn->cell_name = NULL;
879 
880     g_free (wn);
881 }
882 
883 void
gnc_header_widths_destroy(GNCHeaderWidths widths)884 gnc_header_widths_destroy (GNCHeaderWidths widths)
885 {
886     if (!widths) return;
887     g_hash_table_foreach (widths, header_width_destroy_helper, NULL);
888     g_hash_table_destroy (widths);
889 }
890 
891 void
gnc_header_widths_set_width(GNCHeaderWidths widths,const char * cell_name,int width)892 gnc_header_widths_set_width (GNCHeaderWidths widths,
893                              const char *cell_name,
894                              int width)
895 {
896     WidthNode *wn;
897 
898     g_return_if_fail (widths != NULL);
899     g_return_if_fail (cell_name != NULL);
900 
901     wn = g_hash_table_lookup (widths, cell_name);
902     if (!wn)
903     {
904         wn = g_new0 (WidthNode, 1);
905 
906         wn->cell_name = g_strdup (cell_name);
907 
908         g_hash_table_insert (widths, wn->cell_name, wn);
909     }
910 
911     wn->width = width;
912 }
913 
914 int
gnc_header_widths_get_width(GNCHeaderWidths widths,const char * cell_name)915 gnc_header_widths_get_width (GNCHeaderWidths widths,
916                              const char *cell_name)
917 {
918     WidthNode *wn;
919 
920     g_return_val_if_fail (widths != NULL, 0);
921 
922     wn = g_hash_table_lookup (widths, cell_name);
923     if (!wn)
924         return 0;
925 
926     return wn->width;
927 }
928 
929 void
gnucash_sheet_get_header_widths(GnucashSheet * sheet,GNCHeaderWidths widths)930 gnucash_sheet_get_header_widths (GnucashSheet *sheet,
931                                  GNCHeaderWidths widths)
932 {
933     SheetBlockStyle *style;
934     CellBlock *header;
935     int row, col;
936 
937     g_return_if_fail (sheet != NULL);
938     g_return_if_fail (GNUCASH_IS_SHEET(sheet));
939 
940     style = gnucash_sheet_get_style_from_cursor (sheet, CURSOR_HEADER);
941     g_return_if_fail (style != NULL);
942 
943     header = style->cursor;
944     g_return_if_fail (header != NULL);
945 
946     for (row = 0; row < style->nrows; row++)
947         for (col = 0; col < style->ncols; col++)
948         {
949             CellDimensions *cd;
950             BasicCell *cell;
951 
952             cd = gnucash_style_get_cell_dimensions (style,
953                                                     row, col);
954             if (cd == NULL)
955                 continue;
956 
957             cell = gnc_cellblock_get_cell (header, row, col);
958             if (!cell || !cell->cell_name)
959                 continue;
960 
961             gnc_header_widths_set_width (widths,
962                                          cell->cell_name,
963                                          cd->pixel_width);
964         }
965 }
966 
967 void
gnucash_sheet_set_header_widths(GnucashSheet * sheet,GNCHeaderWidths widths)968 gnucash_sheet_set_header_widths (GnucashSheet *sheet,
969                                  GNCHeaderWidths widths)
970 {
971     SheetBlockStyle *style;
972     CellBlock *header;
973     int row, col;
974 
975     g_return_if_fail (sheet != NULL);
976     g_return_if_fail (GNUCASH_IS_SHEET(sheet));
977 
978     style = gnucash_sheet_get_style_from_cursor (sheet, CURSOR_HEADER);
979     g_return_if_fail (style != NULL);
980 
981     header = style->cursor;
982     g_return_if_fail (header != NULL);
983 
984     for (row = 0; row < style->nrows; row++)
985         for (col = 0; col < style->ncols; col++)
986         {
987             CellDimensions *cd;
988             BasicCell *cell;
989 
990             cd = gnucash_style_get_cell_dimensions (style,
991                                                     row, col);
992 
993             cell = gnc_cellblock_get_cell (header, row, col);
994             if (!cell || !cell->cell_name || !cd)
995                 continue;
996 
997             cd->pixel_width = gnc_header_widths_get_width
998                               (widths, cell->cell_name);
999         }
1000 }
1001 
1002 gboolean
gnucash_style_init(void)1003 gnucash_style_init (void)
1004 {
1005     return TRUE;
1006 }
1007 
1008 
1009