#include "printer_window.h" #include #include "util.h" struct _PrinterWindow { GtkWindowClass parent_class; GtkDrawingArea *printer_canvas; GtkButton *printer_save_button; GtkButton *printer_clear_button; uint32_t *current_image; size_t current_height; cairo_surface_t *surface; GMutex surface_mutex; char *suggestion_prefix; }; G_DEFINE_TYPE(PrinterWindow, printer_window, GTK_TYPE_WINDOW); static gboolean on_printer_draw(GtkWidget *widget, cairo_t *cr, PrinterWindow *window) { GtkStyleContext *context = gtk_widget_get_style_context(widget); g_mutex_lock(&window->surface_mutex); int width = cairo_image_surface_get_width(window->surface); int height = cairo_image_surface_get_height(window->surface); gtk_render_background(context, cr, 0, 0, width, height); gtk_render_frame(context, cr, 0, 0, width, height); cairo_scale(cr, 2.0, 2.0); cairo_set_source_surface(cr, window->surface, 0, 0); cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); cairo_paint(cr); g_mutex_unlock(&window->surface_mutex); return false; } static void on_printer_save(GtkWidget *w, PrinterWindow *self) { // This function is defined in `main.c` ... gpointer perform_atomic(gpointer (*fn)(gpointer args), gpointer args); // This is ugly, yup. bool success = perform_atomic((gpointer)(*printer_window_save), self); g_debug("File saving status: %d", success); } static void on_printer_clear(GtkWidget *w, PrinterWindow *self) { printer_window_clear(self); } static void printer_window_init(PrinterWindow *window) { gtk_widget_init_template(GTK_WIDGET(window)); set_combo_box_row_separator_func(GTK_CONTAINER(window)); printer_window_clear(window); } static void printer_window_finalize(GObject *object) { PrinterWindow *window = (PrinterWindow *) object; g_free(window->current_image); cairo_surface_destroy(window->surface); G_OBJECT_CLASS(printer_window_parent_class)->finalize(object); } static void printer_window_class_init(PrinterWindowClass *class) { gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/printer_window.ui"); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PrinterWindow, printer_canvas); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PrinterWindow, printer_save_button); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), PrinterWindow, printer_clear_button); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_printer_draw); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_printer_save); gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_printer_clear); G_OBJECT_CLASS(class)->finalize = printer_window_finalize; } PrinterWindow *printer_window_new(void) { return g_object_new(PRINTER_WINDOW_TYPE, NULL); } void printer_window_update(PrinterWindow *self, struct PrinterData *data) { g_mutex_lock(&self->surface_mutex); size_t current_size = self->current_height * 160; size_t new_height = self->current_height + data->top_margin * 8 + data->height + data->bottom_margin * 8; size_t new_size = new_height * 160 * sizeof(uint32_t); uint32_t *new_image = g_malloc(new_size); memset(new_image, 0xFF, new_size); // fill with white memcpy(new_image, self->current_image, current_size * sizeof(uint32_t)); // copy old image memcpy(new_image + current_size + (data->top_margin * 160 * 8), data->image, data->height * 160 * sizeof(uint32_t)); // copy new image g_free(self->current_image); self->current_image = new_image; self->current_height = new_height; self->surface = cairo_image_surface_create_for_data( (unsigned char*)self->current_image, CAIRO_FORMAT_RGB24, 160, self->current_height, cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, 160) ); // cairo_surface_finish(self->surface); g_mutex_unlock(&self->surface_mutex); gtk_widget_set_size_request(GTK_WIDGET(self->printer_canvas), 160 * 2, self->current_height * 2); gtk_window_present_with_time(GTK_WINDOW(self), time(NULL)); } void printer_window_clear(PrinterWindow *self) { g_mutex_lock(&self->surface_mutex); self->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 160, 0); self->current_image = NULL; self->current_height = 0; gtk_widget_set_size_request(GTK_WIDGET(self->printer_canvas), 160 * 2, 0); g_mutex_unlock(&self->surface_mutex); } bool printer_window_save(PrinterWindow *self) { GtkFileChooserNative *native = gtk_file_chooser_native_new("Save Printed Image", GTK_WINDOW(self), GTK_FILE_CHOOSER_ACTION_SAVE, "_Save", "_Cancel"); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(native), true); gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(native), true); if (self->suggestion_prefix == NULL) { gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(native), "print.png"); } else { GString *suggestion = g_string_new(self->suggestion_prefix); #if GLIB_CHECK_VERSION(2,62,0) gchar *date = g_date_time_format_iso8601(g_date_time_new_now_local()); #else GTimeVal tv; g_get_current_time(&tv); gchar *date = g_time_val_to_iso8601(&tv); #endif g_string_append_printf(suggestion, "_printout_%s.png", date); for (gsize i = 0; i < suggestion->len; i++) { if (suggestion->str[i] == ':') { suggestion->str[i] = '-'; } } gchar *basename = g_path_get_basename(suggestion->str); gchar *dirname = g_path_get_dirname(suggestion->str); gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(native), suggestion->str); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(native), basename); g_free(dirname); g_free(basename); g_string_free(suggestion, true); g_free(date); } GtkFileFilter *filter = gtk_file_filter_new(); gtk_file_filter_add_pattern(filter, "*.png"); gtk_file_filter_add_mime_type(filter, "image/png"); gtk_file_filter_set_name(filter, "PNG"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(native), filter); gint res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)); if (res == GTK_RESPONSE_ACCEPT) { const char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(native)); g_mutex_lock(&self->surface_mutex); cairo_status_t status = cairo_surface_write_to_png(self->surface, path); g_mutex_unlock(&self->surface_mutex); return status == CAIRO_STATUS_SUCCESS; } g_object_unref(native); return false; } void printer_window_set_suggestion_prefix(PrinterWindow *self, char* prefix) { if (self->suggestion_prefix != NULL) { g_free(self->suggestion_prefix); } if (prefix != NULL) { self->suggestion_prefix = g_strdup(prefix); } else { self->suggestion_prefix = NULL; } }