1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 Maxime DOYEN
4 * This file is part of HomeBank.
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * HomeBank is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "gtk-chart-colors.h"
28 #include "gtk-chart-progress.h"
42 #define DBGDRAW_RECT 0
43 #define DBGDRAW_TEXT 0
44 #define DBGDRAW_ITEM 0
48 /* --- prototypes --- */
49 static void ui_chart_progress_class_init (ChartProgressClass
*klass
);
50 static void ui_chart_progress_init (ChartProgress
*chart
);
51 static void ui_chart_progress_destroy (GtkWidget
*chart
);
52 /*static void ui_chart_progress_set_property (GObject *object,
57 static gboolean
drawarea_configure_event_callback (GtkWidget
*widget
, GdkEventConfigure
*event
, gpointer user_data
);
58 static gboolean
drawarea_draw_callback(GtkWidget
*widget
, cairo_t
*cr
, gpointer user_data
);
59 static gboolean
drawarea_scroll_event_callback( GtkWidget
*widget
, GdkEventScroll
*event
, gpointer user_data
);
60 static gboolean
drawarea_motionnotifyevent_callback(GtkWidget
*widget
, GdkEventMotion
*event
, gpointer user_data
);
61 static void ui_chart_progress_first_changed( GtkAdjustment
*adj
, gpointer user_data
);
63 static void ui_chart_progress_clear(ChartProgress
*chart
, gboolean store
);
65 static gboolean
drawarea_full_redraw(GtkWidget
*widget
, gpointer user_data
);
66 static void ui_chart_progress_queue_redraw(ChartProgress
*chart
);
68 /* --- variables --- */
69 static GtkBoxClass
*parent_class
= NULL
;
72 /* --- functions --- */
73 GType
ui_chart_progress_get_type ()
75 static GType ui_chart_progress_type
= 0;
77 if (G_UNLIKELY(ui_chart_progress_type
== 0))
79 const GTypeInfo ui_chart_progress_info
=
81 sizeof (ChartProgressClass
),
83 NULL
, /* base_finalize */
84 (GClassInitFunc
) ui_chart_progress_class_init
,
85 NULL
, /* class_finalize */
86 NULL
, /* class_init */
87 sizeof (ChartProgress
),
89 (GInstanceInitFunc
) ui_chart_progress_init
,
90 NULL
/* value_table */
93 ui_chart_progress_type
= g_type_register_static (GTK_TYPE_BOX
, "ChartProgress",
94 &ui_chart_progress_info
, 0);
97 return ui_chart_progress_type
;
101 static void ui_chart_progress_class_init (ChartProgressClass
* class)
103 //GObjectClass *gobject_class;
104 GtkWidgetClass
*widget_class
;
106 DB( g_print("\n[chartstack] class_init\n") );
108 //gobject_class = (GObjectClass*) class;
109 widget_class
= (GtkWidgetClass
*) class;
111 parent_class
= g_type_class_peek_parent (class);
113 //gobject_class->dispose = ui_chart_progress_dispose;
114 //gobject_class->finalize = ui_chart_progress_finalize;
115 //gobject_class->set_property = ui_chart_progress_set_property;
116 //gobject_class->get_property = ui_chart_progress_get_property;
118 widget_class
->destroy
= ui_chart_progress_destroy
;
123 /* get/set properties goes here */
127 ui_chart_progress_init (ChartProgress
* chart
)
129 GtkWidget
*widget
, *hbox
, *frame
;
132 DB( g_print("\n[chartstack] init\n") );
134 chart
->surface
= NULL
;
138 chart
->subtitle
= NULL
;
141 chart
->budget_title
= "Budget";
142 chart
->result_title
= "Result";
144 chart
->barw
= GTK_CHARTPROGRESS_BARW
;
145 ui_chart_progress_set_color_scheme(chart
, CHART_COLMAP_HOMEBANK
);
147 widget
=GTK_WIDGET(chart
);
149 gtk_box_set_homogeneous(GTK_BOX(widget
), FALSE
);
151 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
152 gtk_box_pack_start (GTK_BOX (widget
), hbox
, TRUE
, TRUE
, 0);
154 /* frame & drawing area */
155 frame
= gtk_frame_new(NULL
);
156 gtk_frame_set_shadow_type (GTK_FRAME(frame
), GTK_SHADOW_ETCHED_IN
);
157 gtk_box_pack_start (GTK_BOX (hbox
), frame
, TRUE
, TRUE
, 0);
159 chart
->drawarea
= gtk_drawing_area_new();
160 //gtk_widget_set_double_buffered (GTK_WIDGET(widget), FALSE);
162 gtk_container_add( GTK_CONTAINER(frame
), chart
->drawarea
);
163 gtk_widget_set_size_request(chart
->drawarea
, 150, 150 );
165 gtk_widget_set_has_tooltip(chart
->drawarea
, TRUE
);
167 gtk_widget_show(chart
->drawarea
);
170 chart
->adjustment
= GTK_ADJUSTMENT(gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0));
171 chart
->scrollbar
= gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL
,GTK_ADJUSTMENT (chart
->adjustment
));
172 gtk_box_pack_start (GTK_BOX (hbox
), chart
->scrollbar
, FALSE
, TRUE
, 0);
175 g_signal_connect( G_OBJECT(chart
->drawarea
), "configure-event", G_CALLBACK (drawarea_configure_event_callback
), chart
);
176 //g_signal_connect( G_OBJECT(chart->drawarea), "realize", G_CALLBACK(drawarea_realize_callback), chart ) ;
177 g_signal_connect( G_OBJECT(chart
->drawarea
), "draw", G_CALLBACK(drawarea_draw_callback
), chart
);
180 gtk_widget_add_events(GTK_WIDGET(chart
->drawarea
),
182 //GDK_POINTER_MOTION_MASK |
183 //GDK_POINTER_MOTION_HINT_MASK |
187 //g_signal_connect( G_OBJECT(chart->drawarea), "query-tooltip", G_CALLBACK(drawarea_querytooltip_callback), chart );
188 g_signal_connect( G_OBJECT(chart
->drawarea
), "scroll-event", G_CALLBACK(drawarea_scroll_event_callback
), chart
) ;
189 g_signal_connect( G_OBJECT(chart
->drawarea
), "motion-notify-event", G_CALLBACK(drawarea_motionnotifyevent_callback
), chart
);
192 g_signal_connect (G_OBJECT(chart
->adjustment
), "value-changed", G_CALLBACK (ui_chart_progress_first_changed
), chart
);
195 g_signal_connect( G_OBJECT(chart->drawarea), "leave-notify-event", G_CALLBACK(ui_chart_progress_leave), chart );
196 g_signal_connect( G_OBJECT(chart->drawarea), "enter-notify-event", G_CALLBACK(ui_chart_progress_enter), chart );
197 g_signal_connect( G_OBJECT(chart->drawarea), "button-press-event", G_CALLBACK(ui_chart_progress_button_press), chart );
198 g_signal_connect( G_OBJECT(chart->drawarea), "button-release-event", G_CALLBACK(ui_chart_progress_button_release), chart );
206 ui_chart_progress_new (void)
210 chart
= (GtkWidget
*)g_object_new (GTK_TYPE_CHARTPROGRESS
, NULL
);
217 ui_chart_progress_destroy (GtkWidget
* object
)
219 ChartProgress
*chart
= GTK_CHARTPROGRESS(object
);
221 g_return_if_fail (GTK_IS_CHARTPROGRESS (object
));
223 ui_chart_progress_clear(GTK_CHARTPROGRESS (object
), FALSE
);
227 pango_font_description_free (chart
->pfd
);
233 cairo_surface_destroy (chart
->surface
);
234 chart
->surface
= NULL
;
237 GTK_WIDGET_CLASS (parent_class
)->destroy (object
);
244 ** print a integer number
246 static gchar
*ui_chart_progress_print_int(ChartProgress
*chart
, gdouble value
)
249 hb_strfmon(chart
->buffer
, CHART_BUFFER_LENGTH
-1, value
, chart
->kcur
, chart
->minor
);
250 return chart
->buffer
;
254 static void ui_chart_progress_clear(ChartProgress
*chart
, gboolean store
)
258 DB( g_print("\n[chartstack] clear\n") );
260 //free & clear any previous allocated datas
261 if(chart
->title
!= NULL
)
263 g_free(chart
->title
);
267 if(chart
->subtitle
!= NULL
)
269 g_free(chart
->subtitle
);
270 chart
->subtitle
= NULL
;
273 if(chart
->items
!= NULL
)
275 for(i
=0;i
<chart
->nb_items
;i
++)
277 StackItem
*item
= &g_array_index(chart
->items
, StackItem
, i
);
279 g_free(item
->label
); //we free label as it comes from a model_get into setup_with_model
280 g_free(item
->status
); //we free status as it comes from a model_get into setup_with_model
282 g_array_free(chart
->items
, TRUE
);
291 static void ui_chart_progress_setup_with_model(ChartProgress
*chart
, GtkTreeModel
*list_store
, gchar
*coltitle1
, gchar
*coltitle2
)
297 DB( g_print("\n[chartstack] setup with model\n") );
299 ui_chart_progress_clear(chart
, TRUE
);
301 chart
->nb_items
= gtk_tree_model_iter_n_children(GTK_TREE_MODEL(list_store
), NULL
);
303 chart
->items
= g_array_sized_new(FALSE
, FALSE
, sizeof(StackItem
), chart
->nb_items
);
305 DB( g_print(" nb=%d\n", chart
->nb_items
) );
308 chart
->budget_title
= coltitle1
;
310 chart
->result_title
= coltitle2
;
312 /* Get the first iter in the list */
313 valid
= gtk_tree_model_get_iter_first (GTK_TREE_MODEL(list_store
), &iter
);
318 gchar
*label
, *status
;
319 gdouble value1
, value2
;
322 gtk_tree_model_get (GTK_TREE_MODEL(list_store
), &iter
,
334 item
.budget
= value2
;
335 item
.status
= status
;
337 /* additional pre-compute */
338 item
.result
= item
.spent
- item
.budget
;
340 if(ABS(item
.budget
) > 0)
342 item
.rawrate
= item
.spent
/ item
.budget
;
345 item
.warn
= item
.result
< 0.0 ? TRUE
: FALSE
;
347 item
.rate
= CLAMP(item
.rawrate
, 0, 1.0);
349 g_array_append_vals(chart
->items
, &item
, 1);
351 //don't g_free(label); here done into chart_clear
352 //don't g_free(status); here done into chart_clear
355 valid
= gtk_tree_model_iter_next (list_store
, &iter
);
361 static void ui_chart_progress_set_font_size(ChartProgress
*chart
, gint font_size
)
365 if( chart
->pfd
== NULL
)
368 DB( g_print("\n[chartstack] set font size\n") );
372 case CHART_FONT_SIZE_TITLE
:
373 size
= chart
->pfd_size
+ 3;
375 case CHART_FONT_SIZE_SUBTITLE
:
376 size
= chart
->pfd_size
+ 1;
378 case CHART_FONT_SIZE_NORMAL
:
379 size
= chart
->pfd_size
- 1;
383 DB( g_print(" size=%d\n", size
) );
385 pango_font_description_set_size(chart
->pfd
, size
* PANGO_SCALE
);
391 static void ui_chart_progress_calculation(ChartProgress
*chart
)
393 GtkWidget
*drawarea
= chart
->drawarea
;
394 cairo_surface_t
*surf
;
400 GtkAllocation allocation
;
403 DB( g_print("\n[chartstack] bar calculation\n") );
405 gtk_widget_get_allocation(drawarea
, &allocation
);
407 chart
->l
= CHART_MARGIN
;
408 chart
->t
= CHART_MARGIN
;
409 chart
->r
= allocation
.width
- CHART_MARGIN
;
410 chart
->b
= allocation
.height
- CHART_MARGIN
;
411 chart
->w
= allocation
.width
- (CHART_MARGIN
*2);
412 chart
->h
= allocation
.height
- (CHART_MARGIN
*2);
414 //todo: seems not working well...
415 surf
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, allocation
.width
, allocation
.height
);
416 cr
= cairo_create (surf
);
418 /* Create a PangoLayout, set the font and text */
419 layout
= pango_cairo_create_layout (cr
);
426 //pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_TITLE * PANGO_SCALE);
427 ui_chart_progress_set_font_size(chart
, CHART_FONT_SIZE_TITLE
);
428 pango_layout_set_font_description (layout
, chart
->pfd
);
430 pango_layout_set_text (layout
, chart
->title
, -1);
431 pango_layout_get_size (layout
, &tw
, &th
);
432 chart
->title_zh
= (th
/ PANGO_SCALE
) + CHART_SPACING
;
433 DB( g_print(" - title: %s w=%d h=%d\n", chart
->title
, tw
, th
) );
437 chart
->subtitle_zh
= 0;
440 //pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_PERIOD * PANGO_SCALE);
441 ui_chart_progress_set_font_size(chart
, CHART_FONT_SIZE_SUBTITLE
);
442 pango_layout_set_font_description (layout
, chart
->pfd
);
444 pango_layout_set_text (layout
, chart
->subtitle
, -1);
445 pango_layout_get_size (layout
, &tw
, &th
);
446 chart
->subtitle_zh
= (th
/ PANGO_SCALE
) + CHART_SPACING
;
447 DB( g_print(" - period: %s w=%d h=%d\n", chart
->subtitle
, tw
, th
) );
450 // compute other text
451 //pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_NORMAL * PANGO_SCALE);
452 ui_chart_progress_set_font_size(chart
, CHART_FONT_SIZE_NORMAL
);
453 pango_layout_set_font_description (layout
, chart
->pfd
);
456 chart
->bud_col_w
= 0;
457 chart
->rel_col_w
= 0;
459 gdouble maxbudget
= 0;
460 gdouble maxresult
= 0;
462 for(i
=0;i
<chart
->nb_items
;i
++)
464 StackItem
*item
= &g_array_index(chart
->items
, StackItem
, i
);
467 if( item
->label
!= NULL
)
469 pango_layout_set_text (layout
, item
->label
, -1);
470 pango_layout_get_size (layout
, &tw
, &th
);
471 title_w
= MAX(title_w
, (tw
/ PANGO_SCALE
));
474 DB( g_print(" - calc '%s' title_w=%f (w=%d)\n", item
->label
, title_w
, tw
) );
476 //result = ABS(chart->spent[i]) - ABS(chart->budget[i]);
477 result
= ABS(item
->spent
- item
->budget
);
479 maxbudget
= MAX(maxbudget
, ABS(item
->budget
) );
480 maxresult
= MAX(maxresult
, result
);
482 DB( g_print(" - maxbudget maxbudget=%f (w=%d)\n", maxbudget
, tw
) );
484 if( item
->status
!= NULL
)
486 pango_layout_set_text (layout
, item
->status
, -1);
487 pango_layout_get_size (layout
, &tw
, &th
);
488 chart
->rel_col_w
= MAX(chart
->rel_col_w
, (tw
/ PANGO_SCALE
));
492 chart
->rel_col_w
+= CHART_SPACING
;
494 // compute budget/result width
495 valstr
= ui_chart_progress_print_int(chart
, -maxbudget
);
496 pango_layout_set_text (layout
, valstr
, -1);
497 pango_layout_get_size (layout
, &tw
, &th
);
498 chart
->bud_col_w
= (tw
/ PANGO_SCALE
);
499 pango_layout_set_text (layout
, chart
->budget_title
, -1);
500 pango_layout_get_size (layout
, &tw
, &th
);
501 chart
->bud_col_w
= MAX(chart
->bud_col_w
, (tw
/ PANGO_SCALE
));
502 DB( g_print(" - budget-col: w=%f, %.2f, '%s'\n", chart
->bud_col_w
, maxbudget
, valstr
) );
505 valstr
= ui_chart_progress_print_int(chart
, -maxresult
);
506 pango_layout_set_text (layout
, valstr
, -1);
507 pango_layout_get_size (layout
, &tw
, &th
);
508 chart
->res_col_w
= (tw
/ PANGO_SCALE
);
509 pango_layout_set_text (layout
, chart
->result_title
, -1);
510 pango_layout_get_size (layout
, &tw
, &th
);
511 chart
->res_col_w
= MAX(chart
->res_col_w
, (tw
/ PANGO_SCALE
));
512 DB( g_print(" - result-col: w=%f, %.2f, '%s'\n", chart
->res_col_w
, maxresult
, valstr
) );
515 // collect other width, add margins
516 chart
->header_zh
= (th
/ PANGO_SCALE
) + CHART_SPACING
;
517 chart
->cat_col_w
= title_w
+ CHART_SPACING
;
519 //chart->title_y = chart->t;
520 chart
->subtitle_y
= chart
->t
+ chart
->title_zh
;
521 chart
->header_y
= chart
->subtitle_y
+ chart
->subtitle_zh
;
524 chart
->graph_width
= chart
->w
- chart
->cat_col_w
- chart
->bud_col_w
- chart
->res_col_w
- chart
->rel_col_w
- (double)(CHART_SPACING
*4);
525 chart
->graph_height
= chart
->h
- chart
->title_zh
- chart
->subtitle_zh
-chart
->header_zh
;
528 DB( g_print("gfx_w = %.2f - %.2f - %.2f - %.2f - %.2f \n",
529 chart
->w
, chart
->cat_col_w
, chart
->bud_col_w
, chart
->res_col_w
, (double)(CHART_SPACING
*4)) );
531 DB( g_print("gfx_w = %.2f\n", chart
->graph_width
) );
533 //if expand : we compute available space
534 //chart->barw = MAX(32, (chart->graph_width)/chart->nb_items);
535 //chart->barw = 32; // usr setted or defaut to BARW
537 blkw
= chart
->barw
+ floor(chart
->barw
* 0.2);
540 chart
->visible
= (chart
->graph_height
- chart
->t
) / blkw
;
541 chart
->visible
= MIN(chart
->visible
, chart
->nb_items
);
543 g_object_unref (layout
);
546 cairo_surface_destroy(surf
);
551 #if (DBGDRAW_RECT + DBGDRAW_TEXT + DBGDRAW_ITEM) > 0
552 static void ui_chart_progress_draw_help(GtkWidget
*widget
, gpointer user_data
)
554 ChartProgress
*chart
= GTK_CHARTPROGRESS(user_data
);
560 DB( g_print("\n[chartstack] draw help\n") );
563 //cr = gdk_cairo_create (gtk_widget_get_window(widget));
564 cr
= cairo_create (chart
->surface
);
566 cairo_set_line_width (cr
, 1);
568 #if DBGDRAW_RECT == 1
570 cairo_set_line_width(cr
, 1.0);
571 cairo_set_source_rgb(cr
, 0.0, 1.0, 0.0); //green
572 cairo_rectangle(cr
, chart
->l
+0.5, chart
->t
+0.5, chart
->w
, chart
->h
);
576 y
= chart
->header_y
+ chart
->header_zh
;
577 cairo_set_source_rgb(cr
, 1.0, 0.5, 0.0); //orange
578 cairo_rectangle(cr
, chart
->l
+chart
->cat_col_w
+0.5, y
+0.5, chart
->graph_width
+0.5, chart
->graph_height
+0.5);
582 #if DBGDRAW_TEXT == 1
584 cairo_set_source_rgb(cr
, .0, .0, 1.0);
585 cairo_rectangle(cr
, chart
->l
+0.5, chart
->t
+0.5, chart
->w
, chart
->title_zh
);
589 cairo_set_source_rgb(cr
, .0, 0, 1.0);
590 cairo_rectangle(cr
, chart
->l
+0.5, chart
->subtitle_y
+0.5, chart
->w
, chart
->subtitle_zh
);
594 cairo_set_source_rgb(cr
, .0, 1.0, 1.0);
595 cairo_rectangle(cr
, chart
->l
+0.5, chart
->header_y
+0.5, chart
->w
, chart
->header_zh
);
599 y
= chart
->t
+ chart
->title_zh
+ chart
->header_zh
+ chart
->subtitle_zh
;
600 cairo_set_source_rgb(cr
, 0.0, 0.0, 1.0); //blue
601 cairo_rectangle(cr
, chart
->l
+0.5, y
+0.5, chart
->cat_col_w
, chart
->h
- y
);
605 x
= chart
->l
+ chart
->cat_col_w
+ chart
->graph_width
+ CHART_SPACING
;
606 cairo_set_source_rgb(cr
, 0.0, 0.0, 1.0); //blue
607 cairo_rectangle(cr
, x
+0.5, y
+0.5, chart
->bud_col_w
, chart
->h
- y
);
611 x
= chart
->l
+ chart
->cat_col_w
+ chart
->graph_width
+ chart
->bud_col_w
+ (CHART_SPACING
*3);
612 cairo_set_source_rgb(cr
, 0.0, 0.0, 1.0); //blue
613 cairo_rectangle(cr
, x
+0.5, y
+0.5, chart
->res_col_w
, chart
->h
- y
);
619 #if DBGDRAW_ITEM == 1
621 cairo_set_line_width(cr
, 1.0);
624 cairo_set_dash (cr
, &dashlength
, 1, 0);
625 cairo_set_source_rgb(cr
, 1.0, 0.0, 1.0); // violet
626 for(i
=first
; i
<=(first
+chart
->visible
) ;i
++)
628 cairo_move_to(cr
, chart
->l
, y2
);
629 cairo_line_to(cr
, chart
->r
, y2
);
644 ** draw all visible bars
646 static void ui_chart_progress_draw_bars(ChartProgress
*chart
, cairo_t
*cr
)
648 double x
, y
, x2
, y2
, h
;
655 DB( g_print("\n[chartstack] bar draw bars\n") );
657 layout
= pango_cairo_create_layout (cr
);
659 x
= chart
->l
+ chart
->cat_col_w
;
660 y
= chart
->t
+ chart
->title_zh
+ chart
->header_zh
+ chart
->subtitle_zh
;
661 first
= (gint
)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart
->adjustment
));
666 //pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_TITLE * PANGO_SCALE);
667 ui_chart_progress_set_font_size(chart
, CHART_FONT_SIZE_TITLE
);
668 pango_layout_set_font_description (layout
, chart
->pfd
);
669 pango_layout_set_text (layout
, chart
->title
, -1);
670 pango_layout_get_size (layout
, &tw
, &th
);
672 cairo_user_set_rgbcol(cr
, &global_colors
[THTEXT
]);
673 cairo_move_to(cr
, chart
->l
, chart
->t
);
674 pango_cairo_show_layout (cr
, layout
);
680 //pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_PERIOD * PANGO_SCALE);
681 ui_chart_progress_set_font_size(chart
, CHART_FONT_SIZE_SUBTITLE
);
682 pango_layout_set_font_description (layout
, chart
->pfd
);
683 pango_layout_set_text (layout
, chart
->subtitle
, -1);
684 pango_layout_get_size (layout
, &tw
, &th
);
686 cairo_user_set_rgbcol(cr
, &global_colors
[THTEXT
]);
687 cairo_move_to(cr
, chart
->l
, chart
->subtitle_y
);
688 pango_cairo_show_layout (cr
, layout
);
692 //cairo_user_set_rgbcol(cr, &global_colors[BLACK]);
693 cairo_user_set_rgbacol (cr
, &global_colors
[THTEXT
], 0.78);
694 //pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_NORMAL * PANGO_SCALE);
695 ui_chart_progress_set_font_size(chart
, CHART_FONT_SIZE_NORMAL
);
696 pango_layout_set_font_description (layout
, chart
->pfd
);
698 pango_layout_set_text (layout
, chart
->budget_title
, -1);
699 pango_layout_get_size (layout
, &tw
, &th
);
700 cairo_move_to(cr
, chart
->l
+ chart
->cat_col_w
+ chart
->graph_width
+ chart
->bud_col_w
+ CHART_SPACING
- (tw
/PANGO_SCALE
), chart
->header_y
);
701 pango_cairo_show_layout (cr
, layout
);
703 pango_layout_set_text (layout
, chart
->result_title
, -1);
704 pango_layout_get_size (layout
, &tw
, &th
);
705 cairo_move_to(cr
, chart
->l
+ chart
->cat_col_w
+ chart
->graph_width
+ chart
->bud_col_w
+ chart
->res_col_w
- (tw
/PANGO_SCALE
) + (CHART_SPACING
*3), chart
->header_y
);
706 pango_cairo_show_layout (cr
, layout
);
710 //pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_NORMAL * PANGO_SCALE);
711 ui_chart_progress_set_font_size(chart
, CHART_FONT_SIZE_NORMAL
);
712 pango_layout_set_font_description (layout
, chart
->pfd
);
714 for(i
=0; i
<chart
->visible
;i
++)
717 gint barw
= chart
->barw
;
718 gint blkw
= chart
->blkw
;
722 item
= &g_array_index(chart
->items
, StackItem
, idx
);
725 y2
= y
+ (CHART_SPACING
/2) + (blkw
* i
);
727 DB( g_print("'%-32s' wrn=%d %.2f%% (%.2f%%) :: r=% 4.2f s=% 4.2f b=% 4.2f\n",
728 item
->label
, item
->warn
, item
->rawrate
, item
->rate
, item
->result
, item
->spent
, item
->budget
) );
730 valstr
= item
->label
;
731 pango_layout_set_text (layout
, valstr
, -1);
732 pango_layout_get_size (layout
, &tw
, &th
);
734 double ytext
= y2
+ ((barw
- (th
/ PANGO_SCALE
))/2);
736 //cairo_user_set_rgbcol(cr, &global_colors[BLACK]);
737 cairo_user_set_rgbacol (cr
, &global_colors
[THTEXT
], 0.78);
738 cairo_move_to(cr
, chart
->l
+ chart
->cat_col_w
- (tw
/ PANGO_SCALE
) - CHART_SPACING
, ytext
);
739 pango_cairo_show_layout (cr
, layout
);
742 cairo_user_set_rgbacol(cr
, &global_colors
[THTEXT
], 0.15);
743 cairo_rectangle(cr
, x2
, y2
, chart
->graph_width
, barw
);
746 //bar with color :: todo migrate this
747 h
= floor(item
->rate
* chart
->graph_width
);
750 cairo_user_set_rgbcol_over(cr
, &chart
->color_scheme
.colors
[chart
->color_scheme
.cs_red
], idx
== chart
->active
);
754 if(item
->rate
> 0.8 && item
->rate
< 1.0)
755 cairo_user_set_rgbcol_over(cr
, &chart
->color_scheme
.colors
[chart
->color_scheme
.cs_orange
], idx
== chart
->active
);
757 cairo_user_set_rgbcol_over(cr
, &chart
->color_scheme
.colors
[chart
->color_scheme
.cs_green
], idx
== chart
->active
);
761 cairo_rectangle(cr
, x2
, y2
, h
, barw
);
765 if( item
->result
!= 0)
767 valstr
= ui_chart_progress_print_int(chart
, item
->spent
);
768 pango_layout_set_text (layout
, valstr
, -1);
769 pango_layout_get_size (layout
, &tw
, &th
);
771 if( h
>= ( (tw
/ PANGO_SCALE
) + (CHART_SPACING
*2)) )
774 cairo_user_set_rgbcol(cr
, &global_colors
[WHITE
]);
775 //cairo_user_set_rgbcol (cr, &global_colors[THBASE]);
776 cairo_move_to(cr
, x2
+ h
- (tw
/ PANGO_SCALE
) - CHART_SPACING
, ytext
);
781 //cairo_user_set_rgbcol(cr, &global_colors[TEXT]);
782 cairo_user_set_rgbacol (cr
, &global_colors
[THTEXT
], 0.78);
783 cairo_move_to(cr
, x2
+ h
+ CHART_SPACING
, ytext
);
786 pango_cairo_show_layout (cr
, layout
);
790 valstr
= ui_chart_progress_print_int(chart
, item
->budget
);
791 pango_layout_set_text (layout
, valstr
, -1);
792 pango_layout_get_size (layout
, &tw
, &th
);
793 //cairo_user_set_rgbcol(cr, &global_colors[BLACK]);
794 cairo_user_set_rgbacol (cr
, &global_colors
[THTEXT
], 0.78);
795 cairo_move_to(cr
, chart
->l
+ chart
->cat_col_w
+ chart
->graph_width
+ chart
->bud_col_w
+ CHART_SPACING
- (tw
/ PANGO_SCALE
), ytext
);
796 pango_cairo_show_layout (cr
, layout
);
800 if( item
->result
!= 0)
802 valstr
= ui_chart_progress_print_int(chart
, item
->result
);
805 //cairo_set_source_rgb(cr, COLTOCAIRO(164), COLTOCAIRO(0), COLTOCAIRO(0));
806 cairo_user_set_rgbcol(cr
, &chart
->color_scheme
.colors
[chart
->color_scheme
.cs_red
]);
808 //cairo_user_set_rgbcol(cr, &global_colors[TEXT]);
809 cairo_user_set_rgbacol (cr
, &global_colors
[THTEXT
], 0.78);
811 pango_layout_set_text (layout
, valstr
, -1);
812 pango_layout_get_size (layout
, &tw
, &th
);
813 cairo_move_to(cr
, chart
->l
+ chart
->cat_col_w
+ chart
->graph_width
+ chart
->bud_col_w
+ chart
->res_col_w
- (tw
/ PANGO_SCALE
) + (CHART_SPACING
*3), ytext
);
814 pango_cairo_show_layout (cr
, layout
);
819 pango_layout_set_text (layout
, item
->status
, -1);
820 pango_layout_get_size (layout
, &tw
, &th
);
821 cairo_move_to(cr
, chart
->l
+ chart
->cat_col_w
+ chart
->graph_width
+ chart
->bud_col_w
+ chart
->res_col_w
+ (CHART_SPACING
*4), ytext
);
822 pango_cairo_show_layout (cr
, layout
);
830 g_object_unref (layout
);
836 ** get the bar under the mouse pointer
838 static gint
ui_chart_progress_get_active(GtkWidget
*widget
, gint x
, gint y
, gpointer user_data
)
840 ChartProgress
*chart
= GTK_CHARTPROGRESS(user_data
);
841 gint retval
, first
, index
, py
;
842 gint blkw
= chart
->blkw
;
845 DB( g_print("\n[chartstack] get active\n") );
849 oy
= chart
->t
+ chart
->title_zh
+ chart
->header_zh
+ chart
->subtitle_zh
;
851 //DB( g_print(" y=%d, oy=%f, cb=%f\n", y, oy, chart->b) );
853 if( (y
<= chart
->b
&& y
>= oy
) && (x
>= chart
->l
&& x
<= chart
->r
) )
855 first
= gtk_adjustment_get_value(GTK_ADJUSTMENT(chart
->adjustment
));
857 index
= first
+ (py
/ blkw
);
860 if(index
< chart
->nb_items
)
863 DB( g_print(" hover=%d\n", retval
) );
870 static void ui_chart_progress_first_changed( GtkAdjustment
*adj
, gpointer user_data
)
872 ChartProgress
*chart
= GTK_CHARTPROGRESS(user_data
);
875 DB( g_print("\n[chartstack] bar first changed\n") );
877 //first = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj));
879 //DB( g_print(" first=%d\n", first) );
882 DB( g_print("scrollbar\n adj=%8x, low=%.2f upp=%.2f val=%.2f step=%.2f page=%.2f size=%.2f\n", adj,
883 adj->lower, adj->upper, adj->value, adj->step_increment, adj->page_increment, adj->page_size) );
885 /* Set the number of decimal places to which adj->value is rounded */
886 //gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value);
887 //gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value);
888 drawarea_full_redraw (chart
->drawarea
, chart
);
889 gtk_widget_queue_draw(chart
->drawarea
);
894 ** scrollbar set values for upper, page size, and also show/hide
896 static void ui_chart_progress_scrollbar_setvalues(ChartProgress
*chart
)
898 GtkAdjustment
*adj
= chart
->adjustment
;
901 g_return_if_fail (GTK_IS_ADJUSTMENT (adj
));
903 DB( g_print("\n[chartstack] sb_set_values\n") );
905 //if(visible < entries)
907 first
= gtk_adjustment_get_value(GTK_ADJUSTMENT(adj
));
909 DB( g_print(" entries=%d, visible=%d\n", chart
->nb_items
, chart
->visible
) );
910 DB( g_print(" first=%d, upper=%d, pagesize=%d\n", first
, chart
->nb_items
, chart
->visible
) );
912 gtk_adjustment_set_upper(adj
, (gdouble
)chart
->nb_items
);
913 gtk_adjustment_set_page_size(adj
, (gdouble
)chart
->visible
);
914 gtk_adjustment_set_page_increment(adj
, (gdouble
)chart
->visible
);
916 if(first
+chart
->visible
> chart
->nb_items
)
918 gtk_adjustment_set_value(adj
, (gdouble
)chart
->nb_items
- chart
->visible
);
921 gtk_adjustment_changed (adj
);
923 //gtk_widget_show(GTK_WIDGET(scrollbar));
926 //gtk_widget_hide(GTK_WIDGET(scrollbar));
932 static void ui_chart_progress_recompute(ChartProgress
*chart
)
935 DB( g_print("\n[chartstack] recompute\n") );
937 ui_chart_progress_calculation(chart
);
938 gtk_adjustment_set_value(chart
->adjustment
, 0);
939 ui_chart_progress_scrollbar_setvalues(chart
);
940 gtk_widget_show(chart
->scrollbar
);
942 gtk_widget_queue_draw( chart
->drawarea
);
946 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
947 static gboolean
drawarea_full_redraw(GtkWidget
*widget
, gpointer user_data
)
949 ChartProgress
*chart
= GTK_CHARTPROGRESS(user_data
);
952 DB( g_print("\n[gtkchart] drawarea full redraw\n") );
954 cr
= cairo_create (chart
->surface
);
956 /* fillin the back in white */
957 //cairo_user_set_rgbcol(cr, &global_colors[WHITE]);
958 cairo_user_set_rgbcol(cr
, &global_colors
[THBASE
]);
961 if(chart
->nb_items
== 0)
967 cairo_rectangle(cr
, chart
->l
, chart
->t
, chart
->w
, chart
->h
);
971 #if (DBGDRAW_RECT + DBGDRAW_TEXT + DBGDRAW_ITEM) > 0
972 ui_chart_progress_draw_help(widget
, user_data
);
975 ui_chart_progress_draw_bars(chart
, cr
);
984 drawarea_configure_event_callback (GtkWidget
*widget
,
985 GdkEventConfigure
*event
,
988 ChartProgress
*chart
= GTK_CHARTPROGRESS(user_data
);
989 GtkAllocation allocation
;
990 GtkStyleContext
*context
;
991 PangoFontDescription
*desc
;
995 DB( g_print("\n[chartstack] drawarea configure \n") );
997 DB( g_print("w=%d h=%d\n", allocation
.width
, allocation
.height
) );
999 gtk_widget_get_allocation (widget
, &allocation
);
1002 cairo_surface_destroy (chart
->surface
);
1004 chart
->surface
= gdk_window_create_similar_surface (gtk_widget_get_window (widget
),
1005 CAIRO_CONTENT_COLOR
,
1009 context
= gtk_widget_get_style_context (widget
);
1011 chart_color_global_default();
1014 colfound
= gtk_style_context_lookup_color(context
, "theme_base_color", &color
);
1016 colfound
= gtk_style_context_lookup_color(context
, "base_color", &color
);
1020 struct rgbcol
*tcol
= &global_colors
[THBASE
];
1021 tcol
->r
= color
.red
* 255;
1022 tcol
->g
= color
.green
* 255;
1023 tcol
->b
= color
.blue
* 255;
1024 DB( g_print(" - theme base col: %x %x %x\n", tcol
->r
, tcol
->g
, tcol
->b
) );
1028 colfound
= gtk_style_context_lookup_color(context
, "theme_text_color", &color
);
1030 gtk_style_context_lookup_color(context
, "text_color", &color
);
1034 struct rgbcol
*tcol
= &global_colors
[THTEXT
];
1035 tcol
->r
= color
.red
* 255;
1036 tcol
->g
= color
.green
* 255;
1037 tcol
->b
= color
.blue
* 255;
1038 DB( g_print(" - theme text (bg) col: %x %x %x\n", tcol
->r
, tcol
->g
, tcol
->b
) );
1041 /* get and copy the font */
1042 gtk_style_context_get(context
, GTK_STATE_FLAG_NORMAL
, "font", &desc
, NULL
);
1045 pango_font_description_free (chart
->pfd
);
1048 chart
->pfd
= pango_font_description_copy(desc
);
1049 chart
->pfd_size
= pango_font_description_get_size (desc
) / PANGO_SCALE
;
1050 chart
->barw
= (6 + chart
->pfd_size
) * PHI
;
1052 //leak: we should free desc here ?
1053 //or no need to copy above ?
1054 //pango_font_description_free(desc);
1056 DB( g_print("family: %s\n", pango_font_description_get_family(chart
->pfd
) ) );
1057 DB( g_print("size : %d (%d)\n", chart
->pfd_size
, chart
->pfd_size
/PANGO_SCALE
) );
1058 DB( g_print("isabs : %d\n", pango_font_description_get_size_is_absolute (chart
->pfd
) ) );
1060 if( gtk_widget_get_realized(widget
) )
1062 ui_chart_progress_recompute(chart
);
1063 drawarea_full_redraw(widget
, user_data
);
1066 /* We've handled the configure event, no need for further processing. */
1071 static gboolean
drawarea_draw_callback(GtkWidget
*widget
, cairo_t
*cr
, gpointer user_data
)
1073 ChartProgress
*chart
= GTK_CHARTPROGRESS(user_data
);
1075 if( !gtk_widget_get_realized(widget
) || chart
->surface
== NULL
)
1078 //DB( g_print("\n[chartstack] drawarea draw cb\n") );
1080 cairo_set_source_surface (cr
, chart
->surface
, 0, 0);
1083 /* always redraw directly the active block */
1087 first
= (gint
)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart
->adjustment
));
1088 ox
= chart
->l
+ chart
->cat_col_w
;
1089 oy
= chart
->t
+ chart
->title_zh
+ chart
->header_zh
+ chart
->subtitle_zh
;
1092 if(chart
->active
!= -1)
1094 DB( g_print(" draw active\n") );
1096 cairo_rectangle(cr
, chart
->l
, chart
->t
, chart
->w
, chart
->h
);
1099 oy
+= CHART_SPACING
/2 + (chart
->active
- first
) * chart
->blkw
;
1100 //cairo_user_set_rgbacol (cr, &global_colors[THTEXT], 0.78);
1101 cairo_user_set_rgbacol(cr
, &global_colors
[WHITE
], OVER_ALPHA
);
1102 //cairo_move_to(cr, chart->l, chart->t);
1103 cairo_rectangle(cr
, ox
, oy
, chart
->graph_width
, chart
->barw
);
1111 static gboolean
drawarea_motionnotifyevent_callback(GtkWidget
*widget
, GdkEventMotion
*event
, gpointer user_data
)
1113 ChartProgress
*chart
= GTK_CHARTPROGRESS(user_data
);
1116 if(chart
->surface
== NULL
|| chart
->nb_items
== 0)
1119 DB( g_print("\n[chartstack] drawarea motion cb\n") );
1126 //DB( g_print(" is hint\n") );
1128 gdk_window_get_device_position(event
->window
, event
->device
, &x
, &y
, NULL
);
1129 //gdk_window_get_pointer(event->window, &x, &y, NULL);
1133 chart
->active
= ui_chart_progress_get_active(widget
, x
, y
, chart
);
1136 // rollover redraw ?
1137 DB( g_print(" %d, %d :: active: last=%d, curr=%d\n", x
, y
, chart
->lastactive
, chart
->active
) );
1139 if(chart
->lastactive
!= chart
->active
)
1141 GdkRectangle update_rect
;
1145 DB( g_print(" motion rollover redraw :: active=%d\n", chart
->active
) );
1147 first
= (gint
)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart
->adjustment
));
1148 oy
= chart
->t
+ chart
->title_zh
+ chart
->header_zh
+ chart
->subtitle_zh
;
1150 if(chart
->lastactive
!= -1)
1152 update_rect
.x
= chart
->l
;
1153 update_rect
.y
= oy
+ (chart
->lastactive
- first
) * chart
->blkw
;
1154 update_rect
.width
= chart
->r
;
1155 update_rect
.height
= chart
->blkw
;
1157 /* Now invalidate the affected region of the drawing area. */
1158 gdk_window_invalidate_rect (gtk_widget_get_window (widget
),
1163 update_rect
.x
= chart
->l
;
1164 update_rect
.y
= oy
+ (chart
->active
- first
) * chart
->blkw
;
1165 update_rect
.width
= chart
->r
;
1166 update_rect
.height
= chart
->blkw
;
1168 /* Now invalidate the affected region of the drawing area. */
1169 gdk_window_invalidate_rect (gtk_widget_get_window (widget
),
1173 //gtk_widget_queue_draw( widget );
1177 chart
->lastactive
= chart
->active
;
1183 static gboolean
drawarea_scroll_event_callback( GtkWidget
*widget
, GdkEventScroll
*event
, gpointer user_data
)
1185 ChartProgress
*chart
= GTK_CHARTPROGRESS(user_data
);
1186 GtkAdjustment
*adj
= chart
->adjustment
;
1187 gdouble first
, upper
, pagesize
;
1189 DB( g_print("\n[chartstack] scroll\n") );
1191 first
= gtk_adjustment_get_value(GTK_ADJUSTMENT(adj
));
1192 //lower = gtk_adjustment_get_lower(GTK_ADJUSTMENT(adj));
1193 upper
= gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj
));
1194 pagesize
= gtk_adjustment_get_page_size(GTK_ADJUSTMENT(adj
));
1196 DB( g_print("- pos is %.2f, [%.2f - %.2f]\n", first
, 0.0, upper
) );
1198 switch(event
->direction
)
1201 gtk_adjustment_set_value(adj
, first
- 1);
1203 case GDK_SCROLL_DOWN
:
1204 gtk_adjustment_set_value(adj
, CLAMP(first
+ 1, 0, upper
- pagesize
) );
1210 drawarea_full_redraw(widget
, user_data
);
1216 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1217 /* public functions */
1218 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1220 static void ui_chart_progress_queue_redraw(ChartProgress
*chart
)
1223 if( gtk_widget_get_realized(GTK_WIDGET(chart
)) )
1225 ui_chart_progress_recompute(chart
);
1226 drawarea_full_redraw(chart
->drawarea
, chart
);
1227 //gtk_widget_queue_draw( chart->drawarea );
1232 ** change the model and/or column
1234 void ui_chart_progress_set_dualdatas(ChartProgress
*chart
, GtkTreeModel
*model
, gchar
*coltitle1
, gchar
*coltitle2
, gchar
*title
, gchar
*subtitle
)
1236 g_return_if_fail (GTK_IS_CHARTPROGRESS (chart
));
1238 if( GTK_IS_TREE_MODEL(model
) )
1240 ui_chart_progress_setup_with_model(chart
, model
, coltitle1
, coltitle2
);
1242 chart
->title
= g_strdup(title
);
1243 if(subtitle
!= NULL
)
1244 chart
->subtitle
= g_strdup(subtitle
);
1246 ui_chart_progress_queue_redraw(chart
);
1250 ui_chart_progress_clear(chart
, TRUE
);
1255 ** change the tooltip title
1257 void ui_chart_progress_set_title(ChartProgress
* chart
, gchar
*title
)
1259 g_return_if_fail (GTK_IS_CHARTPROGRESS (chart
));
1261 chart
->title
= g_strdup(title
);
1263 DB( g_print("\n[chartstack] set title = %s\n", chart
->title
) );
1265 ui_chart_progress_recompute(chart
);
1269 void ui_chart_progress_set_subtitle(ChartProgress
* chart
, gchar
*subtitle
)
1271 g_return_if_fail (GTK_IS_CHARTPROGRESS (chart
));
1273 chart
->subtitle
= g_strdup(subtitle
);
1275 DB( g_print("\n[chartstack] set period = %s\n", chart
->subtitle
) );
1277 ui_chart_progress_recompute(chart
);
1283 ** change the minor visibility
1285 void ui_chart_progress_show_minor(ChartProgress
* chart
, gboolean minor
)
1287 g_return_if_fail (GTK_IS_CHARTPROGRESS (chart
));
1289 chart
->minor
= minor
;
1291 ui_chart_progress_queue_redraw(chart
);
1295 void ui_chart_progress_set_color_scheme(ChartProgress
* chart
, gint index
)
1297 colorscheme_init(&chart
->color_scheme
, index
);
1302 ** set the minor parameters
1304 /*void ui_chart_progress_set_minor_prefs(ChartProgress * chart, gdouble rate, gchar *symbol)
1306 g_return_if_fail (GTK_IS_CHARTPROGRESS (chart));
1308 chart->minor_rate = rate;
1309 chart->minor_symbol = symbol;
1312 void ui_chart_progress_set_currency(ChartProgress
* chart
, guint32 kcur
)
1314 g_return_if_fail (GTK_IS_CHARTPROGRESS (chart
));