216 lines
6.8 KiB
C
216 lines
6.8 KiB
C
#include "printer_window.h"
|
|
#include <stdbool.h>
|
|
#include "../util.h"
|
|
|
|
struct _PrinterWindow {
|
|
GtkWindowClass parent_class;
|
|
|
|
GtkScrolledWindow *scrolled_window;
|
|
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, scrolled_window);
|
|
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, uint32_t *image, uint8_t height, uint8_t top_margin, uint8_t bottom_margin, uint8_t exposure) {
|
|
g_mutex_lock(&self->surface_mutex);
|
|
|
|
size_t current_size = self->current_height * 160;
|
|
size_t new_height = self->current_height
|
|
+ top_margin * 8
|
|
+ height
|
|
+ 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 + (top_margin * 160 * 8), image, 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)
|
|
);
|
|
|
|
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));
|
|
|
|
// queue scrolling, so that it will execute after the size request
|
|
g_idle_add((GSourceFunc)scrolled_window_scroll_to_bottom, self->scrolled_window);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|