GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnucash-grid.c
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 [email protected] *
18  * *
19 \********************************************************************/
20 
21 /*
22  * The Gnucash Grid Canvas Item
23  *
24  * Based heavily (i.e., cut and pasted from) on the Gnumeric ItemGrid.
25  *
26  * Author:
27  * Heath Martin <[email protected]>
28  */
29 
30 #include "config.h"
31 
32 #include <string.h>
33 #include <libgnomecanvas/libgnomecanvas.h>
34 
35 #include "gnucash-sheet.h"
36 #include "gnucash-sheetP.h"
37 #include "gnucash-grid.h"
38 #include "gnucash-color.h"
39 #include "gnucash-style.h"
40 
41 
43 {
44  GnomeCanvasItem canvas_item;
45 
46  GnucashSheet *sheet;
47 
48  /* The first and last displayed block */
49  int top_block;
50  int bottom_block;
51 
52  /* Offset from spreadsheet origin in units */
53  long top_offset;
54  long left_offset;
55 
56  GdkGC *grid_gc; /* Draw grid gc */
57  GdkGC *fill_gc; /* Default background fill gc */
58  GdkGC *gc; /* Color used for the cell */
59 
60  GdkColor background;
61  GdkColor grid_color;
62  GdkColor default_color;
63 };
64 
65 
67 {
68  GnomeCanvasItemClass parent_class;
69 };
70 
71 static GnomeCanvasItem *gnucash_grid_parent_class;
72 
73 /* Our arguments */
74 enum
75 {
76  PROP_0,
77  PROP_SHEET
78 };
79 
80 
81 static void
82 gnucash_grid_realize (GnomeCanvasItem *item)
83 {
84  GdkWindow *window;
85  GnucashGrid *gnucash_grid;
86  GdkGC *gc;
87 
88  if (GNOME_CANVAS_ITEM_CLASS (gnucash_grid_parent_class)->realize)
89  (GNOME_CANVAS_ITEM_CLASS
90  (gnucash_grid_parent_class)->realize)(item);
91 
92  gnucash_grid = GNUCASH_GRID (item);
93  window = GTK_WIDGET (item->canvas)->window;
94 
95  /* Configure the default grid gc */
96  gnucash_grid->grid_gc = gc = gdk_gc_new (window);
97  gnucash_grid->fill_gc = gdk_gc_new (window);
98  gnucash_grid->gc = gdk_gc_new (window);
99 
100  /* Allocate the default colors */
101  gnucash_grid->background = gn_white;
102  gnucash_grid->grid_color = gn_black;
103  gnucash_grid->default_color = gn_black;
104 
105  gdk_gc_set_foreground (gc, &gnucash_grid->grid_color);
106  gdk_gc_set_background (gc, &gnucash_grid->background);
107 
108  gdk_gc_set_foreground (gnucash_grid->fill_gc,
109  &gnucash_grid->background);
110  gdk_gc_set_background (gnucash_grid->fill_gc,
111  &gnucash_grid->grid_color);
112 }
113 
114 
115 static void
116 gnucash_grid_unrealize (GnomeCanvasItem *item)
117 {
118  GnucashGrid *gnucash_grid = GNUCASH_GRID (item);
119 
120  if (gnucash_grid->grid_gc != NULL)
121  {
122  g_object_unref(gnucash_grid->grid_gc);
123  gnucash_grid->grid_gc = NULL;
124  }
125 
126  if (gnucash_grid->fill_gc != NULL)
127  {
128  g_object_unref(gnucash_grid->fill_gc);
129  gnucash_grid->fill_gc = NULL;
130  }
131 
132  if (gnucash_grid->gc != NULL)
133  {
134  g_object_unref(gnucash_grid->gc);
135  gnucash_grid->gc = NULL;
136  }
137 
138  if (GNOME_CANVAS_ITEM_CLASS (gnucash_grid_parent_class)->unrealize)
139  (*GNOME_CANVAS_ITEM_CLASS
140  (gnucash_grid_parent_class)->unrealize)(item);
141 }
142 
143 
144 static void
145 gnucash_grid_update (GnomeCanvasItem *item, double *affine,
146  ArtSVP *clip_path, int flags)
147 {
148  if (GNOME_CANVAS_ITEM_CLASS (gnucash_grid_parent_class)->update)
149  (* GNOME_CANVAS_ITEM_CLASS (gnucash_grid_parent_class)->update)
150  (item, affine, clip_path, flags);
151 
152  item->x1 = 0;
153  item->y1 = 0;
154  item->x2 = INT_MAX / 2 - 1;
155  item->y2 = INT_MAX / 2 - 1;
156 }
157 
158 
159 /*
160  * Sets virt_row, virt_col to the block coordinates for the
161  * block containing pixel (x, y). Also sets o_x, o_y, to the
162  * pixel coordinates of the origin of the block. Returns
163  * TRUE if a block is found, FALSE if (x,y) is not
164  * in any block.
165  *
166  * All coordinates are with respect to the canvas origin.
167  */
168 static SheetBlock *
169 gnucash_grid_find_block_by_pixel (GnucashGrid *grid,
170  gint x, gint y,
171  VirtualCellLocation *vcell_loc)
172 {
173  SheetBlock *block;
174  VirtualCellLocation vc_loc = { 1, 0 };
175 
176  g_return_val_if_fail(y >= 0, NULL);
177  g_return_val_if_fail(x >= 0, NULL);
178 
179  do
180  {
181  block = gnucash_sheet_get_block (grid->sheet, vc_loc);
182  if (!block)
183  return NULL;
184 
185  if (block->visible &&
186  y >= block->origin_y &&
187  y < block->origin_y + block->style->dimensions->height)
188  {
189  if (vcell_loc)
190  vcell_loc->virt_row = vc_loc.virt_row;
191  break;
192  }
193  vc_loc.virt_row++;
194  }
195  while (vc_loc.virt_row < grid->sheet->num_virt_rows);
196 
197  if (vc_loc.virt_row == grid->sheet->num_virt_rows)
198  return NULL;
199 
200  do
201  {
202  block = gnucash_sheet_get_block (grid->sheet, vc_loc);
203  if (!block)
204  return NULL;
205 
206  if (block->visible &&
207  x >= block->origin_x &&
208  x < block->origin_x + block->style->dimensions->width)
209  {
210  if (vcell_loc)
211  vcell_loc->virt_col = vc_loc.virt_col;
212  break;
213  }
214  vc_loc.virt_col++;
215  }
216  while (vc_loc.virt_col < grid->sheet->num_virt_cols);
217 
218  if (vc_loc.virt_col == grid->sheet->num_virt_cols)
219  return NULL;
220 
221  return block;
222 }
223 
224 static gboolean
225 gnucash_grid_find_cell_by_pixel (GnucashGrid *grid, gint x, gint y,
226  VirtualLocation *virt_loc)
227 {
228  SheetBlock *block;
229  SheetBlockStyle *style;
230  CellDimensions *cd;
231  gint row = 0;
232  gint col = 0;
233 
234  g_return_val_if_fail (virt_loc != NULL, FALSE);
235 
236  block = gnucash_sheet_get_block (grid->sheet, virt_loc->vcell_loc);
237  if (block == NULL)
238  return FALSE;
239 
240  /* now make x, y relative to the block origin */
241  x -= block->origin_x;
242  y -= block->origin_y;
243 
244  style = block->style;
245  if (style == NULL)
246  return FALSE;
247 
248  do
249  {
250  cd = gnucash_style_get_cell_dimensions (style, row, 0);
251 
252  if (y >= cd->origin_y && y < cd->origin_y + cd->pixel_height)
253  break;
254 
255  row++;
256  }
257  while (row < style->nrows);
258 
259  if (row == style->nrows)
260  return FALSE;
261 
262  do
263  {
264  cd = gnucash_style_get_cell_dimensions (style, row, col);
265 
266  if (x >= cd->origin_x && x < cd->origin_x + cd->pixel_width)
267  break;
268 
269  col++;
270  }
271  while (col < style->ncols);
272 
273  if (col == style->ncols)
274  return FALSE;
275 
276  if (virt_loc)
277  virt_loc->phys_row_offset = row;
278  if (virt_loc)
279  virt_loc->phys_col_offset = col;
280 
281  return TRUE;
282 }
283 
284 gboolean
285 gnucash_grid_find_loc_by_pixel (GnucashGrid *grid, gint x, gint y,
286  VirtualLocation *virt_loc)
287 {
288  SheetBlock *block;
289 
290  if (virt_loc == NULL)
291  return FALSE;
292 
293  block = gnucash_grid_find_block_by_pixel (grid, x, y,
294  &virt_loc->vcell_loc);
295  if (block == NULL)
296  return FALSE;
297 
298  return gnucash_grid_find_cell_by_pixel (grid, x, y, virt_loc);
299 }
300 
301 G_INLINE_FUNC void
302 draw_cell_line (GdkDrawable *drawable,
303  GdkGC *gc, GdkColor *bg_color,
304  int x1, int y1, int x2, int y2,
305  PhysicalCellBorderLineStyle style);
306 
307 void
308 draw_cell_line (GdkDrawable *drawable,
309  GdkGC *gc, GdkColor *bg_color,
310  int x1, int y1, int x2, int y2,
311  PhysicalCellBorderLineStyle style)
312 {
313  GdkColor *fg_color;
314 
315  switch (style)
316  {
317  case CELL_BORDER_LINE_NONE:
318  fg_color = bg_color;
319  break;
320 
321  case CELL_BORDER_LINE_LIGHT:
322  fg_color = &gn_light_gray;
323  break;
324 
325  case CELL_BORDER_LINE_NORMAL:
326  case CELL_BORDER_LINE_HEAVY:
327  fg_color = &gn_black;
328  break;
329 
330  case CELL_BORDER_LINE_HIGHLIGHT:
331  fg_color = &gn_red;
332  break;
333 
334  default:
335  return;
336  }
337 
338  gdk_gc_set_foreground (gc, fg_color);
339  gdk_draw_line (drawable, gc, x1, y1, x2, y2);
340 }
341 
342 static void
343 get_cell_borders (GnucashSheet *sheet, VirtualLocation virt_loc,
344  PhysicalCellBorders *borders)
345 {
346  VirtualLocation v_loc;
347  PhysicalCellBorders neighbor;
348 
349  gnucash_sheet_get_borders (sheet, virt_loc, borders);
350 
351  /* top */
352  v_loc = virt_loc;
353  if (gnc_table_move_vertical_position (sheet->table, &v_loc, -1))
354  {
355  gnucash_sheet_get_borders (sheet, v_loc, &neighbor);
356  borders->top = MAX (borders->top, neighbor.bottom);
357  }
358 
359  /* bottom */
360  v_loc = virt_loc;
361  if (gnc_table_move_vertical_position (sheet->table, &v_loc, 1))
362  {
363  gnucash_sheet_get_borders (sheet, v_loc, &neighbor);
364  borders->bottom = MAX (borders->bottom, neighbor.top);
365  }
366 
367  /* left */
368  v_loc = virt_loc;
369  v_loc.phys_col_offset--;
370  if (gnc_table_virtual_loc_valid (sheet->table, v_loc, TRUE))
371  {
372  gnucash_sheet_get_borders (sheet, v_loc, &neighbor);
373  borders->left = MAX (borders->left, neighbor.right);
374  }
375 
376  /* right */
377  v_loc = virt_loc;
378  v_loc.phys_col_offset++;
379  if (gnc_table_virtual_loc_valid (sheet->table, v_loc, TRUE))
380  {
381  gnucash_sheet_get_borders (sheet, v_loc, &neighbor);
382  borders->right = MAX (borders->right, neighbor.left);
383  }
384 }
385 
386 void
387 gnucash_draw_hatching (GdkDrawable *drawable, GdkGC *gc,
388  int x, int y, int width, int height)
389 {
390  gdk_gc_set_foreground (gc, &gn_light_gray);
391 
392  gdk_draw_rectangle (drawable, gc, FALSE,
393  x + 2, y + 2, height / 3, height / 3);
394 
395  gdk_draw_line (drawable, gc,
396  x + 2, y + 2 + height / 3, x + 2 + height / 3, y + 2);
397 
398  gdk_draw_line (drawable, gc,
399  x + 2, y + 2, x + 2 + height / 3, y + 2 + height / 3);
400 }
401 
402 #ifdef READONLY_LINES_WITH_CHANGED_FG_COLOR
403 
407 static guint8 inc_intensity_byte(guint8 input, int numerator, int denominator)
408 {
409  guint8 result_inv, result;
410  guint8 input_inv = 0xff - input;
411  result_inv = (input_inv * numerator) / denominator;
412  result = 0xff - result_inv;
413  return result;
414 }
415 
419 static guint32 inc_intensity_10percent(guint32 argb)
420 {
421  guint32 result =
422  (inc_intensity_byte((argb & 0x00FF0000) >> 16, 8, 10) << 16)
423  + (inc_intensity_byte((argb & 0x0000FF00) >> 8, 8, 10) << 8)
424  + (inc_intensity_byte(argb & 0x000000FF, 8, 10));
425  return result;
426 }
427 #endif
428 
433 static guint8 dec_intensity_byte(guint8 input, int numerator, int denominator)
434 {
435  guint8 result;
436  result = (input * numerator) / denominator;
437  return result;
438 }
439 
442 static guint32 dec_intensity_10percent(guint32 argb)
443 {
444  // Multiply each single byte by 9/10 i.e. by 0.9 which decreases the
445  // intensity by 10 percent.
446  guint32 result =
447  (dec_intensity_byte((argb & 0x00FF0000) >> 16, 9, 10) << 16)
448  + (dec_intensity_byte((argb & 0x0000FF00) >> 8, 9, 10) << 8)
449  + (dec_intensity_byte(argb & 0x000000FF, 9, 10));
450  return result;
451 }
452 
453 static void
454 draw_cell (GnucashGrid *grid,
455  SheetBlock *block,
456  VirtualLocation virt_loc,
457  GdkDrawable *drawable,
458  int x, int y, int width, int height)
459 {
460  Table *table = grid->sheet->table;
461  PhysicalCellBorders borders;
462  const char *text;
463  PangoLayout *layout;
464  PangoContext *context;
465  PangoFontDescription *font;
466  PangoRectangle logical_rect;
467  GdkColor *bg_color;
468  GdkColor *fg_color;
469  /* gint x_offset, y_offset;*/
470  GdkRectangle rect;
471  gboolean hatching;
472  guint32 argb, color_type;
473  int x_offset;
474 
475  gdk_gc_set_background (grid->gc, &gn_white);
476 
477  if (grid->sheet->use_theme_colors)
478  {
479  color_type = gnc_table_get_gtkrc_bg_color (table, virt_loc,
480  &hatching);
481  bg_color = get_gtkrc_color(grid->sheet, color_type);
482  }
483  else
484  {
485  argb = gnc_table_get_bg_color (table, virt_loc, &hatching);
486  // Are we in a read-only row? Then make the background color somewhat more gray.
487  if ((virt_loc.phys_row_offset == (block->style->nrows - 1))
488  && (table->model->dividing_row_upper >= 0)
489  && (virt_loc.vcell_loc.virt_row < table->model->dividing_row_upper))
490  {
491  argb = dec_intensity_10percent(argb);
492  }
493  bg_color = gnucash_color_argb_to_gdk (argb);
494  }
495 
496  gdk_gc_set_foreground (grid->gc, bg_color);
497  gdk_draw_rectangle (drawable, grid->gc, TRUE,
498  x + 1, y + 1, width - 1, height - 1);
499 
500  get_cell_borders (grid->sheet, virt_loc, &borders);
501 
502  /* top */
503  draw_cell_line (drawable, grid->gc, bg_color,
504  borders.top >= borders.left ? x : x + 1,
505  y,
506  (borders.top >= borders.right ?
507  x + width : x + width - 1),
508  y,
509  borders.top);
510 
511  /* bottom */
512  draw_cell_line (drawable, grid->gc, bg_color,
513  borders.bottom >= borders.left ? x : x + 1,
514  y + height,
515  (borders.bottom >= borders.right ?
516  x + width : x + width - 1),
517  y + height,
518  borders.bottom);
519 
520  /* left */
521  draw_cell_line (drawable, grid->gc, bg_color,
522  x,
523  borders.left > borders.top ? y : y + 1,
524  x,
525  (borders.left > borders.bottom ?
526  y + height : y + height - 1),
527  borders.left);
528 
529  /* right */
530  draw_cell_line (drawable, grid->gc, bg_color,
531  x + width,
532  borders.right > borders.top ? y : y + 1,
533  x + width,
534  (borders.right > borders.bottom ?
535  y + height : y + height - 1),
536  borders.right);
537 
538  if (hatching)
539  gnucash_draw_hatching (drawable, grid->gc,
540  x, y, width, height);
541 
542  /* dividing line upper (red) */
543  if ((virt_loc.phys_row_offset == 0) &&
544  (table->model->dividing_row_upper >= 0))
545  {
546  if (virt_loc.vcell_loc.virt_row == table->model->dividing_row_upper)
547  {
548  gdk_gc_set_foreground (grid->gc, &gn_red);
549  gdk_draw_line (drawable, grid->gc, x, y - 1, x + width, y - 1);
550  gdk_draw_line (drawable, grid->gc, x, y, x + width, y);
551  gdk_draw_line (drawable, grid->gc, x, y + 1, x + width, y + 1);
552  }
553  }
554 
555  if ((virt_loc.phys_row_offset == (block->style->nrows - 1)) &&
556  (table->model->dividing_row_upper >= 0))
557  {
558  if (virt_loc.vcell_loc.virt_row ==
559  (table->model->dividing_row_upper - 1))
560  {
561  gdk_gc_set_foreground (grid->gc, &gn_red);
562  gdk_draw_line (drawable, grid->gc, x, y + height - 1,
563  x + width, y + height - 1);
564  gdk_draw_line (drawable, grid->gc, x, y + height,
565  x + width, y + height);
566  gdk_draw_line (drawable, grid->gc, x, y + height + 1,
567  x + width, y + height + 1);
568  }
569  }
570 
571  /* dividing line (blue) */
572  if ((virt_loc.phys_row_offset == 0) &&
573  (table->model->dividing_row >= 0))
574  {
575  if (virt_loc.vcell_loc.virt_row == table->model->dividing_row)
576  {
577  gdk_gc_set_foreground (grid->gc, &gn_blue);
578  gdk_draw_line (drawable, grid->gc, x, y - 1, x + width, y - 1);
579  gdk_draw_line (drawable, grid->gc, x, y, x + width, y);
580  gdk_draw_line (drawable, grid->gc, x, y + 1, x + width, y + 1);
581  }
582  }
583 
584  if ((virt_loc.phys_row_offset == (block->style->nrows - 1)) &&
585  (table->model->dividing_row >= 0))
586  {
587  if (virt_loc.vcell_loc.virt_row ==
588  (table->model->dividing_row - 1))
589  {
590  gdk_gc_set_foreground (grid->gc, &gn_blue);
591  gdk_draw_line (drawable, grid->gc, x, y + height - 1,
592  x + width, y + height - 1);
593  gdk_draw_line (drawable, grid->gc, x, y + height,
594  x + width, y + height);
595  gdk_draw_line (drawable, grid->gc, x, y + height + 1,
596  x + width, y + height + 1);
597  }
598  }
599 
600  /* dividing line lower (blue) */
601  if ((virt_loc.phys_row_offset == 0) &&
602  (table->model->dividing_row_lower >= 0))
603  {
604  if (virt_loc.vcell_loc.virt_row == table->model->dividing_row_lower)
605  {
606  gdk_gc_set_foreground (grid->gc, &gn_blue);
607  gdk_draw_line (drawable, grid->gc, x, y - 1, x + width, y - 1);
608  gdk_draw_line (drawable, grid->gc, x, y, x + width, y);
609  gdk_draw_line (drawable, grid->gc, x, y + 1, x + width, y + 1);
610  }
611  }
612 
613  if ((virt_loc.phys_row_offset == (block->style->nrows - 1)) &&
614  (table->model->dividing_row_lower >= 0))
615  {
616  if (virt_loc.vcell_loc.virt_row ==
617  (table->model->dividing_row_lower - 1))
618  {
619  gdk_gc_set_foreground (grid->gc, &gn_blue);
620  gdk_draw_line (drawable, grid->gc, x, y + height - 1,
621  x + width, y + height - 1);
622  gdk_draw_line (drawable, grid->gc, x, y + height,
623  x + width, y + height);
624  gdk_draw_line (drawable, grid->gc, x, y + height + 1,
625  x + width, y + height + 1);
626  }
627  }
628 
629  text = gnc_table_get_entry (table, virt_loc);
630 
631  layout = gtk_widget_create_pango_layout (GTK_WIDGET (grid->sheet), text);
632  // We don't need word wrap or line wrap
633  pango_layout_set_width (layout, -1);
634  context = pango_layout_get_context (layout);
635  font = pango_font_description_copy (pango_context_get_font_description (context));
636 
637  if (grid->sheet->use_theme_colors)
638  {
639  color_type = gnc_table_get_gtkrc_fg_color (table, virt_loc);
640  fg_color = get_gtkrc_color(grid->sheet, color_type);
641  }
642  else
643  {
644  argb = gnc_table_get_fg_color (table, virt_loc);
645 #ifdef READONLY_LINES_WITH_CHANGED_FG_COLOR
646  // Are we in a read-only row? Then make the foreground color somewhat less black
647  if ((virt_loc.phys_row_offset == (block->style->nrows - 1))
648  && (table->model->dividing_row_upper >= 0)
649  && (virt_loc.vcell_loc.virt_row < table->model->dividing_row_upper))
650  {
651  argb = inc_intensity_10percent(argb);
652  }
653 #endif
654  fg_color = gnucash_color_argb_to_gdk (argb);
655  }
656 
657  gdk_gc_set_foreground (grid->gc, fg_color);
658 
659  /* If this is the currently open transaction and
660  there is no text in this cell */
661  if ((table->current_cursor_loc.vcell_loc.virt_row ==
662  virt_loc.vcell_loc.virt_row) &&
663  (!text || strlen(text) == 0))
664  {
665  text = gnc_table_get_label (table, virt_loc);
666  if ((text == NULL) || (*text == '\0'))
667  goto exit;
668  gdk_gc_set_foreground (grid->gc, &gn_light_gray);
669  pango_layout_set_text (layout, text, strlen (text));
670  pango_font_description_set_style (font, PANGO_STYLE_ITALIC);
671  pango_context_set_font_description (context, font);
672  }
673 
674  if ((text == NULL) || (*text == '\0'))
675  {
676  goto exit;
677  }
678 
679  /*y_offset = ((height / 2) +
680  (((font->ascent + font->descent) / 2) - font->descent));
681  y_offset++;*/
682 
683  pango_layout_get_pixel_extents(layout,
684  NULL,
685  &logical_rect);
686 
687  rect.x = x + CELL_HPADDING;
688  rect.y = y + CELL_VPADDING;
689  rect.width = MAX (0, width - (2 * CELL_HPADDING));
690  rect.height = height - 2;
691 
692  gdk_gc_set_clip_rectangle (grid->gc, &rect);
693 
694 
695  switch (gnc_table_get_align (table, virt_loc))
696  {
697  default:
698  case CELL_ALIGN_LEFT:
699  x_offset = 0;
700  break;
701 
702  case CELL_ALIGN_RIGHT:
703  x_offset = width - 2 * CELL_HPADDING - logical_rect.width;
704  break;
705 
706  case CELL_ALIGN_CENTER:
707  if (logical_rect.width > width - 2 * CELL_HPADDING)
708  x_offset = 0;
709  else
710  x_offset = (width - 2 * CELL_HPADDING -
711  logical_rect.width) / 2;
712  break;
713  }
714 
715 
716 
717  gdk_draw_layout (drawable,
718  grid->gc,
719  x + CELL_HPADDING + x_offset,
720  y + CELL_VPADDING + 1,
721  layout);
722 
723  gdk_gc_set_clip_rectangle (grid->gc, NULL);
724 
725 exit:
726  pango_font_description_set_style (font, PANGO_STYLE_NORMAL);
727  pango_context_set_font_description (context, font);
728  pango_font_description_free (font);
729  g_object_unref (layout);
730 }
731 
732 static void
733 draw_block (GnucashGrid *grid,
734  SheetBlock *block,
735  VirtualLocation virt_loc,
736  GdkDrawable *drawable,
737  int x, int y, int width, int height)
738 {
739  CellDimensions *cd;
740  gint x_paint;
741  gint y_paint;
742  gint w, h;
743 
744  for ( virt_loc.phys_row_offset = 0;
745  virt_loc.phys_row_offset < block->style->nrows ;
746  virt_loc.phys_row_offset++ )
747  {
748  for ( virt_loc.phys_col_offset = 0;
749  virt_loc.phys_col_offset < block->style->ncols ;
750  virt_loc.phys_col_offset++ )
751  {
752  cd = gnucash_style_get_cell_dimensions
753  (block->style,
754  virt_loc.phys_row_offset,
755  virt_loc.phys_col_offset);
756 
757  x_paint = block->origin_x + cd->origin_x;
758  if (x_paint > x + width)
759  break;
760 
761  y_paint = block->origin_y + cd->origin_y;
762  if (y_paint > y + height)
763  return;
764 
765  h = cd->pixel_height;
766  w = cd->pixel_width;
767 
768  if (w == 0)
769  continue;
770 
771  if (x_paint + w < x)
772  continue;
773 
774  if (y_paint + h < y)
775  continue;
776 
777  draw_cell (grid, block, virt_loc, drawable,
778  x_paint - x, y_paint - y, w, h);
779  }
780  }
781 }
782 
783 static void
784 gnucash_grid_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
785  int x, int y, int width, int height)
786 {
787  GnucashGrid *grid = GNUCASH_GRID (item);
788  VirtualLocation virt_loc;
789  SheetBlock *sheet_block;
790 
791  if (x < 0 || y < 0)
792  return;
793 
794  /* compute our initial values where we start drawing */
795  sheet_block = gnucash_grid_find_block_by_pixel (grid, x, y,
796  &virt_loc.vcell_loc);
797  if (!sheet_block || !sheet_block->style)
798  return;
799 
800  for ( ; virt_loc.vcell_loc.virt_row < grid->sheet->num_virt_rows;
801  virt_loc.vcell_loc.virt_row++ )
802  {
803  while (TRUE)
804  {
805  sheet_block = gnucash_sheet_get_block
806  (grid->sheet, virt_loc.vcell_loc);
807 
808  if (!sheet_block || !sheet_block->style)
809  return;
810 
811  if (sheet_block->visible)
812  break;
813 
814  virt_loc.vcell_loc.virt_row++;
815  }
816 
817  if (y + height < sheet_block->origin_y)
818  return;
819 
820  draw_block (grid, sheet_block, virt_loc, drawable,
821  x, y, width, height);
822  }
823 }
824 
825 
826 static void
827 gnucash_grid_init (GnucashGrid *grid)
828 {
829  GnomeCanvasItem *item = GNOME_CANVAS_ITEM (grid);
830 
831  item->x1 = 0;
832  item->y1 = 0;
833  item->x2 = 0;
834  item->y2 = 0;
835 
836  grid->top_block = 0;
837  grid->top_offset = 0;
838  grid->left_offset = 0;
839 }
840 
841 
842 static void
843 gnucash_grid_set_property (GObject *object,
844  guint prop_id,
845  const GValue *value,
846  GParamSpec *pspec)
847 {
848  GnucashGrid *grid = GNUCASH_GRID (object);
849 
850  switch (prop_id)
851  {
852  case PROP_SHEET:
853  grid->sheet =
854  GNUCASH_SHEET (g_value_get_object (value));
855  break;
856  default:
857  break;
858  }
859 }
860 
861 
862 /* Note that g_value_set_object() refs the object, as does
863  * g_object_get(). But g_object_get() only unrefs once when it disgorges
864  * the object, leaving an unbalanced ref, which leaks. So instead of
865  * using g_value_set_object(), use g_value_take_object() which doesn't
866  * ref the object when used in get_property().
867  */
868 static void
869 gnucash_grid_get_property (GObject *object,
870  guint prop_id,
871  GValue *value,
872  GParamSpec *pspec)
873 {
874  GnucashGrid *grid = GNUCASH_GRID (object);
875 
876  switch (prop_id)
877  {
878  case PROP_SHEET:
879  g_value_take_object (value, grid->sheet);
880  break;
881  default:
882  break;
883  }
884 }
885 
886 
887 static void
888 gnucash_grid_class_init (GnucashGridClass *klass)
889 {
890  GObjectClass *object_class;
891  GnomeCanvasItemClass *item_class;
892 
893  object_class = G_OBJECT_CLASS (klass);
894  item_class = GNOME_CANVAS_ITEM_CLASS (klass);
895 
896  gnucash_grid_parent_class = g_type_class_peek_parent (klass);
897 
898  /* GObject method overrides */
899  object_class->set_property = gnucash_grid_set_property;
900  object_class->get_property = gnucash_grid_get_property;
901 
902  /* GnomeCanvasItem method overrides */
903  item_class->update = gnucash_grid_update;
904  item_class->realize = gnucash_grid_realize;
905  item_class->unrealize = gnucash_grid_unrealize;
906  item_class->draw = gnucash_grid_draw;
907 
908  /* properties */
909  g_object_class_install_property
910  (object_class,
911  PROP_SHEET,
912  g_param_spec_object ("sheet",
913  "Sheet Value",
914  "Sheet Value",
915  GNUCASH_TYPE_SHEET,
916  G_PARAM_READWRITE));
917 }
918 
919 
920 GType
921 gnucash_grid_get_type (void)
922 {
923  static GType gnucash_grid_type = 0;
924 
925  if (!gnucash_grid_type)
926  {
927  static const GTypeInfo gnucash_grid_info =
928  {
929  sizeof (GnucashGridClass),
930  NULL, /* base_init */
931  NULL, /* base_finalize */
932  (GClassInitFunc) gnucash_grid_class_init,
933  NULL, /* class_finalize */
934  NULL, /* class_data */
935  sizeof (GnucashGrid),
936  0, /* n_preallocs */
937  (GInstanceInitFunc) gnucash_grid_init
938  };
939 
940  gnucash_grid_type =
941  g_type_register_static (gnome_canvas_item_get_type (),
942  "GnucashGrid",
943  &gnucash_grid_info, 0);
944  }
945 
946  return gnucash_grid_type;
947 }
948 
949 
gboolean gnc_table_move_vertical_position(Table *table, VirtualLocation *virt_loc, int phys_row_offset)