25 #include "gnc-dense-cal.h" 26 #include "gnc-dense-cal-model.h" 30 #include <glib/gi18n.h> 35 #include "dialog-utils.h" 38 static const QofLogModule log_module =
"gnc.gui.dense-cal";
71 static const int DENSE_CAL_DEFAULT_WIDTH = 15;
72 static const int DENSE_CAL_DEFAULT_HEIGHT = 105;
73 static const int MINOR_BORDER_SIZE = 1;
74 static const int COL_BORDER_SIZE = 3;
76 static void gnc_dense_cal_finalize (GObject *
object);
77 static void gnc_dense_cal_dispose (GObject *
object);
78 static void gnc_dense_cal_realize (GtkWidget *widget, gpointer user_data);
79 static void gnc_dense_cal_configure (GtkWidget *widget,
80 GdkEventConfigure *event,
82 static void gnc_dense_cal_draw_to_buffer (GncDenseCal *dcal);
83 static gboolean gnc_dense_cal_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data);
85 static void gdc_reconfig (GncDenseCal *dcal);
87 static void gdc_free_all_mark_data (GncDenseCal *dcal);
89 static void _gdc_compute_min_size (GncDenseCal *dcal,
90 guint *min_width, guint *min_height);
91 static void _gdc_set_cal_min_size_req (GncDenseCal *dcal);
92 static gint gnc_dense_cal_motion_notify (GtkWidget *widget,
93 GdkEventMotion *event);
94 static gint gnc_dense_cal_button_press (GtkWidget *widget,
97 static void _gdc_view_option_changed (GtkComboBox *widget, gpointer user_data);
99 static inline int day_width_at (GncDenseCal *dcal, guint xScale);
100 static inline int day_width (GncDenseCal *dcal);
101 static inline int day_height_at (GncDenseCal *dcal, guint yScale);
102 static inline int day_height (GncDenseCal *dcal);
103 static inline int week_width_at (GncDenseCal *dcal, guint xScale);
104 static inline int week_width (GncDenseCal *dcal);
105 static inline int week_height_at (GncDenseCal *dcal, guint yScale);
106 static inline int week_height (GncDenseCal *dcal);
107 static inline int col_width_at (GncDenseCal *dcal, guint xScale);
108 static inline int col_width (GncDenseCal *dcal);
110 static inline int col_height (GncDenseCal *dcal);
111 static inline int num_cols (GncDenseCal *dcal);
113 static void _gnc_dense_cal_set_month (GncDenseCal *dcal, GDateMonth mon, gboolean redraw);
114 static void _gnc_dense_cal_set_year (GncDenseCal *dcal, guint year, gboolean redraw);
120 static inline int num_weeks (GncDenseCal *dcal);
125 static int num_weeks_per_col (GncDenseCal *dcal);
128 static gint wheres_this (GncDenseCal *dcal,
int x,
int y);
130 static void recompute_x_y_scales (GncDenseCal *dcal);
131 static void recompute_mark_storage (GncDenseCal *dcal);
132 static void recompute_extents (GncDenseCal *dcal);
133 static void populate_hover_window (GncDenseCal *dcal);
135 static void month_coords (GncDenseCal *dcal,
int monthOfCal, GList **outList);
136 static void doc_coords (GncDenseCal *dcal,
int dayOfCal,
137 int *x1,
int *y1,
int *x2,
int *y2);
139 static void gdc_mark_add (GncDenseCal *dcal, guint tag, gchar *name,
140 gchar *info, guint size, GDate **dateArray);
141 static void gdc_mark_remove (GncDenseCal *dcal, guint mark_to_remove, gboolean redraw);
143 static void gdc_add_tag_markings (GncDenseCal *cal, guint tag);
144 static void gdc_add_markings (GncDenseCal *cal);
145 static void gdc_remove_markings (GncDenseCal *cal);
147 typedef 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;
211 typedef struct _gdc_mark_data
222 G_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);
251 day_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);
263 gnc_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;
277 enum _GdcViewOptsColumns
279 VIEW_OPTS_COLUMN_LABEL = 0,
280 VIEW_OPTS_COLUMN_NUM_MONTHS,
281 VIEW_OPTS_COLUMN_NUM_MONTHS_PER_COLUMN
284 static 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;
303 gnc_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);
486 gnc_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);
497 gnc_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);
505 recompute_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);
515 gnc_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));
541 gnc_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));
563 gnc_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));
616 gnc_dense_cal_get_num_months (GncDenseCal *dcal)
618 return dcal->numMonths;
622 gnc_dense_cal_set_months_per_col (GncDenseCal *dcal, guint monthsPerCol)
624 dcal->monthsPerCol = monthsPerCol;
625 recompute_x_y_scales (dcal);
629 gnc_dense_cal_get_month (GncDenseCal *dcal)
635 gnc_dense_cal_get_year (GncDenseCal *dcal)
641 gnc_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);
676 gnc_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);
685 gnc_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);
698 gnc_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);
711 gdc_reconfig (GncDenseCal *dcal)
717 cairo_surface_destroy (dcal->surface);
719 widget = GTK_WIDGET(dcal->cal_drawing_area);
720 gtk_widget_get_allocation (widget, &alloc);
721 dcal->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
724 gnc_dense_cal_draw_to_buffer (dcal);
728 _gdc_compute_min_size (GncDenseCal *dcal, guint *min_width, guint *min_height)
730 if (min_width != NULL)
733 (dcal->leftPadding * 2)
734 + (num_cols (dcal) * (col_width_at (dcal, dcal->min_x_scale)
735 + dcal->month_side_bar_width))
736 + ((num_cols (dcal) - 1) * COL_BORDER_SIZE);
739 if (min_height != NULL)
742 (dcal->topPadding * 2)
744 + dcal->day_top_bar_height
745 + (num_weeks_per_col (dcal)
746 * week_height_at (dcal, dcal->min_y_scale));
751 recompute_x_y_scales (GncDenseCal *dcal)
756 width = DENSE_CAL_DEFAULT_WIDTH;
757 height = DENSE_CAL_DEFAULT_HEIGHT;
758 if (dcal->initialized)
761 gtk_widget_get_allocation (GTK_WIDGET(dcal->cal_drawing_area), &alloc);
763 height = alloc.height;
768 denom = 7 * num_cols (dcal);
769 g_assert (denom != 0);
770 dcal->x_scale = ((gint)(width
771 - (dcal->leftPadding * 2)
772 - (num_cols (dcal) * ((8 * MINOR_BORDER_SIZE)
773 + dcal->month_side_bar_width))
774 - ((num_cols (dcal) - 1) * COL_BORDER_SIZE))
776 dcal->x_scale = MAX(dcal->x_scale, dcal->min_x_scale);
778 denom = num_weeks_per_col (dcal);
779 g_assert (denom != 0);
780 dcal->y_scale = ((gint)(height
781 - (dcal->topPadding * 2)
783 - dcal->day_top_bar_height
784 - (num_weeks_per_col (dcal) - 1
785 * MINOR_BORDER_SIZE))
787 dcal->y_scale = MAX(dcal->y_scale, dcal->min_y_scale);
789 _gdc_set_cal_min_size_req (dcal);
793 gdc_free_all_mark_data (GncDenseCal *dcal)
797 for (i = 0; i < dcal->numMarks; i++)
801 g_list_free (dcal->marks[i]);
803 g_free (dcal->marks);
806 for (l = dcal->markData; l; l = l->next)
814 g_list_free (dcal->markData);
815 dcal->markData = NULL;
819 recompute_mark_storage (GncDenseCal *dcal)
821 if (dcal->marks == NULL)
823 gdc_free_all_mark_data (dcal);
826 dcal->numMarks = num_weeks (dcal) * 7;
827 dcal->marks = g_new0 (GList*, dcal->numMarks);
829 gdc_add_markings (dcal);
833 get_week_of_year (GncDenseCal *dcal, GDate *d)
835 GDateWeekday fwd, lwd;
840 gint monday_offset = 1;
843 g_return_val_if_fail (g_date_valid (d), 0);
845 year = g_date_get_year (d);
850 g_date_clear (&first, 1);
851 g_date_set_dmy (&first, 1, 1, year);
853 fwd = g_date_get_weekday (&first);
855 day_offset = (fwd + 7 - dcal->day_of_week_start) % 7;
857 if (dcal->day_of_week_start == G_DATE_SUNDAY)
859 else if (dcal->day_of_week_start == G_DATE_MONDAY)
861 else if (dcal->day_of_week_start == G_DATE_TUESDAY)
863 else if (dcal->day_of_week_start == G_DATE_WEDNESDAY)
865 else if (dcal->day_of_week_start == G_DATE_THURSDAY)
867 else if (dcal->day_of_week_start == G_DATE_FRIDAY)
869 else if (dcal->day_of_week_start == G_DATE_SATURDAY)
874 day = g_date_get_day_of_year (d) - 1;
876 g_date_clear (&last, 1);
877 g_date_set_dmy (&last, 31, 12, year - 1);
878 lwd = g_date_get_weekday (&last);
879 gint lday_offset = 6 - ((lwd + 7 - dcal->day_of_week_start) % 7);
885 ret = ((day + day_offset)/7U + ((day_offset <= monday_offset) ? addone : 0));
891 get_weeks_in_year (GncDenseCal *dcal, GDateYear year)
895 g_return_val_if_fail (g_date_valid_year (year), 0);
897 g_date_clear (&d, 1);
898 g_date_set_dmy (&d, 1, 1, year);
899 if (g_date_get_weekday (&d) == dcal->day_of_week_start)
return 53;
900 g_date_set_dmy (&d, 31, 12, year);
901 if (g_date_get_weekday (&d) == dcal->day_of_week_start)
return 53;
902 if (g_date_is_leap_year (year))
904 g_date_set_dmy (&d, 2, 1, year);
905 if (g_date_get_weekday (&d) == dcal->day_of_week_start)
return 53;
906 g_date_set_dmy (&d, 30, 12, year);
907 if (g_date_get_weekday (&d) == dcal->day_of_week_start)
return 53;
913 recompute_extents (GncDenseCal *dcal)
916 gint start_week, end_week;
918 g_date_clear (&date, 1);
919 g_date_set_dmy (&date, 1, dcal->month, dcal->year);
920 start_week = get_week_of_year (dcal, &date);
921 g_date_add_months (&date, dcal->numMonths);
922 end_week = get_week_of_year (dcal, &date);
924 if (g_date_get_year (&date) != dcal->year)
925 end_week += get_weeks_in_year (dcal, dcal->year);
927 dcal->num_weeks = end_week - start_week + 1;
931 free_rect (gpointer data, gpointer user_data)
933 g_free ((GdkRectangle*)data);
937 gnc_dense_cal_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data)
941 g_return_val_if_fail (widget != NULL, FALSE);
942 g_return_val_if_fail (GNC_IS_DENSE_CAL(user_data), FALSE);
944 dcal = GNC_DENSE_CAL(user_data);
947 cairo_set_source_surface (cr, dcal->surface, 0, 0);
954 gnc_dense_cal_draw_to_buffer (GncDenseCal *dcal)
957 GtkStyleContext *stylectxt;
958 GtkStateFlags state_flags;
964 gchar *primary_color_class, *secondary_color_class, *marker_color_class;
967 widget = GTK_WIDGET(dcal);
972 cr = cairo_create (dcal->surface);
973 layout = gtk_widget_create_pango_layout (GTK_WIDGET(dcal), NULL);
975 gtk_widget_get_allocation (GTK_WIDGET(dcal->cal_drawing_area), &alloc);
976 stylectxt = gtk_widget_get_style_context (GTK_WIDGET(dcal->cal_drawing_area));
977 state_flags = gtk_style_context_get_state (stylectxt);
979 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_BACKGROUND);
980 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_CALENDAR);
982 gtk_render_background (stylectxt, cr, 0, 0,
983 cairo_image_surface_get_width (dcal->surface),
984 cairo_image_surface_get_height (dcal->surface));
986 gtk_style_context_remove_class (stylectxt, GTK_STYLE_CLASS_BACKGROUND);
991 gchar *class_extension = NULL;
993 gtk_style_context_get_color (stylectxt, GTK_STATE_FLAG_NORMAL, &color);
996 class_extension =
"-dark";
998 primary_color_class = g_strconcat (
"primary", class_extension, NULL);
999 secondary_color_class = g_strconcat (
"secondary", class_extension, NULL);
1000 marker_color_class = g_strconcat (
"markers", class_extension, NULL);
1004 pango_layout_set_text (layout,
"S", -1);
1005 pango_layout_get_pixel_size (layout, NULL, &dcal->label_height);
1006 dcal->month_side_bar_width = dcal->label_height + (dcal->bar_label_padding * 2);
1007 dcal->day_top_bar_height = dcal->label_height + (dcal->bar_label_padding * 2);
1013 GList *mcList, *mcListIter;
1016 for (i = 0; i < 12; i++)
1018 dcal->monthPositions[i].x = dcal->monthPositions[i].y = -1;
1021 gtk_style_context_save (stylectxt);
1024 for (i = 0; i < dcal->numMonths; i++)
1027 month_coords (dcal, i, &mcList);
1028 dcal->monthPositions[i].x = floor (i / dcal->monthsPerCol)
1029 * (col_width (dcal) + COL_BORDER_SIZE);
1030 dcal->monthPositions[i].y = ((GdkRectangle*)mcList->next->next->next->data)->y;
1031 for (mcListIter = mcList; mcListIter != NULL; mcListIter = mcListIter->next)
1033 rect = (GdkRectangle*)mcListIter->data;
1034 gtk_style_context_save (stylectxt);
1037 gtk_style_context_add_class (stylectxt, primary_color_class);
1039 gtk_style_context_add_class (stylectxt, secondary_color_class);
1041 gtk_render_background (stylectxt, cr, rect->x, rect->y, rect->width, rect->height);
1042 gtk_style_context_restore (stylectxt);
1044 g_list_foreach (mcList, free_rect, NULL);
1045 g_list_free (mcList);
1047 gtk_style_context_restore (stylectxt);
1055 gtk_style_context_save (stylectxt);
1056 gtk_style_context_add_class (stylectxt, marker_color_class);
1057 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_VIEW);
1058 gtk_style_context_set_state (stylectxt, GTK_STATE_FLAG_SELECTED);
1060 for (i = 0; i < dcal->numMarks; i++)
1062 if (dcal->marks[i] != NULL)
1064 int center_x, center_y, radius;
1066 doc_coords(dcal, i, &x1, &y1, &x2, &y2);
1067 center_x = (x1 + x2 ) / 2;
1068 center_y = (y1 + y2 ) / 2;
1069 radius = MIN((x2 - x1), (y2 - y1)) * .75;
1072 if (((y2 - y1) % 2) != 0)
1073 center_y = center_y + 1;
1075 if (((x2 - x1) % 2) != 0)
1076 center_x = center_x + 1;
1078 gtk_render_background (stylectxt, cr,
1079 center_x - (radius + 2), center_y - radius,
1080 (radius * 2) + 4, radius * 2);
1083 gtk_style_context_restore (stylectxt);
1086 for (i = 0; i < num_cols (dcal); i++)
1093 gdk_rgba_parse (&color,
"black");
1095 x = dcal->leftPadding
1096 + (i * (col_width (dcal) + COL_BORDER_SIZE))
1097 + dcal->month_side_bar_width + 1;
1098 y = dcal->topPadding + dcal->day_top_bar_height;
1099 w = col_width (dcal) - COL_BORDER_SIZE - dcal->month_side_bar_width;
1100 h = col_height (dcal);
1102 gtk_style_context_save (stylectxt);
1105 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_FRAME);
1107 gtk_render_frame (stylectxt, cr, x, y, w + 1, h + 1);
1110 cairo_set_source_rgb (cr, color.red, color.green, color.blue);
1111 cairo_set_line_width (cr, 1);
1114 for (j = 0; j < num_weeks_per_col (dcal); j++)
1116 gint wy = y + (j * week_height (dcal));
1117 cairo_move_to (cr, x, wy + 0.5);
1118 cairo_line_to (cr, x + w, wy + 0.5);
1123 for (j = 1; j < 7; j++)
1125 gint dx = x + (j * day_width (dcal));
1126 cairo_move_to (cr, dx + 0.5, y);
1127 cairo_line_to (cr, dx + 0.5, y + col_height (dcal));
1131 gtk_style_context_restore (stylectxt);
1135 pango_layout_set_text (layout,
"88", -1);
1136 pango_layout_get_pixel_size (layout, &maxWidth, NULL);
1138 if (dcal->x_scale > maxWidth)
1140 gtk_style_context_save (stylectxt);
1141 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_HEADER);
1143 gtk_render_background (stylectxt, cr, x,
1144 y - dcal->day_top_bar_height,
1145 (day_width(dcal) * 7) + 1,
1146 dcal->day_top_bar_height);
1148 for (j = 0; j < 7; j++)
1150 int day_label_width;
1151 gint label_x_offset, label_y_offset;
1152 gint day_label_str_len = 4;
1153 gchar day_label_str[day_label_str_len + 1];
1154 day_label (day_label_str, day_label_str_len, (j + dcal->day_of_week_start) % 7);
1155 pango_layout_set_text (layout, day_label_str, -1);
1156 pango_layout_get_pixel_size (layout, &day_label_width, NULL);
1158 + (j * day_width (dcal))
1159 + (day_width (dcal) / 2)
1160 - (day_label_width / 2);
1161 label_y_offset = y - dcal->day_top_bar_height + dcal->bar_label_padding;
1162 pango_layout_set_text (layout, day_label_str, -1);
1163 gtk_render_layout (stylectxt, cr, label_x_offset, label_y_offset, layout);
1165 gtk_style_context_restore (stylectxt);
1172 gint x_offset = dcal->leftPadding;
1174 gtk_style_context_save (stylectxt);
1175 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_HEADER);
1177 for (i = 0; i < 12; i++)
1179 if (dcal->monthPositions[i].x == -1)
1182 gtk_render_background (stylectxt, cr, dcal->monthPositions[i].x + x_offset,
1184 dcal->month_side_bar_width + 1,
1185 col_height(dcal) + dcal->day_top_bar_height + 1);
1188 for (i = 0; i < 12; i++)
1192 if (dcal->monthPositions[i].x == -1)
1194 idx = (dcal->month - 1 + i) % 12;
1195 pango_layout_set_text (layout, month_name (idx), -1);
1197 cairo_translate (cr, dcal->monthPositions[i].x + x_offset, dcal->monthPositions[i].y);
1198 cairo_rotate (cr, -G_PI / 2.);
1199 gtk_render_layout (stylectxt, cr, 0, dcal->bar_label_padding, layout);
1202 gtk_style_context_restore (stylectxt);
1211 gint x1, y1, x2, y2, w, h;
1214 g_date_clear (&now, 1);
1216 gboolean today_found = FALSE;
1218 gtk_style_context_save (stylectxt);
1219 gtk_style_context_add_class (stylectxt,
"day-number");
1222 g_date_set_dmy (&d, 1, dcal->month, dcal->year);
1224 g_date_add_months (&eoc, dcal->numMonths);
1225 for (doc = 0; g_date_get_julian (&d) < g_date_get_julian (&eoc); g_date_add_days (&d, 1), doc++)
1227 doc_coords (dcal, doc, &x1, &y1, &x2, &y2);
1228 memset (dayNumBuf, 0, 4);
1229 snprintf (dayNumBuf, 4,
"%d", g_date_get_day(&d));
1230 pango_layout_set_text (layout, dayNumBuf, -1);
1231 pango_layout_get_pixel_size (layout, &numW, &numH);
1235 if (!today_found && g_date_compare (&d, &now) == 0)
1239 gtk_style_context_save (stylectxt);
1240 gtk_style_context_add_class (stylectxt, marker_color_class);
1241 gtk_style_context_add_class (stylectxt, GTK_STYLE_CLASS_FRAME);
1243 gtk_style_context_get_border (stylectxt, GTK_STATE_FLAG_NORMAL, &border);
1247 if (border.left + border.right != 0)
1249 GtkCssProvider *provider = gtk_css_provider_new ();
1250 gchar *frame_css =
".marker-border {\n border-color:black;\n}\n";
1252 gint dayw = day_width (dcal);
1253 gint dayh = day_height (dcal);
1254 gint bw = (border.left + border.right) / 2;
1256 gtk_css_provider_load_from_data (provider, frame_css, -1, NULL);
1257 gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER(provider),
1258 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1259 g_object_unref (provider);
1261 gtk_style_context_add_class (stylectxt,
"marker-border");
1263 gtk_render_frame (stylectxt, cr, x1 - (dayw / 4) + 3,
1264 y1 - (dayh / 4) + 2,
1268 gtk_style_context_remove_class (stylectxt,
"marker-border");
1270 gtk_style_context_restore (stylectxt);
1272 gtk_render_layout (stylectxt, cr, x1 + (w / 2) - (numW / 2), y1 + (h / 2) - (numH / 2), layout);
1275 gtk_style_context_restore (stylectxt);
1278 gtk_widget_get_allocation (widget, &alloc);
1279 gtk_widget_queue_draw_area (GTK_WIDGET(dcal),
1285 g_free (primary_color_class);
1286 g_free (secondary_color_class);
1287 g_free (marker_color_class);
1289 g_object_unref (layout);
1294 populate_hover_window (GncDenseCal *dcal)
1302 GtkListStore *model;
1305 w = GTK_WIDGET(g_object_get_data (G_OBJECT(dcal->transPopup),
"dateLabel"));
1306 date = g_date_new_dmy (1, dcal->month, dcal->year);
1307 g_date_add_days (date, dcal->doc);
1314 g_date_get_month (date),
1315 g_date_get_year (date));
1318 gtk_label_set_text (GTK_LABEL(w), date_buff);
1320 o = G_OBJECT(dcal->transPopup);
1321 model = GTK_LIST_STORE(g_object_get_data (o,
"model"));
1322 gtk_list_store_clear (model);
1323 for (l = dcal->marks[dcal->doc]; l; l = l->next)
1329 gtk_list_store_insert (model, &iter, INT_MAX);
1330 gtk_list_store_set (model, &iter, 0, (gdcmd->name ? gdcmd->name : _(
"(unnamed)")),
1331 1, gdcmd->info, -1);
1335 if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), NULL) == 0)
1338 gtk_list_store_insert (model, &iter, -1);
1342 while(gtk_events_pending ())
1343 gtk_main_iteration ();
1349 static const int POPUP_OFFSET = 5;
1352 popup_window_move (GncDenseCal *dcal, GdkEvent *event)
1354 GtkAllocation alloc;
1355 gdouble x_root, y_root;
1356 gint win_xpos, win_ypos;
1358 if (event->type == GDK_BUTTON_PRESS)
1360 x_root = ((GdkEventButton*)event)->x_root;
1361 y_root = ((GdkEventButton*)event)->y_root;
1365 x_root = ((GdkEventMotion*)event)->x_root;
1366 y_root = ((GdkEventMotion*)event)->y_root;
1368 win_xpos = x_root + POPUP_OFFSET;
1369 win_ypos = y_root + POPUP_OFFSET;
1371 gtk_widget_get_allocation (GTK_WIDGET(dcal->transPopup), &alloc);
1373 if (x_root + POPUP_OFFSET + alloc.width > dcal->screen_width)
1374 win_xpos = x_root - 2 - alloc.width;
1376 if (y_root + POPUP_OFFSET + alloc.height > dcal->screen_height)
1377 win_ypos = y_root - 2 - alloc.height;
1379 gtk_window_move (GTK_WINDOW(dcal->transPopup), win_xpos, win_ypos);
1383 gnc_dense_cal_button_press (GtkWidget *widget,
1384 GdkEventButton *evt)
1386 GdkWindow *win = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
1387 GdkMonitor *mon = gdk_display_get_monitor_at_window (gtk_widget_get_display (widget), win);
1388 GdkRectangle work_area_size;
1389 GncDenseCal *dcal = GNC_DENSE_CAL(widget);
1391 gdk_monitor_get_workarea (mon, &work_area_size);
1393 dcal->screen_width = work_area_size.width;
1394 dcal->screen_height = work_area_size.height;
1396 dcal->doc = wheres_this (dcal, evt->x, evt->y);
1397 dcal->showPopup = ~(dcal->showPopup);
1398 if (dcal->showPopup && dcal->doc >= 0)
1406 gtk_window_move (GTK_WINDOW(dcal->transPopup), evt->x_root + POPUP_OFFSET,
1407 evt->y_root + POPUP_OFFSET);
1409 populate_hover_window (dcal);
1410 gtk_widget_queue_resize (GTK_WIDGET(dcal->transPopup));
1411 gtk_widget_show_all (GTK_WIDGET(dcal->transPopup));
1413 popup_window_move (dcal, (GdkEvent*)evt);
1418 gtk_widget_hide (GTK_WIDGET(dcal->transPopup));
1424 gnc_dense_cal_motion_notify (GtkWidget *widget,
1425 GdkEventMotion *event)
1430 GdkModifierType unused2;
1432 dcal = GNC_DENSE_CAL(widget);
1433 if (!dcal->showPopup)
1439 GdkSeat *seat = gdk_display_get_default_seat (gdk_window_get_display (event->window));
1440 GdkDevice *pointer = gdk_seat_get_pointer (seat);
1442 gdk_window_get_device_position (event->window, pointer, &unused, &unused, &unused2);
1445 doc = wheres_this (dcal, event->x, event->y);
1448 if (dcal->doc != doc)
1451 populate_hover_window (dcal);
1452 gtk_widget_queue_resize (GTK_WIDGET(dcal->transPopup));
1453 gtk_widget_show_all (GTK_WIDGET(dcal->transPopup));
1455 popup_window_move (dcal, (GdkEvent*)event);
1460 gtk_widget_hide (GTK_WIDGET(dcal->transPopup));
1467 _gdc_view_option_changed (GtkComboBox *widget, gpointer user_data)
1470 GtkTreeModel *model;
1473 model = GTK_TREE_MODEL(gtk_combo_box_get_model (widget));
1474 if (!gtk_combo_box_get_active_iter (widget, &iter))
1476 gtk_tree_model_get (model, &iter, VIEW_OPTS_COLUMN_NUM_MONTHS, &months_val, -1);
1477 DEBUG(
"changing to %d months", months_val);
1478 gnc_dense_cal_set_num_months (GNC_DENSE_CAL(user_data), months_val);
1482 day_width_at (GncDenseCal *dcal, guint xScale)
1484 return xScale + MINOR_BORDER_SIZE;
1488 day_width (GncDenseCal *dcal)
1490 return day_width_at (dcal, dcal->x_scale);
1494 day_height_at (GncDenseCal *dcal, guint yScale)
1496 return yScale + MINOR_BORDER_SIZE;
1500 day_height (GncDenseCal *dcal)
1502 return day_height_at (dcal, dcal->y_scale);
1506 week_width_at (GncDenseCal *dcal, guint xScale)
1508 return day_width_at (dcal, xScale) * 7;
1512 week_width (GncDenseCal *dcal)
1514 return week_width_at (dcal, dcal->x_scale);
1518 week_height_at (GncDenseCal *dcal, guint yScale)
1520 return day_height_at (dcal, yScale);
1524 week_height (GncDenseCal *dcal)
1526 return week_height_at (dcal, dcal->y_scale);
1530 col_width_at (GncDenseCal *dcal, guint xScale)
1532 return (week_width_at (dcal, xScale)
1533 + dcal->month_side_bar_width
1538 col_width (GncDenseCal *dcal)
1540 return col_width_at (dcal, dcal->x_scale);
1544 col_height (GncDenseCal *dcal)
1546 return week_height (dcal) * num_weeks_per_col (dcal);
1550 num_cols (GncDenseCal *dcal)
1552 return ceil ((
float)dcal->numMonths / (
float)dcal->monthsPerCol);
1556 num_weeks (GncDenseCal *dcal)
1558 return dcal->num_weeks;
1562 int num_weeks_per_col (GncDenseCal *dcal)
1564 int num_weeks_toRet, numCols, i;
1566 int startWeek, endWeek;
1568 start = g_date_new ();
1569 end = g_date_new ();
1571 num_weeks_toRet = 0;
1572 numCols = num_cols (dcal);
1574 for (i = 0; i < numCols; i++)
1576 g_date_set_dmy (start, 1,
1578 + (i * dcal->monthsPerCol)) % 12)
1580 dcal->year + floor ((dcal->month - 1
1581 + (i * dcal->monthsPerCol))
1587 g_date_add_months (end, MIN(dcal->numMonths,
1588 MIN(dcal->monthsPerCol,
1591 * dcal->monthsPerCol))));
1592 g_date_subtract_days (end, 1);
1593 startWeek = get_week_of_year (dcal, start);
1594 endWeek = get_week_of_year (dcal, end);
1596 if (endWeek < startWeek)
1597 endWeek += get_weeks_in_year (dcal, g_date_get_year (start));
1599 num_weeks_toRet = MAX(num_weeks_toRet, (endWeek - startWeek) + 1);
1601 g_date_free (start);
1603 return num_weeks_toRet;
1613 month_coords (GncDenseCal *dcal,
int monthOfCal, GList **outList)
1615 gint weekRow, colNum, previousMonthsInCol, monthOffset;
1617 GDate *startD, *endD;
1619 gint startWk, endWk;
1621 if (monthOfCal > dcal->numMonths)
1624 colNum = floor (monthOfCal / dcal->monthsPerCol);
1625 monthOffset = colNum * dcal->monthsPerCol;
1626 previousMonthsInCol = MAX(0, (monthOfCal % dcal->monthsPerCol));
1628 startD = g_date_new ();
1629 endD = g_date_new ();
1634 if (previousMonthsInCol > 0)
1636 g_date_set_dmy (startD, 1,
1637 ((dcal->month - 1 + monthOffset) % 12) + 1,
1638 dcal->year + floor ((dcal->month - 1 + monthOffset) / 12));
1640 startWk = get_week_of_year (dcal, startD);
1643 g_date_add_months (endD, previousMonthsInCol);
1644 g_date_subtract_days (endD, 1);
1645 endWk = get_week_of_year (dcal, endD);
1647 if (endWk < startWk)
1648 endWk += get_weeks_in_year (dcal, g_date_get_year (startD));
1652 weekRow = endWk - startWk;
1654 gint end_of_week = dcal->day_of_week_start + 6;
1655 if (end_of_week > 7)
1656 end_of_week = end_of_week - 7;
1658 if (g_date_get_weekday (endD) == end_of_week)
1662 g_date_set_dmy (startD, 1,
1663 ((dcal->month - 1 + monthOfCal) % 12) + 1,
1664 dcal->year + floor ((dcal->month - 1 + monthOfCal) / 12));
1667 g_date_add_months (endD, 1);
1668 g_date_subtract_days (endD, 1);
1672 start = (g_date_get_weekday (startD) + 7 - dcal->day_of_week_start) % 7;
1674 rect = g_new0 (GdkRectangle, 1);
1675 rect->x = dcal->leftPadding
1677 + (colNum * (col_width (dcal) + COL_BORDER_SIZE))
1678 + dcal->month_side_bar_width
1679 + (start * day_width (dcal));
1680 rect->y = dcal->topPadding
1681 + dcal->day_top_bar_height
1683 + (weekRow * week_height (dcal));
1684 rect->width = (7 - start) * day_width (dcal);
1685 rect->height = week_height (dcal);
1686 *outList = g_list_append (*outList, (gpointer)rect);
1693 gint weekStart = get_week_of_year (dcal, startD) + 1;
1694 gint weekEnd = get_week_of_year (dcal, endD);
1696 for (i = weekStart; i < weekEnd; i++)
1698 rect = g_new0 (GdkRectangle, 1);
1699 rect->x = dcal->leftPadding
1701 + dcal->month_side_bar_width
1702 + (colNum * (col_width (dcal) + COL_BORDER_SIZE));
1703 rect->y = dcal->topPadding
1704 + dcal->day_top_bar_height
1706 + ((weekRow + (i - weekStart) + 1) * week_height (dcal));
1707 rect->width = week_width (dcal);
1708 rect->height = week_height (dcal);
1710 *outList = g_list_append (*outList, (gpointer)rect);
1717 gint start_week_of_year = get_week_of_year (dcal, startD);
1718 gint end_week_of_year = get_week_of_year (dcal, endD);
1720 rect = g_new0 (GdkRectangle, 1);
1721 rect->x = dcal->leftPadding
1723 + dcal->month_side_bar_width
1724 + (colNum * (col_width (dcal) + COL_BORDER_SIZE));
1725 rect->y = dcal->topPadding
1727 + dcal->day_top_bar_height
1729 + (end_week_of_year - start_week_of_year))
1730 * week_height (dcal));
1731 rect->width = (((g_date_get_weekday (endD) + 7 - dcal->day_of_week_start) % 7) + 1) * day_width (dcal);
1732 rect->height = week_height (dcal);
1734 *outList = g_list_append (*outList, (gpointer)rect);
1738 g_date_free (startD);
1744 doc_coords (GncDenseCal *dcal,
int dayOfCal,
1745 int *x1,
int *y1,
int *x2,
int *y2)
1749 gint d_week_of_cal, top_of_col_week_of_cal;
1750 gint colNum, dayCol, weekRow;
1753 g_date_set_dmy (&d, 1, dcal->month, dcal->year);
1754 g_date_add_days (&d, dayOfCal);
1755 docMonth = g_date_get_month (&d);
1756 if (g_date_get_year (&d) != dcal->year)
1760 colNum = floor ((
float)(docMonth - dcal->month) / (
float)dcal->monthsPerCol);
1761 dayCol = g_date_get_weekday (&d) - dcal->day_of_week_start;
1764 dayCol = dayCol + 7;
1766 d_week_of_cal = get_week_of_year (dcal, &d);
1767 g_date_set_dmy (&d, 1, dcal->month, dcal->year);
1768 g_date_add_months (&d, (colNum * dcal->monthsPerCol));
1769 top_of_col_week_of_cal = get_week_of_year (dcal, &d);
1771 if (d_week_of_cal < top_of_col_week_of_cal)
1773 gint week_offset = get_weeks_in_year (dcal, dcal->year);
1774 d_week_of_cal += week_offset;
1776 weekRow = d_week_of_cal - top_of_col_week_of_cal;
1781 *x1 = dcal->leftPadding
1783 + dcal->month_side_bar_width
1784 + (colNum * (col_width (dcal) + COL_BORDER_SIZE))
1785 + (dayCol * day_width (dcal))
1786 + (day_width (dcal) / 4);
1787 *y1 = dcal->topPadding
1789 + dcal->day_top_bar_height
1790 + (weekRow * week_height (dcal))
1791 + (day_height (dcal) / 4);
1793 *x2 = *x1 + (day_width (dcal) / 2);
1794 *y2 = *y1 + (day_height (dcal) / 2);
1802 wheres_this (GncDenseCal *dcal,
int x,
int y)
1804 gint colNum, weekRow, dayCol, dayOfCal;
1806 GtkAllocation alloc;
1808 x -= dcal->leftPadding;
1809 y -= dcal->topPadding;
1811 if ((x < 0) || (y < 0))
1815 gtk_widget_get_allocation (GTK_WIDGET(dcal), &alloc);
1816 if ((x >= alloc.width)
1817 || (y >= alloc.height))
1823 if (x >= (num_cols(dcal) * (col_width (dcal) + COL_BORDER_SIZE)))
1827 if (y >= dcal->day_top_bar_height + col_height (dcal))
1833 colNum = floor (x / (col_width (dcal) + COL_BORDER_SIZE));
1835 x %= (col_width (dcal) + COL_BORDER_SIZE);
1836 x -= dcal->month_side_bar_width;
1841 if (x >= day_width (dcal) * 7)
1846 y -= dcal->day_top_bar_height;
1852 dayCol = floor ((
float)x / (
float)day_width (dcal));
1853 weekRow = floor ((
float)y / (
float)week_height (dcal));
1855 g_date_set_dmy (&startD, 1, dcal->month, dcal->year);
1857 g_date_add_months (&d, (colNum * dcal->monthsPerCol));
1859 if (dcal->day_of_week_start == G_DATE_SUNDAY)
1860 dayCol -= (g_date_get_weekday (&d) - 0) % 7;
1862 dayCol -= (g_date_get_weekday (&d) - 1) % 7;
1871 g_date_add_days (&d, dayCol + (weekRow * 7));
1876 g_date_set_dmy (&ccd, 1, dcal->month, dcal->year);
1877 g_date_add_months (&ccd, (colNum + 1) * dcal->monthsPerCol);
1878 if (g_date_get_julian (&d) >= g_date_get_julian (&ccd))
1884 dayOfCal = g_date_get_julian (&d) - g_date_get_julian (&startD);
1887 g_date_subtract_months (&d, dcal->numMonths);
1888 if (g_date_get_julian (&d) >= g_date_get_julian (&startD))
1891 DEBUG(
"%d >= %d", g_date_get_julian (&d), g_date_get_julian (&startD));
1899 gdc_get_doc_offset (GncDenseCal *dcal, GDate *d)
1905 g_date_clear (&soc, 1);
1906 g_date_set_dmy (&soc, 1, dcal->month, dcal->year);
1908 if (g_date_get_julian (d) < g_date_get_julian (&soc))
1912 toRet = g_date_get_julian (d) - g_date_get_julian (&soc);
1914 g_date_add_months (&soc, dcal->numMonths);
1915 if (g_date_get_julian (d) >= g_date_get_julian (&soc))
1922 gdc_add_tag_markings (GncDenseCal *cal, guint tag)
1925 gint num_marks, idx;
1930 name = gnc_dense_cal_model_get_name (cal->model, tag);
1931 info = gnc_dense_cal_model_get_info (cal->model, tag);
1932 num_marks = gnc_dense_cal_model_get_instance_count (cal->model, tag);
1937 dates = g_new0 (GDate*, num_marks);
1938 calDate = g_date_new_dmy (1, cal->month, cal->year);
1940 for (idx = 0; idx < num_marks; idx++)
1942 dates[idx] = g_date_new ();
1943 gnc_dense_cal_model_get_instance (cal->model, tag, idx, dates[idx]);
1946 if (g_date_valid (dates[0]))
1948 if (g_date_get_julian (dates[0]) < g_date_get_julian (calDate))
1954 _gnc_dense_cal_set_month (cal, g_date_get_month (dates[0]), FALSE);
1955 _gnc_dense_cal_set_year (cal, g_date_get_year (dates[0]), FALSE);
1957 gdc_remove_markings (cal);
1958 gdc_add_markings (cal);
1961 gdc_mark_add (cal, tag, name, info, num_marks, dates);
1965 g_warning (
"Bad date, skipped.");
1968 for (idx = 0; idx < num_marks; idx++)
1970 g_date_free (dates[idx]);
1973 g_date_free (calDate);
1980 gdc_add_markings (GncDenseCal *cal)
1982 GList *tags = gnc_dense_cal_model_get_contained (cal->model);
1984 for (GList *n = tags; n; n = n->next)
1985 gdc_add_tag_markings (cal, GPOINTER_TO_UINT(n->data));
1991 gdc_remove_markings (GncDenseCal *cal)
1993 GList *tags = gnc_dense_cal_model_get_contained (cal->model);
1995 for (GList *n = tags; n; n = n->next)
1996 gdc_mark_remove (cal, GPOINTER_TO_UINT(n->data), FALSE);
2002 gdc_model_added_cb (GncDenseCalModel *model, guint added_tag, gpointer user_data)
2004 GncDenseCal *cal = GNC_DENSE_CAL(user_data);
2005 DEBUG(
"gdc_model_added_cb update");
2006 gdc_add_tag_markings (cal, added_tag);
2010 gdc_model_update_cb (GncDenseCalModel *model, guint update_tag, gpointer user_data)
2012 GncDenseCal *cal = GNC_DENSE_CAL(user_data);
2014 DEBUG(
"gdc_model_update_cb update for tag [%d]", update_tag);
2015 num_marks = gnc_dense_cal_model_get_instance_count (cal->model, update_tag);
2017 gdc_mark_remove (cal, update_tag, num_marks==0);
2018 gdc_add_tag_markings (cal, update_tag);
2023 gdc_model_removing_cb (GncDenseCalModel *model, guint remove_tag, gpointer user_data)
2025 GncDenseCal *cal = GNC_DENSE_CAL(user_data);
2026 DEBUG(
"gdc_model_removing_cb update [%d]", remove_tag);
2027 gdc_mark_remove (cal, remove_tag, TRUE);
2031 gnc_dense_cal_set_model (GncDenseCal *cal, GncDenseCalModel *model)
2033 if (cal->model != NULL)
2035 gdc_remove_markings (cal);
2036 g_object_unref (G_OBJECT(cal->model));
2040 g_object_ref (G_OBJECT(model));
2041 g_signal_connect (G_OBJECT(cal->model),
"added", (GCallback)gdc_model_added_cb, cal);
2042 g_signal_connect (G_OBJECT(cal->model),
"update", (GCallback)gdc_model_update_cb, cal);
2043 g_signal_connect (G_OBJECT(cal->model),
"removing", (GCallback)gdc_model_removing_cb, cal);
2045 gdc_add_markings (cal);
2052 gdc_mark_add (GncDenseCal *dcal,
2066 g_error (
"0 size not allowed");
2071 newMark->name = NULL;
2073 newMark->name = g_strdup (name);
2074 newMark->info = NULL;
2076 newMark->info = g_strdup (info);
2079 DEBUG(
"saving mark with tag [%d]", newMark->tag);
2081 for (i = 0; i < size; i++)
2084 doc = gdc_get_doc_offset (dcal, d);
2087 if (doc >= dcal->numMarks)
2093 dcal->marks[doc] = g_list_append (dcal->marks[doc], newMark);
2095 GINT_TO_POINTER(doc));
2097 dcal->markData = g_list_append (dcal->markData, (gpointer)newMark);
2098 gnc_dense_cal_draw_to_buffer (dcal);
2099 gtk_widget_queue_draw (GTK_WIDGET(dcal->cal_drawing_area));
2103 gdc_mark_remove (GncDenseCal *dcal, guint mark_to_remove, gboolean redraw)
2105 GList *iter, *calendar_marks;
2110 if ((gint)mark_to_remove == -1)
2112 DEBUG(
"mark_to_remove = -1");
2117 for (iter = dcal->markData; iter != NULL; iter = iter->next)
2120 if (mark_data->tag == mark_to_remove)
2125 PINFO(
"couldn't find tag [%d]", mark_to_remove);
2128 if (mark_data == NULL)
2130 DEBUG(
"mark_data == null");
2134 for (calendar_marks = mark_data->
ourMarks; calendar_marks != NULL; calendar_marks = calendar_marks->next)
2136 day_of_cal = GPOINTER_TO_INT(calendar_marks->data);
2137 dcal->marks[day_of_cal] = g_list_remove (dcal->marks[day_of_cal], mark_data);
2140 dcal->markData = g_list_remove (dcal->markData, mark_data);
2141 g_free (mark_data->name);
2142 g_free (mark_data->info);
2147 gnc_dense_cal_draw_to_buffer (dcal);
2148 gtk_widget_queue_draw (GTK_WIDGET(dcal->cal_drawing_area));
Date and Time handling routines.
time64 gnc_dmy2time64_neutral(gint day, gint month, gint year)
Converts a day, month, and year to a time64 representing 11:00:00 UTC 11:00:00 UTC falls on the same ...
#define PINFO(format, args...)
Print an informational note.
#define DEBUG(format, args...)
Print a debugging message.
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.
void gnc_dow_abbrev(gchar *buf, int buf_len, int dow)
Localized DOW abbreviation.
GList * ourMarks
A GList of the dcal->marks indexes containing this mark.
GList * markData
A GList of gdc_mark_data structs, one for each active/valid markTag.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
All type declarations for the whole Gnucash engine.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
gint gnc_start_of_week(void)
returns an integer corresponding to locale start of week
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().