25#include "gnc-dense-cal.h"
26#include "gnc-dense-cal-model.h"
30#include <glib/gi18n.h>
35#include "dialog-utils.h"
38static const QofLogModule log_module =
"gnc.gui.dense-cal";
71static const int DENSE_CAL_DEFAULT_WIDTH = 15;
72static const int DENSE_CAL_DEFAULT_HEIGHT = 105;
73static const int MINOR_BORDER_SIZE = 1;
74static const int COL_BORDER_SIZE = 3;
76static void gnc_dense_cal_finalize (GObject *
object);
77static void gnc_dense_cal_dispose (GObject *
object);
78static void gnc_dense_cal_realize (GtkWidget *widget, gpointer user_data);
79static void gnc_dense_cal_configure (GtkWidget *widget,
80 GdkEventConfigure *event,
82static void gnc_dense_cal_draw_to_buffer (GncDenseCal *dcal);
83static gboolean gnc_dense_cal_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data);
85static void gdc_reconfig (GncDenseCal *dcal);
87static void gdc_free_all_mark_data (GncDenseCal *dcal);
89static void _gdc_compute_min_size (GncDenseCal *dcal,
90 guint *min_width, guint *min_height);
91static void _gdc_set_cal_min_size_req (GncDenseCal *dcal);
92static gint gnc_dense_cal_motion_notify (GtkWidget *widget,
93 GdkEventMotion *event);
94static gint gnc_dense_cal_button_press (GtkWidget *widget,
97static void _gdc_view_option_changed (GtkComboBox *widget, gpointer user_data);
99static inline int day_width_at (GncDenseCal *dcal, guint xScale);
100static inline int day_width (GncDenseCal *dcal);
101static inline int day_height_at (GncDenseCal *dcal, guint yScale);
102static inline int day_height (GncDenseCal *dcal);
103static inline int week_width_at (GncDenseCal *dcal, guint xScale);
104static inline int week_width (GncDenseCal *dcal);
105static inline int week_height_at (GncDenseCal *dcal, guint yScale);
106static inline int week_height (GncDenseCal *dcal);
107static inline int col_width_at (GncDenseCal *dcal, guint xScale);
108static inline int col_width (GncDenseCal *dcal);
110static inline int col_height (GncDenseCal *dcal);
111static inline int num_cols (GncDenseCal *dcal);
113static void _gnc_dense_cal_set_month (GncDenseCal *dcal, GDateMonth mon, gboolean redraw);
114static void _gnc_dense_cal_set_year (GncDenseCal *dcal, guint year, gboolean redraw);
120static inline int num_weeks (GncDenseCal *dcal);
125static int num_weeks_per_col (GncDenseCal *dcal);
128static gint wheres_this (GncDenseCal *dcal,
int x,
int y);
130static void recompute_x_y_scales (GncDenseCal *dcal);
131static void recompute_mark_storage (GncDenseCal *dcal);
132static void recompute_extents (GncDenseCal *dcal);
133static void populate_hover_window (GncDenseCal *dcal);
135static void month_coords (GncDenseCal *dcal,
int monthOfCal, GList **outList);
136static void doc_coords (GncDenseCal *dcal,
int dayOfCal,
137 int *x1,
int *y1,
int *x2,
int *y2);
139static void gdc_mark_add (GncDenseCal *dcal, guint tag, gchar *name,
140 gchar *info, guint size, GDate **dateArray);
141static void gdc_mark_remove (GncDenseCal *dcal, guint mark_to_remove, gboolean redraw);
143static void gdc_add_tag_markings (GncDenseCal *cal, guint tag);
144static void gdc_add_markings (GncDenseCal *cal);
145static void gdc_remove_markings (GncDenseCal *cal);
147typedef struct _gdc_month_coords
156 GtkComboBox *view_options;
157 GtkDrawingArea *cal_drawing_area;
159 cairo_surface_t *surface;
161 gboolean initialized;
164 GtkWindow *transPopup;
181 gint firstOfMonthOffset;
190 guint month_side_bar_width;
191 guint day_top_bar_height;
192 guint bar_label_padding;
194 GncDenseCalModel *model;
198 GDateWeekday day_of_week_start;
211typedef struct _gdc_mark_data
222G_DEFINE_TYPE(GncDenseCal, gnc_dense_cal, GTK_TYPE_BOX)
224#define MONTH_NAME_BUFSIZE 10
232 static gchar buf[MONTH_NAME_BUFSIZE];
234 gint arbitrary_year = 1977;
236 memset (buf, 0, MONTH_NAME_BUFSIZE);
237 g_date_clear (&date, 1);
239 g_date_set_year (&date, arbitrary_year);
240 g_date_set_day (&date, 1);
242 g_date_set_month (&date, mon + 1);
243 g_date_strftime (buf, MONTH_NAME_BUFSIZE,
"%b", &date);
251day_label (gchar *buf,
int buf_len,
int dow)
255 if (g_utf8_strlen (buf, -1) > 2)
257 gchar *pointer = g_utf8_offset_to_pointer (buf, 2);
263gnc_dense_cal_class_init (GncDenseCalClass *klass)
265 GObjectClass *object_class = G_OBJECT_CLASS(klass);
266 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
268 gtk_widget_class_set_css_name (GTK_WIDGET_CLASS(klass),
"calendar");
270 object_class->finalize = gnc_dense_cal_finalize;
271 object_class->dispose = gnc_dense_cal_dispose;
273 widget_class->motion_notify_event = gnc_dense_cal_motion_notify;
274 widget_class->button_press_event = gnc_dense_cal_button_press;
277enum _GdcViewOptsColumns
279 VIEW_OPTS_COLUMN_LABEL = 0,
280 VIEW_OPTS_COLUMN_NUM_MONTHS,
281 VIEW_OPTS_COLUMN_NUM_MONTHS_PER_COLUMN
284static GtkListStore *_cal_view_options = NULL;
286_gdc_get_view_options (
void)
288 if (_cal_view_options == NULL)
290 _cal_view_options = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
291 gtk_list_store_insert_with_values (_cal_view_options, NULL, G_MAXINT, 0,
_(
"12 months"), 1, 12, 2, 3, -1);
292 gtk_list_store_insert_with_values (_cal_view_options, NULL, G_MAXINT, 0,
_(
"6 months"), 1, 6, 2, 2, -1);
293 gtk_list_store_insert_with_values (_cal_view_options, NULL, G_MAXINT, 0,
_(
"4 months"), 1, 4, 2, 2, -1);
294 gtk_list_store_insert_with_values (_cal_view_options, NULL, G_MAXINT, 0,
_(
"3 months"), 1, 3, 2, 2, -1);
295 gtk_list_store_insert_with_values (_cal_view_options, NULL, G_MAXINT, 0,
_(
"2 months"), 1, 2, 2, 1, -1);
296 gtk_list_store_insert_with_values (_cal_view_options, NULL, G_MAXINT, 0,
_(
"1 month"), 1, 1, 2, 1, -1);
299 return _cal_view_options;
303gnc_dense_cal_init (GncDenseCal *dcal)
305 GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(dcal));
307 gtk_orientable_set_orientation (GTK_ORIENTABLE(dcal), GTK_ORIENTATION_VERTICAL);
310 gnc_widget_style_context_add_class (GTK_WIDGET(dcal),
"calendar");
313 gtk_widget_set_name (GTK_WIDGET(dcal),
"gnc-id-dense-calendar");
315 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CALENDAR);
317 GtkTreeModel *options = GTK_TREE_MODEL(_gdc_get_view_options());
318 GtkCellRenderer *text_rend = GTK_CELL_RENDERER(gtk_cell_renderer_text_new ());
320 dcal->view_options = GTK_COMBO_BOX(gtk_combo_box_new_with_model (options));
321 gtk_combo_box_set_active (GTK_COMBO_BOX(dcal->view_options), 0);
322 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(dcal->view_options), text_rend, TRUE);
323 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(dcal->view_options),
324 text_rend,
"text", VIEW_OPTS_COLUMN_LABEL);
325 g_signal_connect (G_OBJECT(dcal->view_options),
"changed",
326 G_CALLBACK(_gdc_view_option_changed), (gpointer)dcal);
330 GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
331 GtkWidget *label = gtk_label_new (
_(
"View"));
333 gtk_box_set_homogeneous (GTK_BOX(hbox), FALSE);
334 gtk_widget_set_halign (label, GTK_ALIGN_END);
335 gtk_widget_set_margin_end (label, 5);
336 gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
337 gtk_box_pack_start (GTK_BOX(hbox), GTK_WIDGET(dcal->view_options), FALSE, FALSE, 0);
339 gtk_box_pack_start (GTK_BOX(dcal), GTK_WIDGET(hbox), FALSE, FALSE, 0);
341 dcal->cal_drawing_area = GTK_DRAWING_AREA(gtk_drawing_area_new ());
343 gtk_widget_add_events (GTK_WIDGET(dcal->cal_drawing_area), (GDK_EXPOSURE_MASK
344 | GDK_BUTTON_PRESS_MASK
345 | GDK_BUTTON_RELEASE_MASK
346 | GDK_POINTER_MOTION_MASK
347 | GDK_POINTER_MOTION_HINT_MASK));
348 gtk_box_pack_start (GTK_BOX(dcal), GTK_WIDGET(dcal->cal_drawing_area), TRUE, TRUE, 0);
349 g_signal_connect (G_OBJECT(dcal->cal_drawing_area),
"draw",
350 G_CALLBACK(gnc_dense_cal_draw), (gpointer)dcal);
351 g_signal_connect (G_OBJECT(dcal->cal_drawing_area),
"realize",
352 G_CALLBACK(gnc_dense_cal_realize), (gpointer)dcal);
353 g_signal_connect (G_OBJECT(dcal->cal_drawing_area),
"configure_event",
354 G_CALLBACK(gnc_dense_cal_configure), (gpointer)dcal);
356 dcal->disposed = FALSE;
357 dcal->initialized = FALSE;
358 dcal->markData = NULL;
361 dcal->lastMarkTag = 0;
363 dcal->showPopup = FALSE;
365 dcal->transPopup = GTK_WINDOW(gtk_window_new (GTK_WINDOW_POPUP));
367 GtkWidget *vbox, *hbox;
369 GtkListStore *tree_data;
370 GtkTreeView *tree_view;
372 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
373 gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
374 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
375 gtk_box_set_homogeneous (GTK_BOX(hbox), FALSE);
377 gtk_widget_set_name (GTK_WIDGET(dcal->transPopup),
"gnc-id-dense-calendar-popup");
379 l = gtk_label_new (
_(
"Date: "));
380 gtk_widget_set_margin_start (l, 5);
381 gtk_container_add (GTK_CONTAINER(hbox), l);
382 l = gtk_label_new (
"YY/MM/DD");
383 g_object_set_data (G_OBJECT(dcal->transPopup),
"dateLabel", l);
384 gtk_container_add (GTK_CONTAINER(hbox), l);
385 gtk_container_add (GTK_CONTAINER(vbox), hbox);
387 gtk_container_add (GTK_CONTAINER(vbox), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
389 tree_data = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
390 tree_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model (GTK_TREE_MODEL(tree_data)));
391 gtk_tree_view_insert_column_with_attributes (tree_view, -1,
_(
"Name"),
392 gtk_cell_renderer_text_new (),
"text", 0, NULL);
393 gtk_tree_view_insert_column_with_attributes (tree_view, -1,
_(
"Frequency"),
394 gtk_cell_renderer_text_new (),
"text", 1, NULL);
395 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW(tree_view)), GTK_SELECTION_NONE);
396 g_object_set_data (G_OBJECT(dcal->transPopup),
"model", tree_data);
397 g_object_unref (tree_data);
399 gtk_container_add (GTK_CONTAINER(vbox), GTK_WIDGET(tree_view));
400 gtk_container_add (GTK_CONTAINER(dcal->transPopup), vbox);
402 gtk_window_set_resizable (GTK_WINDOW(dcal->transPopup), FALSE);
404 gtk_widget_realize (GTK_WIDGET(dcal->transPopup));
407 dcal->month = G_DATE_JANUARY;
410 dcal->numMonths = 12;
411 dcal->monthsPerCol = 3;
412 dcal->leftPadding = 4;
413 dcal->topPadding = 4;
417 g_date_clear (&now, 1);
419 _gnc_dense_cal_set_month (dcal, g_date_get_month (&now), FALSE);
420 _gnc_dense_cal_set_year (dcal, g_date_get_year (&now), FALSE);
423 recompute_extents (dcal);
424 recompute_mark_storage (dcal);
431 int width_88, height_88;
432 int width_XXX, height_XXX;
434 layout = gtk_widget_create_pango_layout (GTK_WIDGET(dcal), NULL);
436 pango_layout_set_text (layout,
"88", -1);
437 pango_layout_get_pixel_size (layout, &width_88, &height_88);
439 pango_layout_set_text (layout,
"XXX", -1);
440 pango_layout_get_pixel_size (layout, &width_XXX, &height_XXX);
442 dcal->min_x_scale = dcal->x_scale = width_88 + 2;
443 dcal->min_y_scale = dcal->y_scale = MAX(floor ((
float)width_XXX / 3.), height_88 + 2);
445 gtk_style_context_get_padding (context, GTK_STATE_FLAG_NORMAL, &padding);
446 if ((padding.top + padding.bottom) == 0)
447 dcal->bar_label_padding = 2;
449 dcal->bar_label_padding = (padding.top + padding.bottom) / 2;
451 dcal->month_side_bar_width = height_88 + (dcal->bar_label_padding * 2);
452 dcal->day_top_bar_height = height_88 + (dcal->bar_label_padding * 2);
454 g_object_unref (layout);
457 dcal->initialized = TRUE;
459 dcal->day_of_week_start = G_DATE_SUNDAY;
466 first_day = G_DATE_SUNDAY;
468 first_day = first_day - 1;
470 if (first_day > 0 && first_day < 8)
471 dcal->day_of_week_start = first_day;
473 gtk_widget_show_all (GTK_WIDGET(dcal));
477_gdc_set_cal_min_size_req (GncDenseCal *dcal)
479 guint min_width, min_height;
481 _gdc_compute_min_size (dcal, &min_width, &min_height);
482 gtk_widget_set_size_request (GTK_WIDGET(dcal->cal_drawing_area), min_width, min_height);
486gnc_dense_cal_new (GtkWindow *parent)
488 GncDenseCal *dcal = g_object_new (GNC_TYPE_DENSE_CAL, NULL);
490 gtk_window_set_transient_for (GTK_WINDOW(dcal->transPopup),
493 return GTK_WIDGET(dcal);
497gnc_dense_cal_new_with_model (GtkWindow *parent, GncDenseCalModel *model)
499 GncDenseCal *cal = GNC_DENSE_CAL(gnc_dense_cal_new (parent));
500 gnc_dense_cal_set_model (cal, model);
501 return GTK_WIDGET(cal);
505recompute_first_of_month_offset (GncDenseCal *dcal)
509 tmpDate = g_date_new_dmy (1, dcal->month, dcal->year);
510 dcal->firstOfMonthOffset = g_date_get_weekday (tmpDate) % 7;
511 g_date_free (tmpDate);
515gnc_dense_cal_set_month (GncDenseCal *dcal, GDateMonth mon)
517 _gnc_dense_cal_set_month (dcal, mon, TRUE);
521_gnc_dense_cal_set_month (GncDenseCal *dcal, GDateMonth mon, gboolean redraw)
523 if (dcal->month == mon)
528 recompute_first_of_month_offset (dcal);
530 recompute_extents (dcal);
532 if (redraw && gtk_widget_get_realized (GTK_WIDGET(dcal)))
534 recompute_x_y_scales (dcal);
535 gnc_dense_cal_draw_to_buffer (dcal);
536 gtk_widget_queue_draw (GTK_WIDGET(dcal->cal_drawing_area));
541gnc_dense_cal_set_year (GncDenseCal *dcal, guint year)
543 _gnc_dense_cal_set_year (dcal, year, TRUE);
547_gnc_dense_cal_set_year (GncDenseCal *dcal, guint year, gboolean redraw)
549 if (dcal->year == year)
552 recompute_first_of_month_offset (dcal);
553 recompute_extents (dcal);
554 if (redraw && gtk_widget_get_realized (GTK_WIDGET(dcal)))
556 recompute_x_y_scales (dcal);
557 gnc_dense_cal_draw_to_buffer (dcal);
558 gtk_widget_queue_draw (GTK_WIDGET(dcal->cal_drawing_area));
563gnc_dense_cal_set_num_months (GncDenseCal *dcal, guint num_months)
565 GtkListStore *options = _gdc_get_view_options ();
566 GtkTreeIter view_opts_iter, iter_closest_to_req;
567 int months_per_column = 0;
568 int closest_index_distance = G_MAXINT;
571 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL(options), &view_opts_iter))
573 g_critical (
"no view options?");
579 gint months_val, delta_months;
580 gtk_tree_model_get (GTK_TREE_MODEL(options), &view_opts_iter,
581 VIEW_OPTS_COLUMN_NUM_MONTHS, &months_val,
582 VIEW_OPTS_COLUMN_NUM_MONTHS_PER_COLUMN, &months_per_column,
585 delta_months = abs (months_val - (
int)num_months);
586 if (delta_months < closest_index_distance)
588 iter_closest_to_req = view_opts_iter;
589 closest_index_distance = delta_months;
592 while (closest_index_distance != 0
593 && (gtk_tree_model_iter_next (GTK_TREE_MODEL(options), &view_opts_iter)));
596 g_signal_handlers_block_by_func (dcal->view_options, _gdc_view_option_changed, dcal);
597 gtk_combo_box_set_active_iter (GTK_COMBO_BOX(dcal->view_options), &iter_closest_to_req);
598 g_signal_handlers_unblock_by_func (dcal->view_options, _gdc_view_option_changed, dcal);
601 if (months_per_column != 0)
602 dcal->monthsPerCol = months_per_column;
604 dcal->numMonths = num_months;
605 recompute_extents (dcal);
606 recompute_mark_storage (dcal);
607 if (gtk_widget_get_realized (GTK_WIDGET(dcal)))
609 recompute_x_y_scales (dcal);
610 gnc_dense_cal_draw_to_buffer (dcal);
611 gtk_widget_queue_draw (GTK_WIDGET(dcal->cal_drawing_area));
616gnc_dense_cal_get_num_months (GncDenseCal *dcal)
618 return dcal->numMonths;
622gnc_dense_cal_set_months_per_col (GncDenseCal *dcal, guint monthsPerCol)
624 dcal->monthsPerCol = monthsPerCol;
625 recompute_x_y_scales (dcal);
629gnc_dense_cal_get_month (GncDenseCal *dcal)
635gnc_dense_cal_get_year (GncDenseCal *dcal)
641gnc_dense_cal_dispose (GObject *
object)
644 g_return_if_fail (
object != NULL);
645 g_return_if_fail (GNC_IS_DENSE_CAL(
object));
647 dcal = GNC_DENSE_CAL(
object);
651 dcal->disposed = TRUE;
653 if (gtk_widget_get_realized (GTK_WIDGET(dcal->transPopup)))
655 gtk_widget_hide (GTK_WIDGET(dcal->transPopup));
656 gtk_widget_destroy (GTK_WIDGET(dcal->transPopup));
657 dcal->transPopup = NULL;
662 cairo_surface_destroy (dcal->surface);
663 dcal->surface = NULL;
668 gdc_free_all_mark_data (dcal);
670 g_object_unref (G_OBJECT(dcal->model));
672 G_OBJECT_CLASS(gnc_dense_cal_parent_class)->dispose(
object);
676gnc_dense_cal_finalize (GObject *
object)
678 g_return_if_fail (
object != NULL);
679 g_return_if_fail (GNC_IS_DENSE_CAL(
object));
681 G_OBJECT_CLASS(gnc_dense_cal_parent_class)->finalize(
object);
685gnc_dense_cal_configure (GtkWidget *widget,
686 GdkEventConfigure *event,
689 GncDenseCal *dcal = GNC_DENSE_CAL(user_data);
690 recompute_x_y_scales (dcal);
692 gtk_widget_queue_draw_area (widget,
694 event->width, event->height);
698gnc_dense_cal_realize (GtkWidget *widget, gpointer user_data)
702 g_return_if_fail (widget != NULL);
703 g_return_if_fail (GNC_IS_DENSE_CAL(user_data));
704 dcal = GNC_DENSE_CAL(user_data);
706 recompute_x_y_scales (dcal);
711gdc_reconfig (GncDenseCal *dcal)
718 cairo_surface_destroy (dcal->surface);
720 widget = GTK_WIDGET(dcal->cal_drawing_area);
721 gtk_widget_get_allocation (widget, &alloc);
722 scale = gtk_widget_get_scale_factor (widget);
725 dcal->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
727 alloc.height * scale);
728 cairo_surface_set_device_scale (dcal->surface, scale, scale);
729 gnc_dense_cal_draw_to_buffer (dcal);
733_gdc_compute_min_size (GncDenseCal *dcal, guint *min_width, guint *min_height)
735 if (min_width != NULL)
738 (dcal->leftPadding * 2)
739 + (num_cols (dcal) * (col_width_at (dcal, dcal->min_x_scale)
740 + dcal->month_side_bar_width))
741 + ((num_cols (dcal) - 1) * COL_BORDER_SIZE);
744 if (min_height != NULL)
747 (dcal->topPadding * 2)
749 + dcal->day_top_bar_height
750 + (num_weeks_per_col (dcal)
751 * week_height_at (dcal, dcal->min_y_scale));
756recompute_x_y_scales (GncDenseCal *dcal)
761 width = DENSE_CAL_DEFAULT_WIDTH;
762 height = DENSE_CAL_DEFAULT_HEIGHT;
763 if (dcal->initialized)
766 gtk_widget_get_allocation (GTK_WIDGET(dcal->cal_drawing_area), &alloc);
768 height = alloc.height;
773 denom = 7 * num_cols (dcal);
774 g_assert (denom != 0);
775 dcal->x_scale = ((gint)(width
776 - (dcal->leftPadding * 2)
777 - (num_cols (dcal) * ((8 * MINOR_BORDER_SIZE)
778 + dcal->month_side_bar_width))
779 - ((num_cols (dcal) - 1) * COL_BORDER_SIZE))
781 dcal->x_scale = MAX(dcal->x_scale, dcal->min_x_scale);
783 denom = num_weeks_per_col (dcal);
784 g_assert (denom != 0);
785 dcal->y_scale = ((gint)(height
786 - (dcal->topPadding * 2)
788 - dcal->day_top_bar_height
789 - (num_weeks_per_col (dcal) - 1
790 * MINOR_BORDER_SIZE))
792 dcal->y_scale = MAX(dcal->y_scale, dcal->min_y_scale);
794 _gdc_set_cal_min_size_req (dcal);
798gdc_free_all_mark_data (GncDenseCal *dcal)
802 for (i = 0; i < dcal->numMarks; i++)
806 g_list_free (dcal->marks[i]);
808 g_free (dcal->marks);
811 for (l = dcal->markData; l; l = l->next)
819 g_list_free (dcal->markData);
820 dcal->markData = NULL;
824recompute_mark_storage (GncDenseCal *dcal)
826 if (dcal->marks == NULL)
828 gdc_free_all_mark_data (dcal);
831 dcal->numMarks = num_weeks (dcal) * 7;
832 dcal->marks = g_new0 (GList*, dcal->numMarks);
834 gdc_add_markings (dcal);
838get_week_of_year (GncDenseCal *dcal, GDate *d)
840 GDateWeekday fwd, lwd;
845 gint monday_offset = 1;
848 g_return_val_if_fail (g_date_valid (d), 0);
850 year = g_date_get_year (d);
855 g_date_clear (&first, 1);
856 g_date_set_dmy (&first, 1, 1, year);
858 fwd = g_date_get_weekday (&first);
860 day_offset = (fwd + 7 - dcal->day_of_week_start) % 7;
862 if (dcal->day_of_week_start == G_DATE_SUNDAY)
864 else if (dcal->day_of_week_start == G_DATE_MONDAY)
866 else if (dcal->day_of_week_start == G_DATE_TUESDAY)
868 else if (dcal->day_of_week_start == G_DATE_WEDNESDAY)
870 else if (dcal->day_of_week_start == G_DATE_THURSDAY)
872 else if (dcal->day_of_week_start == G_DATE_FRIDAY)
874 else if (dcal->day_of_week_start == G_DATE_SATURDAY)
879 day = g_date_get_day_of_year (d) - 1;
881 g_date_clear (&last, 1);
882 g_date_set_dmy (&last, 31, 12, year - 1);
883 lwd = g_date_get_weekday (&last);
884 gint lday_offset = 6 - ((lwd + 7 - dcal->day_of_week_start) % 7);
890 ret = ((day + day_offset)/7U + ((day_offset <= monday_offset) ? addone : 0));
896get_weeks_in_year (GncDenseCal *dcal, GDateYear year)
900 g_return_val_if_fail (g_date_valid_year (year), 0);
902 g_date_clear (&d, 1);
903 g_date_set_dmy (&d, 1, 1, year);
904 if (g_date_get_weekday (&d) == dcal->day_of_week_start)
return 53;
905 g_date_set_dmy (&d, 31, 12, year);
906 if (g_date_get_weekday (&d) == dcal->day_of_week_start)
return 53;
907 if (g_date_is_leap_year (year))
909 g_date_set_dmy (&d, 2, 1, year);
910 if (g_date_get_weekday (&d) == dcal->day_of_week_start)
return 53;
911 g_date_set_dmy (&d, 30, 12, year);
912 if (g_date_get_weekday (&d) == dcal->day_of_week_start)
return 53;
918recompute_extents (GncDenseCal *dcal)
921 gint start_week, end_week;
923 g_date_clear (&date, 1);
924 g_date_set_dmy (&date, 1, dcal->month, dcal->year);
925 start_week = get_week_of_year (dcal, &date);
926 g_date_add_months (&date, dcal->numMonths);
927 end_week = get_week_of_year (dcal, &date);
929 if (g_date_get_year (&date) != dcal->year)
930 end_week += get_weeks_in_year (dcal, dcal->year);
932 dcal->num_weeks = end_week - start_week + 1;
936free_rect (gpointer data, gpointer user_data)
938 g_free ((GdkRectangle*)data);
942gnc_dense_cal_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data)
946 g_return_val_if_fail (widget != NULL, FALSE);
947 g_return_val_if_fail (GNC_IS_DENSE_CAL(user_data), FALSE);
949 dcal = GNC_DENSE_CAL(user_data);
952 cairo_set_source_surface (cr, dcal->surface, 0, 0);
959gnc_dense_cal_draw_to_buffer (GncDenseCal *dcal)
962 GtkStyleContext *stylectxt;
963 GtkStateFlags state_flags;
969 gchar *primary_color_class, *secondary_color_class, *marker_color_class;
972 widget = GTK_WIDGET(dcal);
977 cr = cairo_create (dcal->surface);
978 layout = gtk_widget_create_pango_layout (GTK_WIDGET(dcal), NULL);
980 gtk_widget_get_allocation (GTK_WIDGET(dcal->cal_drawing_area), &alloc);
981 stylectxt = gtk_widget_get_style_context (GTK_WIDGET(dcal->cal_drawing_area));
982 state_flags = gtk_style_context_get_state (stylectxt);
984 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_BACKGROUND);
985 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_CALENDAR);
987 gtk_render_background (stylectxt, cr, 0, 0,
991 gtk_style_context_remove_class (stylectxt, GTK_STYLE_CLASS_BACKGROUND);
996 gchar *class_extension = NULL;
998 gtk_style_context_get_color (stylectxt, GTK_STATE_FLAG_NORMAL, &color);
1001 class_extension =
"-dark";
1003 primary_color_class = g_strconcat (
"primary", class_extension, NULL);
1004 secondary_color_class = g_strconcat (
"secondary", class_extension, NULL);
1005 marker_color_class = g_strconcat (
"markers", class_extension, NULL);
1009 pango_layout_set_text (layout,
"S", -1);
1010 pango_layout_get_pixel_size (layout, NULL, &dcal->label_height);
1011 dcal->month_side_bar_width = dcal->label_height + (dcal->bar_label_padding * 2);
1012 dcal->day_top_bar_height = dcal->label_height + (dcal->bar_label_padding * 2);
1018 GList *mcList, *mcListIter;
1021 for (i = 0; i < 12; i++)
1023 dcal->monthPositions[i].x = dcal->monthPositions[i].y = -1;
1026 gtk_style_context_save (stylectxt);
1029 for (i = 0; i < dcal->numMonths; i++)
1032 month_coords (dcal, i, &mcList);
1033 dcal->monthPositions[i].x = floor (i / dcal->monthsPerCol)
1034 * (col_width (dcal) + COL_BORDER_SIZE);
1035 dcal->monthPositions[i].y = ((GdkRectangle*)mcList->next->next->next->data)->y;
1036 for (mcListIter = mcList; mcListIter != NULL; mcListIter = mcListIter->next)
1038 rect = (GdkRectangle*)mcListIter->data;
1039 gtk_style_context_save (stylectxt);
1042 gtk_style_context_add_class (stylectxt, primary_color_class);
1044 gtk_style_context_add_class (stylectxt, secondary_color_class);
1046 gtk_render_background (stylectxt, cr, rect->x, rect->y, rect->width, rect->height);
1047 gtk_style_context_restore (stylectxt);
1049 g_list_foreach (mcList, free_rect, NULL);
1050 g_list_free (mcList);
1052 gtk_style_context_restore (stylectxt);
1060 gtk_style_context_save (stylectxt);
1061 gtk_style_context_add_class (stylectxt, marker_color_class);
1062 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_VIEW);
1063 gtk_style_context_set_state (stylectxt, GTK_STATE_FLAG_SELECTED);
1065 for (i = 0; i < dcal->numMarks; i++)
1067 if (dcal->marks[i] != NULL)
1069 int center_x, center_y, radius;
1071 doc_coords(dcal, i, &x1, &y1, &x2, &y2);
1072 center_x = (x1 + x2 ) / 2;
1073 center_y = (y1 + y2 ) / 2;
1074 radius = MIN((x2 - x1), (y2 - y1)) * .75;
1077 if (((y2 - y1) % 2) != 0)
1078 center_y = center_y + 1;
1080 if (((x2 - x1) % 2) != 0)
1081 center_x = center_x + 1;
1083 gtk_render_background (stylectxt, cr,
1084 center_x - (radius + 2), center_y - radius,
1085 (radius * 2) + 4, radius * 2);
1088 gtk_style_context_restore (stylectxt);
1091 for (i = 0; i < num_cols (dcal); i++)
1098 gdk_rgba_parse (&color,
"black");
1100 x = dcal->leftPadding
1101 + (i * (col_width (dcal) + COL_BORDER_SIZE))
1102 + dcal->month_side_bar_width + 1;
1103 y = dcal->topPadding + dcal->day_top_bar_height;
1104 w = col_width (dcal) - COL_BORDER_SIZE - dcal->month_side_bar_width;
1105 h = col_height (dcal);
1107 gtk_style_context_save (stylectxt);
1110 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_FRAME);
1112 gtk_render_frame (stylectxt, cr, x, y, w + 1, h + 1);
1115 cairo_set_source_rgb (cr, color.red, color.green, color.blue);
1116 cairo_set_line_width (cr, 1);
1119 for (j = 0; j < num_weeks_per_col (dcal); j++)
1121 gint wy = y + (j * week_height (dcal));
1122 cairo_move_to (cr, x, wy + 0.5);
1123 cairo_line_to (cr, x + w, wy + 0.5);
1128 for (j = 1; j < 7; j++)
1130 gint dx = x + (j * day_width (dcal));
1131 cairo_move_to (cr, dx + 0.5, y);
1132 cairo_line_to (cr, dx + 0.5, y + col_height (dcal));
1136 gtk_style_context_restore (stylectxt);
1140 pango_layout_set_text (layout,
"88", -1);
1141 pango_layout_get_pixel_size (layout, &maxWidth, NULL);
1143 if (dcal->x_scale > maxWidth)
1145 gtk_style_context_save (stylectxt);
1146 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_HEADER);
1148 gtk_render_background (stylectxt, cr, x,
1149 y - dcal->day_top_bar_height,
1150 (day_width(dcal) * 7) + 1,
1151 dcal->day_top_bar_height);
1153 for (j = 0; j < 7; j++)
1155 int day_label_width;
1156 gint label_x_offset, label_y_offset;
1157 gint day_label_str_len = 4;
1158 gchar day_label_str[day_label_str_len + 1];
1159 day_label (day_label_str, day_label_str_len, (j + dcal->day_of_week_start) % 7);
1160 pango_layout_set_text (layout, day_label_str, -1);
1161 pango_layout_get_pixel_size (layout, &day_label_width, NULL);
1163 + (j * day_width (dcal))
1164 + (day_width (dcal) / 2)
1165 - (day_label_width / 2);
1166 label_y_offset = y - dcal->day_top_bar_height + dcal->bar_label_padding;
1167 pango_layout_set_text (layout, day_label_str, -1);
1168 gtk_render_layout (stylectxt, cr, label_x_offset, label_y_offset, layout);
1170 gtk_style_context_restore (stylectxt);
1177 gint x_offset = dcal->leftPadding;
1179 gtk_style_context_save (stylectxt);
1180 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_HEADER);
1182 for (i = 0; i < 12; i++)
1184 if (dcal->monthPositions[i].x == -1)
1187 gtk_render_background (stylectxt, cr, dcal->monthPositions[i].x + x_offset,
1189 dcal->month_side_bar_width + 1,
1190 col_height(dcal) + dcal->day_top_bar_height + 1);
1193 for (i = 0; i < 12; i++)
1197 if (dcal->monthPositions[i].x == -1)
1199 idx = (dcal->month - 1 + i) % 12;
1200 pango_layout_set_text (layout, month_name (idx), -1);
1202 cairo_translate (cr, dcal->monthPositions[i].x + x_offset, dcal->monthPositions[i].y);
1203 cairo_rotate (cr, -G_PI / 2.);
1204 gtk_render_layout (stylectxt, cr, 0, dcal->bar_label_padding, layout);
1207 gtk_style_context_restore (stylectxt);
1216 gint x1, y1, x2, y2, w, h;
1219 g_date_clear (&now, 1);
1221 gboolean today_found = FALSE;
1223 gtk_style_context_save (stylectxt);
1224 gtk_style_context_add_class (stylectxt,
"day-number");
1227 g_date_set_dmy (&d, 1, dcal->month, dcal->year);
1229 g_date_add_months (&eoc, dcal->numMonths);
1230 for (doc = 0; g_date_get_julian (&d) < g_date_get_julian (&eoc); g_date_add_days (&d, 1), doc++)
1232 doc_coords (dcal, doc, &x1, &y1, &x2, &y2);
1233 memset (dayNumBuf, 0, 4);
1234 snprintf (dayNumBuf, 4,
"%d", g_date_get_day(&d));
1235 pango_layout_set_text (layout, dayNumBuf, -1);
1236 pango_layout_get_pixel_size (layout, &numW, &numH);
1240 if (!today_found && g_date_compare (&d, &now) == 0)
1244 gtk_style_context_save (stylectxt);
1245 gtk_style_context_add_class (stylectxt, marker_color_class);
1246 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_FRAME);
1248 gtk_style_context_get_border (stylectxt, GTK_STATE_FLAG_NORMAL, &border);
1252 if (border.left + border.right != 0)
1254 GtkCssProvider *provider = gtk_css_provider_new ();
1255 gchar *frame_css =
".marker-border {\n border-color:black;\n}\n";
1257 gint dayw = day_width (dcal);
1258 gint dayh = day_height (dcal);
1259 gint bw = (border.left + border.right) / 2;
1261 gtk_css_provider_load_from_data (provider, frame_css, -1, NULL);
1262 gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER(provider),
1263 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1264 g_object_unref (provider);
1266 gtk_style_context_add_class (stylectxt,
"marker-border");
1268 gtk_render_frame (stylectxt, cr, x1 - (dayw / 4) + 3,
1269 y1 - (dayh / 4) + 2,
1273 gtk_style_context_remove_class (stylectxt,
"marker-border");
1275 gtk_style_context_restore (stylectxt);
1277 gtk_render_layout (stylectxt, cr, x1 + (w / 2) - (numW / 2), y1 + (h / 2) - (numH / 2), layout);
1280 gtk_style_context_restore (stylectxt);
1283 gtk_widget_get_allocation (widget, &alloc);
1284 gtk_widget_queue_draw_area (GTK_WIDGET(dcal),
1290 g_free (primary_color_class);
1291 g_free (secondary_color_class);
1292 g_free (marker_color_class);
1294 g_object_unref (layout);
1299populate_hover_window (GncDenseCal *dcal)
1307 GtkListStore *model;
1310 w = GTK_WIDGET(g_object_get_data (G_OBJECT(dcal->transPopup),
"dateLabel"));
1311 date = g_date_new_dmy (1, dcal->month, dcal->year);
1312 g_date_add_days (date, dcal->doc);
1318 time64 t64 = gnc_dmy2time64_neutral (g_date_get_day (date),
1319 g_date_get_month (date),
1320 g_date_get_year (date));
1323 gtk_label_set_text (GTK_LABEL(w), date_buff);
1325 o = G_OBJECT(dcal->transPopup);
1326 model = GTK_LIST_STORE(g_object_get_data (o,
"model"));
1327 gtk_list_store_clear (model);
1328 for (l = dcal->marks[dcal->doc]; l; l = l->next)
1334 gtk_list_store_insert (model, &iter, INT_MAX);
1335 gtk_list_store_set (model, &iter, 0, (gdcmd->name ? gdcmd->name :
_(
"(unnamed)")),
1336 1, gdcmd->info, -1);
1340 if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), NULL) == 0)
1343 gtk_list_store_insert (model, &iter, -1);
1347 while(gtk_events_pending ())
1348 gtk_main_iteration ();
1354static const int POPUP_OFFSET = 5;
1357popup_window_move (GncDenseCal *dcal, GdkEvent *event)
1359 GtkAllocation alloc;
1360 gdouble x_root, y_root;
1361 gint win_xpos, win_ypos;
1363 if (event->type == GDK_BUTTON_PRESS)
1365 x_root = ((GdkEventButton*)event)->x_root;
1366 y_root = ((GdkEventButton*)event)->y_root;
1370 x_root = ((GdkEventMotion*)event)->x_root;
1371 y_root = ((GdkEventMotion*)event)->y_root;
1373 win_xpos = x_root + POPUP_OFFSET;
1374 win_ypos = y_root + POPUP_OFFSET;
1376 gtk_widget_get_allocation (GTK_WIDGET(dcal->transPopup), &alloc);
1378 if (x_root + POPUP_OFFSET + alloc.width > dcal->screen_width)
1379 win_xpos = x_root - 2 - alloc.width;
1381 if (y_root + POPUP_OFFSET + alloc.height > dcal->screen_height)
1382 win_ypos = y_root - 2 - alloc.height;
1384 gtk_window_move (GTK_WINDOW(dcal->transPopup), win_xpos, win_ypos);
1388gnc_dense_cal_button_press (GtkWidget *widget,
1389 GdkEventButton *evt)
1391 GdkWindow *win = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
1392 GdkMonitor *mon = gdk_display_get_monitor_at_window (gtk_widget_get_display (widget), win);
1393 GdkRectangle work_area_size;
1394 GncDenseCal *dcal = GNC_DENSE_CAL(widget);
1396 gdk_monitor_get_workarea (mon, &work_area_size);
1398 dcal->screen_width = work_area_size.width;
1399 dcal->screen_height = work_area_size.height;
1401 dcal->doc = wheres_this (dcal, evt->x, evt->y);
1402 dcal->showPopup = ~(dcal->showPopup);
1403 if (dcal->showPopup && dcal->doc >= 0)
1411 gtk_window_move (GTK_WINDOW(dcal->transPopup), evt->x_root + POPUP_OFFSET,
1412 evt->y_root + POPUP_OFFSET);
1414 populate_hover_window (dcal);
1415 gtk_widget_queue_resize (GTK_WIDGET(dcal->transPopup));
1416 gtk_widget_show_all (GTK_WIDGET(dcal->transPopup));
1418 popup_window_move (dcal, (GdkEvent*)evt);
1423 gtk_widget_hide (GTK_WIDGET(dcal->transPopup));
1429gnc_dense_cal_motion_notify (GtkWidget *widget,
1430 GdkEventMotion *event)
1435 GdkModifierType unused2;
1437 dcal = GNC_DENSE_CAL(widget);
1438 if (!dcal->showPopup)
1444 GdkSeat *seat = gdk_display_get_default_seat (gdk_window_get_display (event->window));
1445 GdkDevice *pointer = gdk_seat_get_pointer (seat);
1447 gdk_window_get_device_position (event->window, pointer, &unused, &unused, &unused2);
1450 doc = wheres_this (dcal, event->x, event->y);
1453 if (dcal->doc != doc)
1456 populate_hover_window (dcal);
1457 gtk_widget_queue_resize (GTK_WIDGET(dcal->transPopup));
1458 gtk_widget_show_all (GTK_WIDGET(dcal->transPopup));
1460 popup_window_move (dcal, (GdkEvent*)event);
1465 gtk_widget_hide (GTK_WIDGET(dcal->transPopup));
1472_gdc_view_option_changed (GtkComboBox *widget, gpointer user_data)
1475 GtkTreeModel *model;
1478 model = GTK_TREE_MODEL(gtk_combo_box_get_model (widget));
1479 if (!gtk_combo_box_get_active_iter (widget, &iter))
1481 gtk_tree_model_get (model, &iter, VIEW_OPTS_COLUMN_NUM_MONTHS, &months_val, -1);
1482 DEBUG(
"changing to %d months", months_val);
1483 gnc_dense_cal_set_num_months (GNC_DENSE_CAL(user_data), months_val);
1487day_width_at (GncDenseCal *dcal, guint xScale)
1489 return xScale + MINOR_BORDER_SIZE;
1493day_width (GncDenseCal *dcal)
1495 return day_width_at (dcal, dcal->x_scale);
1499day_height_at (GncDenseCal *dcal, guint yScale)
1501 return yScale + MINOR_BORDER_SIZE;
1505day_height (GncDenseCal *dcal)
1507 return day_height_at (dcal, dcal->y_scale);
1511week_width_at (GncDenseCal *dcal, guint xScale)
1513 return day_width_at (dcal, xScale) * 7;
1517week_width (GncDenseCal *dcal)
1519 return week_width_at (dcal, dcal->x_scale);
1523week_height_at (GncDenseCal *dcal, guint yScale)
1525 return day_height_at (dcal, yScale);
1529week_height (GncDenseCal *dcal)
1531 return week_height_at (dcal, dcal->y_scale);
1535col_width_at (GncDenseCal *dcal, guint xScale)
1537 return (week_width_at (dcal, xScale)
1538 + dcal->month_side_bar_width
1543col_width (GncDenseCal *dcal)
1545 return col_width_at (dcal, dcal->x_scale);
1549col_height (GncDenseCal *dcal)
1551 return week_height (dcal) * num_weeks_per_col (dcal);
1555num_cols (GncDenseCal *dcal)
1557 return ceil ((
float)dcal->numMonths / (
float)dcal->monthsPerCol);
1561num_weeks (GncDenseCal *dcal)
1563 return dcal->num_weeks;
1567int num_weeks_per_col (GncDenseCal *dcal)
1569 int num_weeks_toRet, numCols, i;
1571 int startWeek, endWeek;
1573 start = g_date_new ();
1574 end = g_date_new ();
1576 num_weeks_toRet = 0;
1577 numCols = num_cols (dcal);
1579 for (i = 0; i < numCols; i++)
1581 g_date_set_dmy (start, 1,
1583 + (i * dcal->monthsPerCol)) % 12)
1585 dcal->year + floor ((dcal->month - 1
1586 + (i * dcal->monthsPerCol))
1592 g_date_add_months (end, MIN(dcal->numMonths,
1593 MIN(dcal->monthsPerCol,
1596 * dcal->monthsPerCol))));
1597 g_date_subtract_days (end, 1);
1598 startWeek = get_week_of_year (dcal, start);
1599 endWeek = get_week_of_year (dcal, end);
1601 if (endWeek < startWeek)
1602 endWeek += get_weeks_in_year (dcal, g_date_get_year (start));
1604 num_weeks_toRet = MAX(num_weeks_toRet, (endWeek - startWeek) + 1);
1606 g_date_free (start);
1608 return num_weeks_toRet;
1618month_coords (GncDenseCal *dcal,
int monthOfCal, GList **outList)
1620 gint weekRow, colNum, previousMonthsInCol, monthOffset;
1622 GDate *startD, *endD;
1624 gint startWk, endWk;
1626 if (monthOfCal > dcal->numMonths)
1629 colNum = floor (monthOfCal / dcal->monthsPerCol);
1630 monthOffset = colNum * dcal->monthsPerCol;
1631 previousMonthsInCol = MAX(0, (monthOfCal % dcal->monthsPerCol));
1633 startD = g_date_new ();
1634 endD = g_date_new ();
1639 if (previousMonthsInCol > 0)
1641 g_date_set_dmy (startD, 1,
1642 ((dcal->month - 1 + monthOffset) % 12) + 1,
1643 dcal->year + floor ((dcal->month - 1 + monthOffset) / 12));
1645 startWk = get_week_of_year (dcal, startD);
1648 g_date_add_months (endD, previousMonthsInCol);
1649 g_date_subtract_days (endD, 1);
1650 endWk = get_week_of_year (dcal, endD);
1652 if (endWk < startWk)
1653 endWk += get_weeks_in_year (dcal, g_date_get_year (startD));
1657 weekRow = endWk - startWk;
1659 gint end_of_week = dcal->day_of_week_start + 6;
1660 if (end_of_week > 7)
1661 end_of_week = end_of_week - 7;
1663 if (g_date_get_weekday (endD) == end_of_week)
1667 g_date_set_dmy (startD, 1,
1668 ((dcal->month - 1 + monthOfCal) % 12) + 1,
1669 dcal->year + floor ((dcal->month - 1 + monthOfCal) / 12));
1672 g_date_add_months (endD, 1);
1673 g_date_subtract_days (endD, 1);
1677 start = (g_date_get_weekday (startD) + 7 - dcal->day_of_week_start) % 7;
1679 rect = g_new0 (GdkRectangle, 1);
1680 rect->x = dcal->leftPadding
1682 + (colNum * (col_width (dcal) + COL_BORDER_SIZE))
1683 + dcal->month_side_bar_width
1684 + (start * day_width (dcal));
1685 rect->y = dcal->topPadding
1686 + dcal->day_top_bar_height
1688 + (weekRow * week_height (dcal));
1689 rect->width = (7 - start) * day_width (dcal);
1690 rect->height = week_height (dcal);
1691 *outList = g_list_append (*outList, (gpointer)rect);
1698 gint weekStart = get_week_of_year (dcal, startD) + 1;
1699 gint weekEnd = get_week_of_year (dcal, endD);
1701 for (i = weekStart; i < weekEnd; i++)
1703 rect = g_new0 (GdkRectangle, 1);
1704 rect->x = dcal->leftPadding
1706 + dcal->month_side_bar_width
1707 + (colNum * (col_width (dcal) + COL_BORDER_SIZE));
1708 rect->y = dcal->topPadding
1709 + dcal->day_top_bar_height
1711 + ((weekRow + (i - weekStart) + 1) * week_height (dcal));
1712 rect->width = week_width (dcal);
1713 rect->height = week_height (dcal);
1715 *outList = g_list_append (*outList, (gpointer)rect);
1722 gint start_week_of_year = get_week_of_year (dcal, startD);
1723 gint end_week_of_year = get_week_of_year (dcal, endD);
1725 rect = g_new0 (GdkRectangle, 1);
1726 rect->x = dcal->leftPadding
1728 + dcal->month_side_bar_width
1729 + (colNum * (col_width (dcal) + COL_BORDER_SIZE));
1730 rect->y = dcal->topPadding
1732 + dcal->day_top_bar_height
1734 + (end_week_of_year - start_week_of_year))
1735 * week_height (dcal));
1736 rect->width = (((g_date_get_weekday (endD) + 7 - dcal->day_of_week_start) % 7) + 1) * day_width (dcal);
1737 rect->height = week_height (dcal);
1739 *outList = g_list_append (*outList, (gpointer)rect);
1743 g_date_free (startD);
1749doc_coords (GncDenseCal *dcal,
int dayOfCal,
1750 int *x1,
int *y1,
int *x2,
int *y2)
1754 gint d_week_of_cal, top_of_col_week_of_cal;
1755 gint colNum, dayCol, weekRow;
1758 g_date_set_dmy (&d, 1, dcal->month, dcal->year);
1759 g_date_add_days (&d, dayOfCal);
1760 docMonth = g_date_get_month (&d);
1761 if (g_date_get_year (&d) != dcal->year)
1765 colNum = floor ((
float)(docMonth - dcal->month) / (
float)dcal->monthsPerCol);
1766 dayCol = g_date_get_weekday (&d) - dcal->day_of_week_start;
1769 dayCol = dayCol + 7;
1771 d_week_of_cal = get_week_of_year (dcal, &d);
1772 g_date_set_dmy (&d, 1, dcal->month, dcal->year);
1773 g_date_add_months (&d, (colNum * dcal->monthsPerCol));
1774 top_of_col_week_of_cal = get_week_of_year (dcal, &d);
1776 if (d_week_of_cal < top_of_col_week_of_cal)
1778 gint week_offset = get_weeks_in_year (dcal, dcal->year);
1779 d_week_of_cal += week_offset;
1781 weekRow = d_week_of_cal - top_of_col_week_of_cal;
1786 *x1 = dcal->leftPadding
1788 + dcal->month_side_bar_width
1789 + (colNum * (col_width (dcal) + COL_BORDER_SIZE))
1790 + (dayCol * day_width (dcal))
1791 + (day_width (dcal) / 4);
1792 *y1 = dcal->topPadding
1794 + dcal->day_top_bar_height
1795 + (weekRow * week_height (dcal))
1796 + (day_height (dcal) / 4);
1798 *x2 = *x1 + (day_width (dcal) / 2);
1799 *y2 = *y1 + (day_height (dcal) / 2);
1807wheres_this (GncDenseCal *dcal,
int x,
int y)
1809 gint colNum, weekRow, dayCol, dayOfCal;
1811 GtkAllocation alloc;
1813 x -= dcal->leftPadding;
1814 y -= dcal->topPadding;
1816 if ((x < 0) || (y < 0))
1820 gtk_widget_get_allocation (GTK_WIDGET(dcal), &alloc);
1821 if ((x >= alloc.width)
1822 || (y >= alloc.height))
1828 if (x >= (num_cols(dcal) * (col_width (dcal) + COL_BORDER_SIZE)))
1832 if (y >= dcal->day_top_bar_height + col_height (dcal))
1838 colNum = floor (x / (col_width (dcal) + COL_BORDER_SIZE));
1840 x %= (col_width (dcal) + COL_BORDER_SIZE);
1841 x -= dcal->month_side_bar_width;
1846 if (x >= day_width (dcal) * 7)
1851 y -= dcal->day_top_bar_height;
1857 dayCol = floor ((
float)x / (
float)day_width (dcal));
1858 weekRow = floor ((
float)y / (
float)week_height (dcal));
1860 g_date_set_dmy (&startD, 1, dcal->month, dcal->year);
1862 g_date_add_months (&d, (colNum * dcal->monthsPerCol));
1864 if (dcal->day_of_week_start == G_DATE_SUNDAY)
1865 dayCol -= (g_date_get_weekday (&d) - 0) % 7;
1867 dayCol -= (g_date_get_weekday (&d) - 1) % 7;
1876 g_date_add_days (&d, dayCol + (weekRow * 7));
1881 g_date_set_dmy (&ccd, 1, dcal->month, dcal->year);
1882 g_date_add_months (&ccd, (colNum + 1) * dcal->monthsPerCol);
1883 if (g_date_get_julian (&d) >= g_date_get_julian (&ccd))
1889 dayOfCal = g_date_get_julian (&d) - g_date_get_julian (&startD);
1892 g_date_subtract_months (&d, dcal->numMonths);
1893 if (g_date_get_julian (&d) >= g_date_get_julian (&startD))
1896 DEBUG(
"%d >= %d", g_date_get_julian (&d), g_date_get_julian (&startD));
1904gdc_get_doc_offset (GncDenseCal *dcal, GDate *d)
1910 g_date_clear (&soc, 1);
1911 g_date_set_dmy (&soc, 1, dcal->month, dcal->year);
1913 if (g_date_get_julian (d) < g_date_get_julian (&soc))
1917 toRet = g_date_get_julian (d) - g_date_get_julian (&soc);
1919 g_date_add_months (&soc, dcal->numMonths);
1920 if (g_date_get_julian (d) >= g_date_get_julian (&soc))
1927gdc_add_tag_markings (GncDenseCal *cal, guint tag)
1930 gint num_marks, idx;
1935 name = gnc_dense_cal_model_get_name (cal->model, tag);
1936 info = gnc_dense_cal_model_get_info (cal->model, tag);
1937 num_marks = gnc_dense_cal_model_get_instance_count (cal->model, tag);
1942 dates = g_new0 (GDate*, num_marks);
1943 calDate = g_date_new_dmy (1, cal->month, cal->year);
1945 for (idx = 0; idx < num_marks; idx++)
1947 dates[idx] = g_date_new ();
1948 gnc_dense_cal_model_get_instance (cal->model, tag, idx, dates[idx]);
1951 if (g_date_valid (dates[0]))
1953 if (g_date_get_julian (dates[0]) < g_date_get_julian (calDate))
1959 _gnc_dense_cal_set_month (cal, g_date_get_month (dates[0]), FALSE);
1960 _gnc_dense_cal_set_year (cal, g_date_get_year (dates[0]), FALSE);
1962 gdc_remove_markings (cal);
1963 gdc_add_markings (cal);
1966 gdc_mark_add (cal, tag, name, info, num_marks, dates);
1970 g_warning (
"Bad date, skipped.");
1973 for (idx = 0; idx < num_marks; idx++)
1975 g_date_free (dates[idx]);
1978 g_date_free (calDate);
1985gdc_add_markings (GncDenseCal *cal)
1987 GList *tags = gnc_dense_cal_model_get_contained (cal->model);
1989 for (GList *n = tags; n; n = n->next)
1990 gdc_add_tag_markings (cal, GPOINTER_TO_UINT(n->data));
1996gdc_remove_markings (GncDenseCal *cal)
1998 GList *tags = gnc_dense_cal_model_get_contained (cal->model);
2000 for (GList *n = tags; n; n = n->next)
2001 gdc_mark_remove (cal, GPOINTER_TO_UINT(n->data), FALSE);
2007gdc_model_added_cb (GncDenseCalModel *model, guint added_tag, gpointer user_data)
2009 GncDenseCal *cal = GNC_DENSE_CAL(user_data);
2010 DEBUG(
"gdc_model_added_cb update");
2011 gdc_add_tag_markings (cal, added_tag);
2015gdc_model_update_cb (GncDenseCalModel *model, guint update_tag, gpointer user_data)
2017 GncDenseCal *cal = GNC_DENSE_CAL(user_data);
2019 DEBUG(
"gdc_model_update_cb update for tag [%d]", update_tag);
2020 num_marks = gnc_dense_cal_model_get_instance_count (cal->model, update_tag);
2022 gdc_mark_remove (cal, update_tag, num_marks==0);
2023 gdc_add_tag_markings (cal, update_tag);
2028gdc_model_removing_cb (GncDenseCalModel *model, guint remove_tag, gpointer user_data)
2030 GncDenseCal *cal = GNC_DENSE_CAL(user_data);
2031 DEBUG(
"gdc_model_removing_cb update [%d]", remove_tag);
2032 gdc_mark_remove (cal, remove_tag, TRUE);
2036gnc_dense_cal_set_model (GncDenseCal *cal, GncDenseCalModel *model)
2038 if (cal->model != NULL)
2040 gdc_remove_markings (cal);
2041 g_object_unref (G_OBJECT(cal->model));
2045 g_object_ref (G_OBJECT(model));
2046 g_signal_connect (G_OBJECT(cal->model),
"added", (GCallback)gdc_model_added_cb, cal);
2047 g_signal_connect (G_OBJECT(cal->model),
"update", (GCallback)gdc_model_update_cb, cal);
2048 g_signal_connect (G_OBJECT(cal->model),
"removing", (GCallback)gdc_model_removing_cb, cal);
2050 gdc_add_markings (cal);
2057gdc_mark_add (GncDenseCal *dcal,
2071 g_error (
"0 size not allowed");
2076 newMark->name = NULL;
2078 newMark->name = g_strdup (name);
2079 newMark->info = NULL;
2081 newMark->info = g_strdup (info);
2084 DEBUG(
"saving mark with tag [%d]", newMark->tag);
2086 for (i = 0; i < size; i++)
2089 doc = gdc_get_doc_offset (dcal, d);
2092 if (doc >= dcal->numMarks)
2098 dcal->marks[doc] = g_list_append (dcal->marks[doc], newMark);
2100 GINT_TO_POINTER(doc));
2102 dcal->markData = g_list_append (dcal->markData, (gpointer)newMark);
2103 gnc_dense_cal_draw_to_buffer (dcal);
2104 gtk_widget_queue_draw (GTK_WIDGET(dcal->cal_drawing_area));
2108gdc_mark_remove (GncDenseCal *dcal, guint mark_to_remove, gboolean redraw)
2110 GList *iter, *calendar_marks;
2115 if ((gint)mark_to_remove == -1)
2117 DEBUG(
"mark_to_remove = -1");
2122 for (iter = dcal->markData; iter != NULL; iter = iter->next)
2125 if (mark_data->tag == mark_to_remove)
2130 PINFO(
"couldn't find tag [%d]", mark_to_remove);
2133 if (mark_data == NULL)
2135 DEBUG(
"mark_data == null");
2139 for (calendar_marks = mark_data->
ourMarks; calendar_marks != NULL; calendar_marks = calendar_marks->next)
2141 day_of_cal = GPOINTER_TO_INT(calendar_marks->data);
2142 dcal->marks[day_of_cal] = g_list_remove (dcal->marks[day_of_cal], mark_data);
2145 dcal->markData = g_list_remove (dcal->markData, mark_data);
2146 g_free (mark_data->name);
2147 g_free (mark_data->info);
2152 gnc_dense_cal_draw_to_buffer (dcal);
2153 gtk_widget_queue_draw (GTK_WIDGET(dcal->cal_drawing_area));
Date and Time handling routines.
All type declarations for the whole Gnucash engine.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
void gnc_dow_abbrev(gchar *buf, int buf_len, int dow)
Localized DOW abbreviation.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
size_t qof_print_date_buff(char *buff, const size_t len, time64 t)
Convenience: calls through to qof_print_date_dmy_buff().
gint gnc_start_of_week(void)
returns an integer corresponding to locale start of week
void gnc_gdate_set_today(GDate *gd)
Set a GDate to the current day.
gboolean gnc_is_dark_theme(GdkRGBA *fg_color)
Return whether the current gtk theme is a dark one.
void gnc_style_context_get_border_color(GtkStyleContext *context, GtkStateFlags state, GdkRGBA *color)
Wrapper to get the border color of a widget for a given state.
#define PINFO(format, args...)
Print an informational note.
#define DEBUG(format, args...)
Print a debugging message.
GList * markData
A GList of gdc_mark_data structs, one for each active/valid markTag.
GList * ourMarks
A GList of the dcal->marks indexes containing this mark.