Gtkdialog Calendar

For discussions about programming, and for programming questions and advice


Moderator: Forum moderators

User avatar
Jasper
Posts: 1696
Joined: Wed Sep 07, 2022 1:20 pm
Has thanked: 867 times
Been thanked: 352 times

Re: Gtkdialog Calendar

Post by Jasper »

@greengeek

Have you tried just creating a script for the application?

From rockedge's post:

https://forum.puppylinux.com/viewtopic. ... 47#p125647

on my Fossapup95

kalendarmuks-fossapup95.png

.......... it does create a lot of text files in the a directory of images.

or as per usual am I 'barking' up the wrong tree here? :lol:

User avatar
rockedge
Site Admin
Posts: 6813
Joined: Mon Dec 02, 2019 1:38 am
Location: Connecticut,U.S.A.
Has thanked: 2965 times
Been thanked: 2793 times
Contact:

Re: Gtkdialog Calendar

Post by rockedge »

Here is the one written in C and compiled on a NoblePup64:

Screenshot(19).jpg
Screenshot(19).jpg (32.96 KiB) Viewed 292 times

Compiled from this code by @superhik:
calendar.c

Code: Select all

#include <gtk/gtk.h>
#include <glib.h>
#include <cairo.h>

typedef struct {
    GtkWidget *window;
    GtkWidget *drawing_area;
    GtkWidget *text_view;
    GtkWidget *save_button;
    GtkWidget *remove_button;
    GtkWidget *spin_year;
    GtkWidget *combo_month;
    gchar *diary_dir;
    guint current_year;
    guint current_month;
    guint selected_day;
    GHashTable *marked_dates;
} AppWidgets;

static gboolean draw_calendar(GtkWidget *widget, cairo_t *cr, AppWidgets *app) {
    const gchar *day_names[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

    guint days_in_month = g_date_get_days_in_month(app->current_month + 1, app->current_year);
    GDate *date = g_date_new_dmy(1, app->current_month + 1, app->current_year);
    GDateWeekday first_weekday = g_date_get_weekday(date);
    g_date_free(date);

    GDateTime *now = g_date_time_new_now_local();
    guint today_day = g_date_time_get_day_of_month(now);
    guint today_month = g_date_time_get_month(now);
    guint today_year = g_date_time_get_year(now);
    g_date_time_unref(now);

    GtkAllocation allocation;
    gtk_widget_get_allocation(widget, &allocation);
    gint cell_width = allocation.width / 7;
    gint cell_height = allocation.height / 7;  // Adjusted for day names
    cairo_set_source_rgba(cr, 1, 1, 1, 0.5);
    cairo_rectangle(cr, cell_width, cell_height, cell_width, cell_height);
    cairo_paint(cr);

    // Centering calculations
    gint text_width, text_height;
    cairo_text_extents_t extents;
    cairo_set_font_size(cr, cell_height * 0.4); // Font size for day names
    cairo_text_extents(cr, day_names[0], &extents);
    text_width = extents.width;
    text_height = extents.height;

    // Calculate starting point for day names to center them
    gint start_x = (cell_width - text_width) / 2;
    gint start_y = cell_height * 0.6;

    cairo_set_source_rgb(cr, 0, 0, 0);
    // Draw day names
    for (int i = 0; i < 7; i++) {
        cairo_move_to(cr, i * cell_width / 2, cell_height * 0.6 / 2);
        if (i == 6) {
            // Draw Sunday in red
            cairo_set_source_rgb(cr, 1, 0, 0);
        } else if (i == 5) {
           cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
        } else {
            cairo_set_source_rgb(cr, 0, 0, 0);
        }
        cairo_text_extents(cr, day_names[i], &extents);
        text_width = extents.width;
        cairo_move_to(cr, i * cell_width + start_x, start_y);
        cairo_show_text(cr, day_names[i]);
    }

    // Draw grid
    cairo_set_line_width(cr, 1);
    cairo_set_source_rgb(cr, 0, 0, 0);
    for (int i = 0; i < 7; i++) {
        for (int j = 1; j < 7; j++) {
            cairo_rectangle(cr, i * cell_width, j * cell_height, cell_width, cell_height);
        }
    }
    cairo_stroke(cr);
    cairo_set_font_size(cr, cell_height * 0.5); // Adjust font size proportionally
    // Draw days
    for (guint day = 1; day <= days_in_month; day++) {
        gint row = (day + first_weekday - 2) / 7 + 1;
        gint column = (day + first_weekday - 2) % 7;
        gchar *text = g_strdup_printf("%2u", day);

        // Calculate text extents for day number
        cairo_text_extents(cr, text, &extents);
        text_width = extents.width;
        text_height = extents.height;
        
        // Calculate starting point to center day number
        start_x = column * cell_width + (cell_width - text_width) / 2;
        start_y = row * cell_height + cell_height / 2 + text_height / 2;
        if (column == 6) {
            // Draw Sunday dates in red
            cairo_set_source_rgb(cr, 1, 0, 0);
        } else if (column == 5) {
           cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
        } else {
            cairo_set_source_rgb(cr, 0, 0, 0);
        }
        cairo_move_to(cr, start_x, start_y);
        cairo_show_text(cr, text);
        g_free(text);

        if (g_hash_table_contains(app->marked_dates, GINT_TO_POINTER(day))) {
            cairo_set_source_rgb(cr, 0, 0, 0); 
            // Calculate dynamic radius based on cell dimensions
            double radius = MIN(cell_width, cell_height) / 10.0;
            cairo_arc(cr, column * cell_width + cell_width - 1.5 * radius, row * cell_height + 1.5 * radius, radius, 0, 2 * G_PI);

            cairo_close_path(cr);
            cairo_fill(cr);
            cairo_set_source_rgb(cr, 0, 0, 0);
        }

        // Highlight the selected day
        if (day == app->selected_day) {
            cairo_set_source_rgba(cr, 1, 0.7, 1, 0.07);
            cairo_set_source_rgb(cr, 0, 0, 1);  // Blue color for selected day
            cairo_rectangle(cr, column * cell_width, row * cell_height, cell_width, cell_height);
            cairo_set_line_width(cr, 3);
            cairo_stroke(cr);
            cairo_set_source_rgba(cr, 0, 0.7, 1, 0.1);
            cairo_rectangle(cr, column * cell_width, row * cell_height, cell_width, cell_height);
            cairo_fill(cr);
            cairo_set_source_rgb(cr, 0, 0, 0);  // Set back to black for text
        }

        // Highlight today's date
        if (day == today_day && app->current_month + 1 == today_month && app->current_year == today_year) {
            cairo_set_source_rgba(cr, 0, 1, 0, 0.3);  // Green color with transparency
            cairo_rectangle(cr, column * cell_width, row * cell_height, cell_width, cell_height);
            cairo_fill(cr);
            cairo_set_source_rgb(cr, 0, 0, 0);  // Set back to black for text
        }

    }

    return FALSE;
}

static void mark_dates_with_entries(AppWidgets *app) {
    g_hash_table_remove_all(app->marked_dates);

    GDir *dir = g_dir_open(app->diary_dir, 0, NULL);
    if (!dir) return;

    const gchar *filename;
    while ((filename = g_dir_read_name(dir))) {
        guint entry_year, entry_month, entry_day;
        if (sscanf(filename, "%4u-%2u-%2u.txt", &entry_year, &entry_month, &entry_day) == 3) {
            if (entry_year == app->current_year && entry_month == app->current_month + 1) {
                g_hash_table_add(app->marked_dates, GINT_TO_POINTER(entry_day));
            }
        }
    }
    g_dir_close(dir);
    gtk_widget_queue_draw(app->drawing_area);
}

static void on_day_selected(GtkWidget *widget, GdkEventButton *event, AppWidgets *app) {
    guint days_in_month = g_date_get_days_in_month(app->current_month + 1, app->current_year);
    GDate *date = g_date_new_dmy(1, app->current_month + 1, app->current_year);
    GDateWeekday first_weekday = g_date_get_weekday(date);
    g_date_free(date);

    GtkAllocation allocation;
    gtk_widget_get_allocation(widget, &allocation);
    gint cell_width = allocation.width / 7;
    gint cell_height = allocation.height / 7;  // Adjusted for day names

    gint column = event->x / cell_width;
    gint row = (event->y / cell_height) - 1;  // Adjust for day names
    gint day = row * 7 + column - first_weekday + 2;

    if (day < 1 || day > days_in_month) return;

    gchar *filename = g_strdup_printf("%s/%u-%02u-%02u.txt", app->diary_dir, app->current_year, app->current_month + 1, day);
    gchar *content = NULL;
    g_file_get_contents(filename, &content, NULL, NULL);
    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(app->text_view));
    gtk_text_buffer_set_text(buffer, content ? content : "", -1);
    g_free(content);
    g_free(filename);

    app->selected_day = day;
    mark_dates_with_entries(app);
}

static void on_save_clicked(GtkButton *button, AppWidgets *app) {
    if (app->selected_day == 0) return; // No day selected

    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(app->text_view));
    GtkTextIter start, end;
    gtk_text_buffer_get_bounds(buffer, &start, &end);
    gchar *content = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);

    gchar *filename = g_strdup_printf("%s/%u-%02u-%02u.txt", app->diary_dir, app->current_year, app->current_month + 1, app->selected_day);
    g_file_set_contents(filename, content, -1, NULL);
    g_free(filename);
    g_free(content);

    mark_dates_with_entries(app);
}

static void on_month_changed(GtkComboBox *combo, AppWidgets *app) {
    app->current_month = gtk_combo_box_get_active(combo);
    mark_dates_with_entries(app);
}

static void on_year_changed(GtkSpinButton *spin, AppWidgets *app) {
    app->current_year = gtk_spin_button_get_value_as_int(spin);
    mark_dates_with_entries(app);
}

static void on_remove_entry_clicked(GtkWidget *button, AppWidgets *app) {
    if (app->selected_day == 0) return;

    gchar *filename = g_strdup_printf("%s/%u-%02u-%02u.txt", app->diary_dir, app->current_year, app->current_month + 1, app->selected_day);
    remove(filename);
    g_free(filename);

    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(app->text_view));
    gtk_text_buffer_set_text(buffer, "", -1);

    mark_dates_with_entries(app);
}

static void on_today_button_clicked(GtkWidget *button, AppWidgets *app) {
    GDateTime *now = g_date_time_new_now_local();
    guint today_year = g_date_time_get_year(now);
    guint today_month = g_date_time_get_month(now) - 1; // Adjust for zero-based index
    guint today_day = g_date_time_get_day_of_month(now);
    g_date_time_unref(now);

    // Update the current year, month, and selected day
    app->current_year = today_year;
    app->current_month = today_month;
    app->selected_day = today_day;

    // Update the spin button for the year
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(app->spin_year), app->current_year);

    // Update the combo box for the month
    gtk_combo_box_set_active(GTK_COMBO_BOX(app->combo_month), app->current_month);

    // Redraw the calendar
    gtk_widget_queue_draw(app->drawing_area);

    // Mark dates with entries
    mark_dates_with_entries(app);
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);

    AppWidgets *app = g_slice_new(AppWidgets);

    // Get the current date
    GDateTime *now = g_date_time_new_now_local();
    app->current_year = g_date_time_get_year(now);
    app->current_month = g_date_time_get_month(now) - 1; // g_date_time_get_month() is 1-based
    app->selected_day = g_date_time_get_day_of_month(now);
    g_date_time_unref(now);

    app->diary_dir = g_build_filename(g_get_user_data_dir(), "diary_entries", NULL);
    g_mkdir_with_parents(app->diary_dir, 0755);
    app->marked_dates = g_hash_table_new(g_direct_hash, g_direct_equal);

    app->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(app->window), "Custom Calendar Widget");
    gtk_window_set_default_size(GTK_WINDOW(app->window), 500, 600);
    gtk_window_set_icon_name(GTK_WINDOW(app->window), "xfcalendar");

    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
    gtk_container_add(GTK_CONTAINER(app->window), vbox);

    GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

    app->spin_year = gtk_spin_button_new_with_range(1900, 2100, 1);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(app->spin_year), app->current_year);
    gtk_box_pack_start(GTK_BOX(hbox), app->spin_year, FALSE, FALSE, 0);

    app->combo_month = gtk_combo_box_text_new();
    const gchar *months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
    for (int i = 0; i < 12; i++) {
        gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(app->combo_month), months[i]);
    }
    gtk_combo_box_set_active(GTK_COMBO_BOX(app->combo_month), app->current_month);
    gtk_box_pack_start(GTK_BOX(hbox), app->combo_month, FALSE, FALSE, 0);

    GtkWidget *current_day_button = gtk_button_new_with_label("Today");
    g_signal_connect(current_day_button, "clicked", G_CALLBACK(on_today_button_clicked), app);
    gtk_box_pack_start(GTK_BOX(hbox), current_day_button, FALSE, FALSE, 0);

    app->drawing_area = gtk_drawing_area_new();
    gtk_box_pack_start(GTK_BOX(vbox), app->drawing_area, TRUE, TRUE, 0);
    gtk_widget_set_size_request(app->drawing_area, 500, 350);
    g_signal_connect(app->drawing_area, "draw", G_CALLBACK(draw_calendar), app);
    g_signal_connect(app->drawing_area, "button-press-event", G_CALLBACK(on_day_selected), app);
    gtk_widget_add_events(app->drawing_area, GDK_BUTTON_PRESS_MASK);

    GtkWidget *text_label = gtk_label_new("Date Entry:");
    gtk_box_pack_start(GTK_BOX(vbox), text_label, FALSE, FALSE, 0);

    app->text_view = gtk_text_view_new();
    gtk_box_pack_start(GTK_BOX(vbox), app->text_view, TRUE, TRUE, 0);

    GtkWidget *hboxb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
    gtk_container_add(GTK_CONTAINER(vbox), hboxb);

    app->remove_button = gtk_button_new_with_label("Remove Entry");
    g_signal_connect(app->remove_button, "clicked", G_CALLBACK(on_remove_entry_clicked), app);
    gtk_box_pack_start(GTK_BOX(hboxb), app->remove_button, FALSE, FALSE, 0);

    app->save_button = gtk_button_new_with_label("Save Entry");
    gtk_box_pack_start(GTK_BOX(hboxb), app->save_button, FALSE, FALSE, 0);
    g_signal_connect(app->save_button, "clicked", G_CALLBACK(on_save_clicked), app);

    g_signal_connect(app->spin_year, "value-changed", G_CALLBACK(on_year_changed), app);
    g_signal_connect(app->combo_month, "changed", G_CALLBACK(on_month_changed), app);

    gtk_widget_show_all(app->window);
    g_signal_connect(app->window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    mark_dates_with_entries(app);

    gtk_main();

    g_hash_table_destroy(app->marked_dates);
    g_free(app->diary_dir);
    g_slice_free(AppWidgets, app);

    return 0;
}

Added a button to select today's date.
Added a label "Date entry" to inform about the text entry.
Added a button to remove an entry.
This application needs an adequate name but I still haven't decided.

Attachments
calendar.tar.xz
Extract the binary!
(6.83 KiB) Downloaded 21 times
vektor_alian
Posts: 74
Joined: Thu Apr 01, 2021 12:36 am
Been thanked: 21 times

Re: Gtkdialog Calendar

Post by vektor_alian »

Hi rockedge

Thanks for the cool calendar app.

As usual I like to break things, so I saved a long one-liner (200) and restarted the app. The window was as wide as the line with no word wrap.
I then saved a multi-line (100) entry and upon restart the window bottom went down past the monitor with no way to access the buttons. (no scroll)

Conclusion: use for notes and not for essays.

That was fun

Vektor

Post Reply

Return to “Programming”