summaryrefslogtreecommitdiff
path: root/applets/clock/calendar-window.c
diff options
context:
space:
mode:
authorVictor Kareh <[email protected]>2025-10-06 16:29:57 -0400
committerVictor Kareh <[email protected]>2025-10-07 13:26:38 +0000
commit7e394e94d14e4691b40cae882633a5307d7182cd (patch)
treeb1e370eb05e66f64352892ad8f02c8ff3efa4cfe /applets/clock/calendar-window.c
parent511eb4de5bc1b3f5881400b7f6ee9c642ab686cf (diff)
downloadmate-panel-7e394e94d14e4691b40cae882633a5307d7182cd.tar.bz2
mate-panel-7e394e94d14e4691b40cae882633a5307d7182cd.tar.xz
clock: Follow EDS calendar colors and highlight current event
Evolution tracks a color for each calendar. We can fetch this to differentiate events from multiple calendars in the popup. This also marks the current event in bold and scrolls the window to make it visible to the user on load.
Diffstat (limited to 'applets/clock/calendar-window.c')
-rw-r--r--applets/clock/calendar-window.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/applets/clock/calendar-window.c b/applets/clock/calendar-window.c
index 9f794dce..b6f25abd 100644
--- a/applets/clock/calendar-window.c
+++ b/applets/clock/calendar-window.c
@@ -84,6 +84,7 @@ struct _CalendarWindowPrivate {
CalendarClient *client;
GtkWidget *appointment_list;
+ GtkWidget *appointment_tree_view;
GtkListStore *appointments_model;
GtkListStore *tasks_model;
@@ -995,6 +996,94 @@ create_hig_calendar_frame (CalendarWindow *calwin,
return create_hig_frame (calwin, title, button_label, key, callback);
}
+/* Calculate relative luminance of a color to
+ * determine if the text should be light or dark */
+static gdouble
+calculate_luminance (GdkRGBA *color)
+{
+
+ /* Use the formula from https://en.wikipedia.org/wiki/Relative_luminance */
+ return 0.2126 * color->red + 0.7152 * color->green + 0.0722 * color->blue;
+}
+
+/* Get current time in the same format as stored event times.
+ * Event times are stored as local time but interpreted as UTC when displayed,
+ * so we need to create a UTC timestamp with local time components. */
+static time_t
+get_current_time_for_appointments (void)
+{
+ GDateTime *now_dt, *utc_dt;
+ time_t current_time;
+
+ now_dt = g_date_time_new_now_local();
+ utc_dt = g_date_time_new_utc(g_date_time_get_year(now_dt),
+ g_date_time_get_month(now_dt),
+ g_date_time_get_day_of_month(now_dt),
+ g_date_time_get_hour(now_dt),
+ g_date_time_get_minute(now_dt),
+ g_date_time_get_second(now_dt));
+ current_time = g_date_time_to_unix(utc_dt);
+ g_date_time_unref(now_dt);
+ g_date_time_unref(utc_dt);
+
+ return current_time;
+}
+
+static void
+appointment_cell_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *color_string = NULL;
+ time_t start_time = 0, end_time = 0;
+ time_t current_time;
+ gboolean is_all_day = FALSE;
+ GdkRGBA bg_color;
+ gchar *bg_color_str;
+ gdouble luminance;
+
+ gtk_tree_model_get(model, iter,
+ APPOINTMENT_COLUMN_COLOR, &color_string,
+ APPOINTMENT_COLUMN_START_TIME, &start_time,
+ APPOINTMENT_COLUMN_END_TIME, &end_time,
+ APPOINTMENT_COLUMN_ALL_DAY, &is_all_day,
+ -1);
+
+ current_time = get_current_time_for_appointments();
+
+ /* Determine if this is the current event and make it bold */
+ if (start_time <= current_time && end_time > current_time && !is_all_day) {
+ g_object_set(cell, "weight", PANGO_WEIGHT_BOLD, NULL);
+ } else {
+ g_object_set(cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
+ }
+
+ /* Set background color from Evolution data */
+ if (color_string && *color_string && gdk_rgba_parse(&bg_color, color_string)) {
+ bg_color_str = gdk_rgba_to_string(&bg_color);
+ g_object_set(cell, "cell-background", bg_color_str, NULL);
+ g_free(bg_color_str);
+
+ /* Calculate luminance to determine if we need light or dark text */
+ luminance = calculate_luminance(&bg_color);
+ if (luminance > 0.5) {
+ /* Light background - use dark text */
+ g_object_set(cell, "foreground", "#000000", NULL);
+ } else {
+ /* Dark background - use light text */
+ g_object_set(cell, "foreground", "#FFFFFF", NULL);
+ }
+ } else {
+ g_object_set(cell,
+ "cell-background-set", FALSE,
+ "foreground-set", FALSE,
+ NULL);
+ }
+
+ g_free(color_string);
+}
static GtkWidget *
create_appointment_list (CalendarWindow *calwin,
@@ -1013,12 +1102,18 @@ create_appointment_list (CalendarWindow *calwin,
gtk_tree_view_set_model (GTK_TREE_VIEW (list),
GTK_TREE_MODEL (calwin->priv->appointments_filter));
+ /* Store tree view reference for cell data function */
+ calwin->priv->appointment_tree_view = list;
+
column = gtk_tree_view_column_new ();
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, cell, FALSE);
gtk_tree_view_column_set_attributes (column, cell,
"text", APPOINTMENT_COLUMN_START_TEXT,
NULL);
+ gtk_tree_view_column_set_cell_data_func (column, cell,
+ appointment_cell_data_func,
+ calwin, NULL);
cell = gtk_cell_renderer_text_new ();
g_object_set (cell,
@@ -1030,6 +1125,10 @@ create_appointment_list (CalendarWindow *calwin,
gtk_tree_view_column_set_attributes (column, cell,
"text", APPOINTMENT_COLUMN_SUMMARY,
NULL);
+ gtk_tree_view_column_set_cell_data_func (column, cell,
+ appointment_cell_data_func,
+ calwin, NULL);
+
gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
@@ -1328,6 +1427,42 @@ handle_appointments_changed (CalendarWindow *calwin)
update_frame_visibility (calwin->priv->appointment_list,
GTK_TREE_MODEL (calwin->priv->appointments_filter));
+
+ /* Auto-scroll to next upcoming event */
+ if (calwin->priv->appointment_tree_view) {
+ GtkTreeView *tree_view = GTK_TREE_VIEW(calwin->priv->appointment_tree_view);
+ GtkTreeModel *model = GTK_TREE_MODEL(calwin->priv->appointments_filter);
+ GtkTreeIter iter;
+ time_t now;
+ gboolean found = FALSE;
+
+ now = get_current_time_for_appointments();
+
+ /* Find first current or future event */
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ time_t event_start, event_end;
+ gboolean is_all_day;
+ gtk_tree_model_get(model, &iter,
+ APPOINTMENT_COLUMN_START_TIME, &event_start,
+ APPOINTMENT_COLUMN_END_TIME, &event_end,
+ APPOINTMENT_COLUMN_ALL_DAY, &is_all_day,
+ -1);
+ /* Check if this is a current event (happening now) or future event */
+ if ((event_start <= now && event_end > now && !is_all_day) || event_start >= now) {
+ found = TRUE;
+ break;
+ }
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ if (found) {
+ GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
+ /* Scroll to center (0.5 puts it in the middle) */
+ gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, 0.5, 0.0);
+ gtk_tree_path_free(path);
+ }
+ }
}
static void