/**
* The GtkPaperTape object
* see gtkpapertape.h for more description and usage information.
*
* Copyright (C) 2008 Sven Köppel
*
* This program is free software; you can redistribute
* it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, see
* .
*
**/
#include
#include "gtkpapertape.h"
#include "lochstreifen.h"
#include
#include
#include
#define GTK_PAPER_TAPE_UPDATE_GTK { while (gtk_events_pending ()) gtk_main_iteration (); }
/* internals */
enum GTK_PAPER_TAPE_SIZE_CHANGED {
PAPER_TAPE_SIZE_CHANGED,
PAPER_TAPE_SIZE_NOT_CHANGED
};
/**
* a dummy structure for g_signal_connect to get both the instance from the
* calling GtkMenuButtons and from the current PaperTape and additional user
* data.
*/
struct GTK_PAPER_TAPE_INSTANCE_AND_DATA {
GtkPaperTape *papertape;
gpointer data;
};
enum GTK_PAPER_TAPE_VIEW_ACTION {
PAPER_TAPE_ZOOM_IN,
PAPER_TAPE_ZOOM_OUT,
PAPER_TAPE_ZOOM_100,
PAPER_TAPE_ZOOM_FIT,
PAPER_TAPE_ROTATE_CLOCKWISE,
PAPER_TAPE_ROTATE_ANTICLOCKWISE,
PAPER_TAPE_MIRROR_HORIZONTALLY,
PAPER_TAPE_MIRROR_VERTICALLY,
PAPER_TAPE_TOGGLE_STATUS_BAR
};
struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION {
GtkPaperTape *papertape;
enum GTK_PAPER_TAPE_VIEW_ACTION action;
};
struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* gtk_paper_tape_instance_and_data(GtkPaperTape *papertape, gpointer data) {
struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* a = g_new(struct GTK_PAPER_TAPE_INSTANCE_AND_DATA, 1);
a->papertape = papertape;
a->data = data;
return a;
};
struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION* gtk_paper_tape_instance_and_action(GtkPaperTape *papertape, enum GTK_PAPER_TAPE_VIEW_ACTION action) {
struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION* a = g_new(struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION, 1);
a->papertape = papertape;
a->action = action;
return a;
};
// Ereignisse:
//gtk_paper_tape_expose // expose_event
//gtk_paper_tape_hover // motion-notify (mausbewegungen => Meldung cursorposition => aktuelles byte hovern)
//gtk_paper_tape_scroll // scroll-event (Scrollbewegungen => weitergeben an SCROLLfenster)
// Menues (EXTERN GEBEN)
gboolean gtk_paper_tape_export(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* d);
void gtk_paper_tape_set_colors(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* d);
// Signals
gboolean gtk_paper_tape_export_png(GtkWidget *widget, GtkPaperTape *papertape);
gboolean gtk_paper_tape_export_svg(GtkWidget *widget, GtkPaperTape *papertape);
// Zeichenfunktionen, werden so bleiben
gboolean gtk_paper_tape_expose(GtkPaperTape *papertape, GdkEventExpose *event, gpointer data);
gboolean gtk_paper_tape_scroll(GtkPaperTape *papertape, GdkEventScroll *event, gpointer user_data);
gboolean gtk_paper_tape_hover(GtkPaperTape *papertape, GdkEventMotion *event, gpointer egal);
void gtk_paper_tape_change_view(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION* d);
void change_constant_gui(GtkWidget *widget, gpointer data);
void gtk_paper_tape_redraw(GtkPaperTape *papertape, enum GTK_PAPER_TAPE_SIZE_CHANGED changed);
void gtk_paper_tape_change_constants(GtkWidget *menu_widget, GtkPaperTape *papertape);
void gtk_paper_tape_message(GtkPaperTape *papertape, char *msg);
//
gboolean gtk_paper_tape_null_bytes_dialog(GtkWidget *menu_widget, GtkPaperTape *papertape);
void gtk_paper_tape_scroll_to(GtkPaperTape* papertape, int byte_number);
int fast_get_dpi();
static GtkWidgetClass *parent_class = NULL;
static void gtk_paper_tape_class_init (GtkPaperTapeClass *class) {
printf("Class Init machen (==> Signale verbinden).\n");
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = (GtkObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
parent_class = gtk_type_class (gtk_widget_get_type ());
/*
widget_class->realize = gtk_dial_realize;
widget_class->expose_event = gtk_dial_expose;
widget_class->size_request = gtk_dial_size_request;
widget_class->size_allocate = gtk_dial_size_allocate;
widget_class->button_press_event = gtk_dial_button_press;
widget_class->button_release_event = gtk_dial_button_release;
widget_class->motion_notify_event = gtk_dial_motion_notify;
*/
}
static void gtk_paper_tape_init (GtkPaperTape *widget) {
// init LOCHSTREIFEN object
widget->lochstreifen = lochstreifen_new();
// So jetzt aber Inhalt: Das Hauptwidget.
lochstreifen_set_d(widget->lochstreifen, 20);
// the statusbar:
widget->statusbar = gtk_statusbar_new();
// the drawing area where the LOCHSTREIFEN object
// is supposed to draw on
widget->draw = gtk_drawing_area_new();
printf("INIT: Wanted size: %d * %d\n",
lochstreifen_get_width(widget->lochstreifen),
lochstreifen_get_height(widget->lochstreifen) // erst mal spasseshalber
);
gtk_widget_set_size_request(widget->draw,
lochstreifen_get_width(widget->lochstreifen),
lochstreifen_get_height(widget->lochstreifen)); // erst mal spasseshalber
gtk_widget_show(widget->draw);
// GtkDrawingAreas aren't capable to scroll,
// so we add a viewport
widget->scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(widget->scroll), widget->draw);
// Scrollbalken nur anzeigen wenn benoetigt
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget->scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget->scroll), GTK_SHADOW_NONE);
gtk_widget_show(widget->scroll);
// connect signals
g_signal_connect_swapped(G_OBJECT(widget->draw), "expose_event", G_CALLBACK(gtk_paper_tape_expose), widget);
gtk_widget_add_events(widget->draw, GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK); // Mauscursor kriegen, Mausscrolling kriegen
g_signal_connect_swapped(G_OBJECT(widget->draw), "motion-notify-event", G_CALLBACK(gtk_paper_tape_hover), widget);
g_signal_connect_swapped(G_OBJECT(widget->draw), "scroll-event", G_CALLBACK(gtk_paper_tape_scroll), widget);
}
GtkWidget* gtk_paper_tape_new (GtkWidget *parent_window) {
GtkPaperTape *papertape = g_object_new (gtk_paper_tape_get_type (), NULL);
papertape->parent_window = parent_window;
return GTK_WIDGET(papertape);
}
GType gtk_paper_tape_get_type () {
static GType paper_tape_type = 0;
if (!paper_tape_type) {
static const GTypeInfo paper_tape_info = {
sizeof (GtkPaperTapeClass),
NULL, // base_init
NULL, // base_finalize
(GClassInitFunc) gtk_paper_tape_class_init,
NULL, // class finalize
NULL, // class data
sizeof (GtkPaperTape),
0, // n_preallocs
(GInstanceInitFunc) gtk_paper_tape_init,
};
paper_tape_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkPaperTape", &paper_tape_info, 0);
}
return paper_tape_type;
}
GtkWidget *gtk_paper_tape_get_whole_box(GtkPaperTape *papertape) {
/**
* This will generate a vbox which contains papertape->scroll
* and papertape->statusbar. Feel free to use this method to
* add this vbox right into your application or use
* papertape->scroll / papertape->statusbar directly.
**/
GtkWidget *box;
box = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(box), papertape->scroll, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(box), papertape->statusbar, FALSE, TRUE, 0);
gtk_widget_show_all(box);
return GTK_WIDGET(box);
}
/** Adds controls to export the current view to the target menu */
void gtk_paper_tape_menu_export(GtkPaperTape *papertape, GtkWidget *target_menu) {
g_signal_connect(G_OBJECT(fast_menuitem(target_menu, "Grafik als PNG exportieren...")),
"activate", G_CALLBACK(gtk_paper_tape_export), gtk_paper_tape_instance_and_data(papertape, "PNG"));
g_signal_connect(G_OBJECT(fast_menuitem(target_menu, "Grafik als SVG exportieren...")),
"activate", G_CALLBACK(gtk_paper_tape_export), gtk_paper_tape_instance_and_data(papertape, "SVG"));
}
/** Adds controls to change the view to the target menu */
void gtk_paper_tape_menu_view(GtkPaperTape *papertape, GtkWidget *target_menu) {
GtkWidget *menu;
menu = target_menu;
GtkWidget *menuitem;
fast_menu_tearoff(menu);
g_signal_connect(G_OBJECT(fast_stock_menuitem(target_menu, GTK_STOCK_ZOOM_IN)),
"activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ZOOM_IN)); // "+"
g_signal_connect(G_OBJECT(fast_stock_menuitem(target_menu, GTK_STOCK_ZOOM_OUT)),
"activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ZOOM_OUT)); //"-");
g_signal_connect(G_OBJECT(fast_stock_menuitem(target_menu, GTK_STOCK_ZOOM_100)),
"activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ZOOM_100)); //"=");
menuitem = gtk_check_menu_item_new_with_mnemonic("Groesse dem Fenster _anpassen");
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), FALSE);
g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(gtk_paper_tape_change_view),
gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ZOOM_FIT)); // "[");
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_show (menuitem);
papertape->menu_toggle_fit_screen = menuitem; // auch hier global speichern, weil es in einer Funktion gebraucht wird.
g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_PROPERTIES)), //"Groessenverhaeltnisse aendern...")),
"activate", G_CALLBACK(gtk_paper_tape_change_constants), papertape);
fast_menu_seperator(menu);
g_signal_connect(G_OBJECT(fast_menuitem(menu, "Drehen im Uhrzeigersinn")),
"activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ROTATE_CLOCKWISE)); // ">");
g_signal_connect(G_OBJECT(fast_menuitem(menu, "Drehen gegen den Uhrzeigersinn")),
"activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ROTATE_ANTICLOCKWISE)); //"<");
g_signal_connect(G_OBJECT(fast_menuitem(menu, "Horizontal spiegeln (Datenreihenfolge umkehren)")),
"activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_MIRROR_HORIZONTALLY)); //"|");
g_signal_connect(G_OBJECT(fast_menuitem(menu, "Vertikal spiegeln (Bitpositionen drehen)")),
"activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_MIRROR_VERTICALLY)); //"_");
fast_menu_seperator(menu);
g_signal_connect(G_OBJECT(fast_menuitem(menu, "Anzahl Nullbytes einstellen...")),
"activate", G_CALLBACK(gtk_paper_tape_null_bytes_dialog), papertape);
menuitem = gtk_check_menu_item_new_with_mnemonic("_Statusleiste anzeigen");
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(gtk_paper_tape_change_view),
gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_TOGGLE_STATUS_BAR)); // "S");
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_show (menuitem);
}
/** Adds controls to change the colors to the target menu */
void gtk_paper_tape_menu_colors(GtkPaperTape *papertape, GtkWidget *target_menu) {
GtkSizeGroup *color_sizes; // Damit Farbmenue einheitlich aussieht
GtkWidget *menu;
menu = target_menu;
color_sizes = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
fast_menu_tearoff(menu);
g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe des Lochstreifens waehlen",
"Farbe des Lochstreifens", color_cairo2gdk(papertape->lochstreifen->streifenbg), color_sizes)),
"color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->streifenbg)));
//gtk_color_button_set_color(GTK_COLOR_BUTTON(menuitem), color_cairo2gdk(papertape->lochstreifen->streifenbg));
fast_menu_seperator(menu);
g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Loecher waehlen",
"Farbe der Loecher waehlen\n(binaere Einsen)", color_cairo2gdk(papertape->lochstreifen->punched), color_sizes)),
"color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->punched)));
fast_menu_seperator(menu);
g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Nicht-Loecher waehlen",
"Farbe der Nullen waehlen\n(nicht gelochte Bits)", color_cairo2gdk(papertape->lochstreifen->notpunched), color_sizes)),
"color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->notpunched)));
fast_menu_seperator(menu);
g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Lochstreifen-Fuehrungsloecher waehlen",
"Farbe der Fuehrungs-loecher\nwaehlen (kleine Loecher)", color_cairo2gdk(papertape->lochstreifen->fuehrung), color_sizes)),
"color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->fuehrung)));
fast_menu_seperator(menu);
g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Hintergrund waehlen",
"Hintergrundfarbe waehlen\n(um dem Streifen rum)", color_cairo2gdk(papertape->lochstreifen->hintergrund), color_sizes)),
"color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->hintergrund)));
}
/**
* Die Exportfunktion fuer Lochstreifen, AUCH FUER EXTERN GEDACHT. Vielleicht sollte man dazu
* diese ungemuetliche Struktur rechts ersetzen und aus der ganzen Sache zwei Funktionen machen.
* (eine event callback fuer menuitems und eine menschenlesbar exportierbare)
*
**/
gboolean gtk_paper_tape_export(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* d) {
gchar *format = (gchar*)(d->data);
cairo_surface_t *surface;
cairo_t *cr;
cairo_status_t status;
GtkWidget *chooser;
char *filename;
// gewuenschten Dateinamen kriegen
chooser = gtk_file_chooser_dialog_new(
g_strdup_printf("Dateiname fuer %s-Export auswaehlen", format),
GTK_WINDOW(d->papertape->parent_window),
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
NULL
);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser), TRUE);
if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) {
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
gtk_widget_destroy(chooser);
} else {
gtk_paper_tape_message(d->papertape, "Export abgebrochen!");
gtk_widget_destroy(chooser);
return FALSE; // return value eh nicht wichtig weil aufgerufen von mainloop
}
lochstreifen_flush_only_start_area(d->papertape->lochstreifen); // damit der ganze Lochstreifen gezeichnet wird, vgl. expose_lochstreifen
if(strcmp(format, "PNG") == 0) {
// PNG erstellen
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
lochstreifen_get_width(d->papertape->lochstreifen),
lochstreifen_get_height(d->papertape->lochstreifen)
);
cr = cairo_create(surface);
lochstreifen_draw(d->papertape->lochstreifen, cr);
status = cairo_surface_write_to_png (surface, filename);
/** TODO: Progressbar im Filechooser hinzufuegen à la GIMP-"speichern"!
Ordentliche Messagebox bei Erfolg! */
gtk_paper_tape_message(d->papertape,
g_strdup_printf("PNG-Datei (%d x %d Pixel, 24bit) wurde exportiert: %s",
lochstreifen_get_width(d->papertape->lochstreifen),
lochstreifen_get_height(d->papertape->lochstreifen),
cairo_status_to_string(status))
);
} else if(strcmp(format, "SVG") == 0) {
// SVG erstellen
double width,height;
// SVG-Surface erwartet Breite in Punkt, 1pt = 1/72 inch
width = 72 * lochstreifen_get_width(d->papertape->lochstreifen) / (double)fast_get_dpi();
height = 72 * lochstreifen_get_height(d->papertape->lochstreifen) / (double)fast_get_dpi();
// irgendwie spinnt der Compiler und meint ohne den folgenden unsinnigen
// exipliten Typcastings "Warnung: Zuweisung erzeugt Zeiger von Ganzzahl ohne Typkonvertierung"
surface = (cairo_surface_t *)cairo_svg_surface_create((char*)filename, (double)width, (double)height);
cr = cairo_create(surface);
lochstreifen_draw(d->papertape->lochstreifen, cr);
gtk_paper_tape_message(d->papertape,
g_strdup_printf("SVG-Datei (%.2f x %.2f Punkt) wurde exportiert: %s",
width, height,
cairo_status_to_string(cairo_surface_status(surface))
)
);
} else {
/** richtigen fehler machen! */
gtk_paper_tape_message(d->papertape, "Gewuenschter Exporttyp nicht feststellbar!");
}
cairo_surface_destroy(surface);
cairo_destroy(cr);
g_free(filename);
return FALSE; /* return value not interesting cause mainloop. */
} //gtk_paper_tape_export
gboolean gtk_paper_tape_null_bytes_dialog(GtkWidget *menu_widget, GtkPaperTape *papertape) {
/**
* This method displays a dialog where there can be selected
* the number of null bytes which shall be painted before/after
* the paper tape.
* It's called as a callback from the `view' menu
*
**/
GtkWidget *dialog, *input_start, *input_end, *label;
GtkWidget *box;
dialog = gtk_message_dialog_new(GTK_WINDOW(papertape->parent_window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_OK_CANCEL,
"Nullbytes");
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"Am Anfang und am Ende der angezeigten Datei koennen\n"
"zusaetzliche \"virtuelle\" Nullbytes angezeigt werden,\n"
"die in der Datei nicht gespeichert sind. Je nach Farbeinstellung\n"
"sieht man nur Fuehrungsloecher - damit sieht der\n"
"Lochstreifen realistischer aus.");
input_start = gtk_spin_button_new_with_range(0., 10000., 1.);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(input_start), (gdouble)papertape->lochstreifen->empty_start);
input_end = gtk_spin_button_new_with_range(0., 10000., 1.);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(input_end), (gdouble)papertape->lochstreifen->empty_end);
box = gtk_table_new(2, 2, FALSE);
gtk_table_set_row_spacings(GTK_TABLE(box), 5);
label = gtk_label_new("Nullbytes am Anfang: ");
gtk_table_attach_defaults(GTK_TABLE(box), label, 0, 1, 0, 1);
gtk_table_attach_defaults(GTK_TABLE(box), input_start, 1, 2, 0, 1);
label = gtk_label_new("Nullbytes am Ende: ");
gtk_table_attach_defaults(GTK_TABLE(box), label, 0, 1, 1, 2);
gtk_table_attach_defaults(GTK_TABLE(box), input_end, 1, 2, 1, 2);
gtk_container_add(GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), box);
gtk_widget_show_all (dialog);
if(gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
papertape->lochstreifen->empty_start = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(input_start));
papertape->lochstreifen->empty_end = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(input_end));
gtk_paper_tape_message(papertape,
g_strdup_printf("Vorne werden nun %d Nullbytes angezeigt, hinten %d.",
papertape->lochstreifen->empty_start, papertape->lochstreifen->empty_end)
);
gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_CHANGED);
} else
gtk_paper_tape_message(papertape, "Anzahl der Nullbytes wurde nicht veraendert.");
gtk_widget_destroy (dialog);
return FALSE; /* return value interessiert eh niemanden. */
}
gboolean gtk_paper_tape_read_from_file(GtkPaperTape* papertape, char *filename) {
/**
* This method will make GtkPaperTape open the file `filename',
* read in its contents as data and repaint itself.
* Will return TRUE if success.
*
**/
int length; gchar *data;
gboolean ret;
GError *err;
ret = g_file_get_contents(filename, &data, (gsize*)&length, &err);
if(ret == FALSE) {
GtkWidget *error;
error = gtk_message_dialog_new (GTK_WINDOW(papertape->parent_window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"Error loading file '%s': %s",
filename, err->message);
gtk_dialog_run (GTK_DIALOG (error));
gtk_widget_destroy (error);
return FALSE;
}
gtk_paper_tape_set_data(papertape, length, (byte_t *)data);
return TRUE;
}
void gtk_paper_tape_set_data(GtkPaperTape* papertape, int length, byte_t *data) {
lochstreifen_set_data(papertape->lochstreifen, length, data, -1, -1);
gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_CHANGED);
}
void gtk_paper_tape_null_bytes(GtkPaperTape *papertape, int empty_start, int empty_end) {
if(empty_start >= 0) papertape->lochstreifen->empty_start = empty_start;
if(empty_end >= 0) papertape->lochstreifen->empty_end = empty_end;
gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_CHANGED);
}
void gtk_paper_tape_set_highlight(GtkPaperTape *papertape, int byte_number, gboolean scroll_to) {
/**
* highlight the byte number `byte_number', nothing more.
* Scrolls to byte `byte_number' and highlights it.
**/
lochstreifen_set_highlight(papertape->lochstreifen, byte_number);
if(scroll_to)
gtk_paper_tape_scroll_to(papertape, byte_number);
gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
}
void gtk_paper_tape_remove_highlight(GtkPaperTape *papertape) {
/**
* No more highlight byte.
*
**/
papertape->lochstreifen->highlight_color = NULL;
gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
}
void gtk_paper_tape_scroll_to(GtkPaperTape* papertape, int byte_number) {
/**
* Scroll to byte `byte_number', which means: CENTER it in the
* viewport!
*
* see also: gtk_paper_tape_scroll (Callback for mouse wheel scrolling)
* gtk_paper_tape_set_highlight (highlights byte)
*
**/
GtkAdjustment* adjustment;
gdouble where;
// je nach Ausrichtung -- lochstreifen_get_orientation(papertape->lochstreifen)
if(papertape->lochstreifen->drehung % 2 == 0) {
// Lochstreifen ist horizontal ausgreichtet
adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(papertape->scroll));
if(adjustment == NULL) { printf("hadjustment = NULL!\n"); return; }
} else {
// nach oben bzw. unten scrollen
adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(papertape->scroll));
if(adjustment == NULL) { printf("vadjustment = NULL!\n"); return; }
}
where = (gdouble)lochstreifen_coordinate_by_byte(papertape->lochstreifen, byte_number)
- adjustment->page_size / 2; // to get the byte in the middle of the viewport!
//printf("To: %d / %d\n", lochstreifen_coordinate_by_byte(papertape->lochstreifen, byte_number), lochstreifen_get_width(papertape->lochstreifen));
printf("Scroll is: %f | up: %f | down: %f | TO: %f\n", gtk_adjustment_get_value(adjustment), adjustment->lower, adjustment->upper, where);
// ggf. hier ein neuzeichnen fordern, weil nicht gescrollt wird!
if(where < adjustment->lower) {
where = adjustment->lower;
gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
}
if(where > adjustment->upper - adjustment->page_size) {
where = adjustment->upper;
gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
}
gtk_adjustment_set_value(adjustment, where);
}
/**
* Called as Event if window where gtkpapertape is nested is resized
* (attached to signal from drawing widget)
*
**/
gboolean gtk_paper_tape_fit(GtkWidget *draw_widget, GdkEventConfigure *event, GtkPaperTape *papertape) {
// Bei Groessenveraenderungen des Lochstreifenwidgets wird dies
// aufgerufen, wenn "an Bildschirmgroesse anpassen" gewuenscht ist
// Das Scrollwidget davon unterrichten, nur noch in einer Richtung zu scrollen, damit
// es beim Verkleinern des Fensters nicht meint, genug Platz zu haben und sich die Lochstreifen-
// groesse gar nicht anpasst.
// muss bloederweise hier gemacht werden, weil man ja nachtraeglich die Ausrichtung des
// Lochstreifens aendern koennte.
int orientation = lochstreifen_get_orientation(papertape->lochstreifen); // 1 = horizontal
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtk_widget_get_parent(gtk_widget_get_parent(papertape->draw))),
orientation == 1 ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER,
orientation == 1 ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC);
// Mindestgroessenberechnungen nach redraw_lochstreifen verlagert.
if(lochstreifen_get_orientation(papertape->lochstreifen) == 1) {
// horizontal ausgerichteter Lochstreifen => Hoehe fixieren.
lochstreifen_set_d_by_height(papertape->lochstreifen, event->height);
// Mindestdimensionssetzen verlagert nach redraw_lochstreifen,
// damit nicht Datengroessenaenderung die Sachen wieder kaputtmachen
// Mindestdimensionen: Breite festgelegt durch Daten, Hoehe variabel
} else {
// vertikal ausgerichtet => Breite fixieren
lochstreifen_set_d_by_width(papertape->lochstreifen, event->width);
// set size request verschoben nach redraw_lochstreifen, siehe oben
}
gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_CHANGED);
return FALSE;
}
/**
* Callback function for most of the GtkPaperTape Callback menus. The special structure
* GTK_PAPER_TAPE_INSTANCE_AND_ACTION encapsulates the GtkPaperTape instance and the
* wanted action (an enum structure) and is generated by the function gtk_paper_tape_instance_and_action
* (see above).
*
**/
void gtk_paper_tape_change_view(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION* d) {
gchar *status_comment = NULL; // = NULL weil er's sonst nicht rafft, wenn nicht alloziiert
enum GTK_PAPER_TAPE_STATUS_TYPE {
PAPER_TAPE_SIMPLE_TEXT,
PAPER_TAPE_ZOOM,
PAPER_TAPE_ROTATION
} status_type;
//byte_t status_type = 0; // 0 = nix ersetzen, 1 = Ansicht in % einsetzen, 2 = Drehausrichtung einsetzen
switch(d->action) {
case PAPER_TAPE_ZOOM_IN:
//if(action == '+') {
lochstreifen_set_d(d->papertape->lochstreifen, d->papertape->lochstreifen->d + 3); // vergroessern
status_comment = "Ansicht vergroessert auf %d%%";
status_type = PAPER_TAPE_ZOOM;
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(d->papertape->menu_toggle_fit_screen), FALSE); // nicht automatisch anpassen
break;
case PAPER_TAPE_ZOOM_OUT:
//} else if(action == '-') {
if(d->papertape->lochstreifen->d - 3 < 1) {
gtk_paper_tape_message(d->papertape, "Der Lochstreifen kann nicht kleiner gezeichnet werden!");
return;
} else {
lochstreifen_set_d(d->papertape->lochstreifen, d->papertape->lochstreifen->d - 3); // verkleinern
status_comment = "Ansicht verkleinert auf %d%%";
status_type = PAPER_TAPE_ZOOM;
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(d->papertape->menu_toggle_fit_screen), FALSE); // nicht automatisch anpassen
break;
}
case PAPER_TAPE_ZOOM_100:
//} else if(action == '=') { // 100% = "Lebensgroesse"
lochstreifen_set_d(d->papertape->lochstreifen,
(int)rint((float)fast_get_dpi()/(float)16)); // ein Loch ist etwa 1/16 Zoll breit
status_comment = "Wirkliche Lebensgroesse eingestellt (kann bei falscher Monitoreinstellung abweichen)";
status_type = PAPER_TAPE_SIMPLE_TEXT;
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(d->papertape->menu_toggle_fit_screen), FALSE); // nicht automatisch anpassen
break;
case PAPER_TAPE_ZOOM_FIT:
//} else if(action == '[') { // an Bildschirmgroesse anpassen
if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_widget))) {
// Checkbox wurde aktiviert
g_signal_connect(G_OBJECT(d->papertape->draw), "configure_event", G_CALLBACK(gtk_paper_tape_fit), d->papertape);
// den Container resizen damit die erste Anpassung inkraft tritt
gtk_widget_queue_resize_no_redraw(d->papertape->draw); // neugezeichnet wird unten.
status_comment = "Lochstreifen passt sich nun stets dem Anzeigebereich an";
status_type = PAPER_TAPE_SIMPLE_TEXT;
break;
} else {
// Checkbox wurde deaktiviert
g_signal_handlers_disconnect_by_func(G_OBJECT(d->papertape->draw),
G_CALLBACK(gtk_paper_tape_fit), d->papertape);
// Scrollwidget wieder zuruecksetzen.
gtk_scrolled_window_set_policy(
GTK_SCROLLED_WINDOW(gtk_widget_get_parent(gtk_widget_get_parent(d->papertape->draw))),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
return; // nichts ausgeben, weil auch durch andere Sachen aufgerufen
}
case PAPER_TAPE_TOGGLE_STATUS_BAR:
// } else if(action == 'S') { // Statusleiste einblenden/ausblenden
if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_widget))) {
// Checkbox wurde aktiviert
gtk_widget_show(d->papertape->statusbar);
status_comment = "Statusleiste wird (wieder) angezeigt.";
status_type = PAPER_TAPE_SIMPLE_TEXT;
} else gtk_widget_hide(d->papertape->statusbar);
break;
case PAPER_TAPE_ROTATE_CLOCKWISE:
//} else if(action == '>') { // Drehen mit Uhrzeigersinn
//lochstreifen_rotate(lochstreifen);
lochstreifen_set_direction(d->papertape->lochstreifen, 4, -1,-1);
status_comment = "Lochstreifen im Uhrzeigersinn gedreht (%s)";
status_type = PAPER_TAPE_ROTATION;
break;
case PAPER_TAPE_ROTATE_ANTICLOCKWISE:
//} else if(action == '<') { // Drehen gegen Uhrzeigersinn
lochstreifen_set_direction(d->papertape->lochstreifen, 5, -1,-1);
status_comment = "Lochstreifen gegen Uhrzeigersinn gedreht (%s)";
status_type = PAPER_TAPE_ROTATION;
break;
case PAPER_TAPE_MIRROR_HORIZONTALLY:
//} else if(action == '_') { // horizontal Spiegeln
lochstreifen_set_direction(d->papertape->lochstreifen, -1, 2, -1);
status_comment = "Lochstreifen wurde vertikal gespiegelt (Datenreihenfolge umgedreht, d.h. links und rechts vertauscht)";
status_type = PAPER_TAPE_SIMPLE_TEXT;
break;
case PAPER_TAPE_MIRROR_VERTICALLY:
//} else if(action == '|') { // vertikal spiegeln
lochstreifen_set_direction(d->papertape->lochstreifen, -1, -1, 2);
status_comment = "Lochstreifen wurde horizontal gespiegelt (unten und oben vertauscht)";
status_type = PAPER_TAPE_SIMPLE_TEXT;
break;
} // switch(d->action)
gtk_paper_tape_redraw(d->papertape, PAPER_TAPE_SIZE_CHANGED);
// prepare output for statusbar
if(status_comment == NULL || strlen(status_comment) == 0) return; // nothing to say
switch(status_type) {
case PAPER_TAPE_SIMPLE_TEXT:
gtk_paper_tape_message(d->papertape, status_comment);
break;
case PAPER_TAPE_ZOOM:
// % von realer Groesse angeben.
gtk_paper_tape_message(d->papertape,
g_strdup_printf(status_comment,
(int)((float)d->papertape->lochstreifen->d / ((float)fast_get_dpi()/(float)16) * 100)) // siehe action=='='
);
break;
case PAPER_TAPE_ROTATION: {
// Ausrichtung angeben als String
char *ausrichtung;
switch(d->papertape->lochstreifen->drehung) {
case 0: ausrichtung = "horizontal, von links nach rechts"; break;
case 1: ausrichtung = "vertikal, von oben nach unten"; break;
case 2: ausrichtung = "horizontal, von rechts nach links"; break;
case 3: ausrichtung = "vertikal, von unten nach oben"; break;
default: ausrichtung = "*** ERROR - ungueltige Ausrichtung";
}
gtk_paper_tape_message(d->papertape,
g_strdup_printf(status_comment, ausrichtung)
);
}
break;
} // switch(status_type)
} /* gtk_paper_tape_change_view */
void gtk_paper_tape_set_colors(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* d) {
cairo_pattern_t **target = (cairo_pattern_t **)(d->data); // Doppellink noetig weil Adressaenderung
GdkColor color;
gtk_color_button_get_color(GTK_COLOR_BUTTON(menu_widget), &color);
/*
double red, green, blue;
printf("Farben gewaehlt: R=%i G=%i B=%i\n",color.red, color.green, color.blue);
red = ((double)color.red / (double)G_MAXUINT16); // G_MAXUINT16 = 2^16 weil Farben = guint16
green = ((double)color.green / (double)G_MAXUINT16);
blue = ((double)color.blue / (double)G_MAXUINT16);
printf("Farben == R=%f G=%f B=%f\n",
red,green,blue);
*/
//if(*target != NULL) cairo_pattern_destroy(*target); // damit nicht sinnlos Speicher zugemuellt wird
*target = cairo_pattern_create_rgb(
// darauf muss man erst mal kommen: Vorher in double casten, denn sonst
// berechnet er einen unsigned short (=guint16), der einfach auf 0 ab oder
// auf 1 hochrundet. (double)(guint16/G_MAXUNIT16) gibt also stets 0,000 oder 1,000.
(double)((double)color.red / (double)G_MAXUINT16), // G_MAXUINT16 = 2^16 weil Farben = guint16
(double)((double)color.green / (double)G_MAXUINT16),
(double)((double)color.blue / (double)G_MAXUINT16)
);
//printf("%s\n", cairo_status_to_string(cairo_pattern_status(*target)));
//GdkColor *g = color_cairo2gdk(*target);
//printf("Farben zurueck: R=%d G=%d B=%d\n", g->red, g->green, g->blue);
//gtk_color_button_get_alpha(GTK_COLOR_BUTTON(widget)) / 65535);
gtk_paper_tape_redraw(d->papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
//gtk_color_button_get_alpha(GTK_COLOR_BUTTON(widget)) / 65535);
// noch mal schnell RGB-Wert fuer Statuszeile ausgeben
gtk_paper_tape_message(d->papertape,
g_strdup_printf("Farbe geaendert auf RGB (%d|%d|%d)",
(int)((double)color.red / (double)G_MAXUINT16 * 255),
(int)((double)color.red / (double)G_MAXUINT16 * 255),
(int)((double)color.red / (double)G_MAXUINT16 * 255)
)
);
}
gboolean gtk_paper_tape_hover(GtkPaperTape *papertape, GdkEventMotion *event, gpointer egal) {
/**
* display the values of the hovered byte in the statusbar while moving the mouse
* over the paper tape widget
*
**/
int byte;
// wait 4 seconds after an "important" message before spamming the
// statusbar with the mouse-on-byte-x informations
if(papertape->last_statusbar_update != NULL &&
g_timer_elapsed(papertape->last_statusbar_update, NULL) < 4) return FALSE; /* return value egal */
// flush statusbar for every case
gtk_statusbar_pop(GTK_STATUSBAR(papertape->statusbar), 0);
byte = lochstreifen_byte_by_coordinate(papertape->lochstreifen, (int)event->x, (int)event->y);
if(byte > 0) {
gchar *msg; byte_t value,bit;
value = papertape->lochstreifen->data[byte-1]; // weil ab 1 zaehlen => -1 weil array ab 0 zaehlt
// Bitdarstellung selbst machen (printf hat sowas wohl nicht)
char bitdarstellung[9]; bitdarstellung[8] = '\0'; // Nullterminierung
for(bit=0; bit<8; bit++) {bitdarstellung[bit] = (((value >> bit) & 0x01) == 0x01) ? '1':'0';}
msg = g_strdup_printf("Byte %03d von %03d: Wert dez=%03d bin=%s okt=%03o hex=%02X",
byte, papertape->lochstreifen->data_length,
value, bitdarstellung, value, value);
gtk_statusbar_push(GTK_STATUSBAR(papertape->statusbar), 0, msg);
g_free(msg);
} else if(byte == 0) {
//gtk_statusbar_push(statusbar, 0, "Zeiger befindet sich außerhalb");
// Zeiger befindet sich ausserhalb. Nichts hier ausgeben.
// d.h. Statusbar wurde exiplit geleert und bleibt leer.
} else if(byte == -1) {
gtk_statusbar_push(GTK_STATUSBAR(papertape->statusbar), 0,
"Mauszeiger befindet sich auf den zusaetzlichen Nullbytes.");
}
return FALSE;
}
void gtk_paper_tape_message(GtkPaperTape *papertape, char *msg) {
/**
* Push short messages to the statusbar. These messages will be visible
* for at least 4 seconds. Afterwards the movements of the mouse cursor
* above the papertape widget will spam the statusbar widget again. There's
* a GTimer to measure this timeout of 4 seconds.
*
**/
gtk_statusbar_pop(GTK_STATUSBAR(papertape->statusbar), 0);
gtk_statusbar_push(GTK_STATUSBAR(papertape->statusbar), 0, msg);
// Statusbar-Timer erst hier einrichten, damit nach Programmstart man nicht
// erst 4 Sekunden warten muss.
if(papertape->last_statusbar_update == NULL)
papertape->last_statusbar_update = g_timer_new(); // wird automatisch gestartet
else
g_timer_start(papertape->last_statusbar_update); // Timer zuruecksetzen
}
gboolean gtk_paper_tape_scroll(GtkPaperTape *papertape, GdkEventScroll *event, gpointer user_data) {
/**
Beim Scrollen auf dem Widget wird das GTK_SCROLLED_WINDOW (user_data) gescrollt, je nach
Ausrichtung des Streifens. Nur eindimensionales Scrolling, nicht 2d. (wer hat schon eine
Apple Mighty Mouse ;-) )
**/
GtkAdjustment* adjustment;
gdouble t;
if(papertape->lochstreifen->drehung % 2 == 0) {
// Lochstreifen ist horizontal ausgreichtet
adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(papertape->scroll));
if(adjustment == NULL) { printf("hadjustment = NULL!\n"); return FALSE; }
t = gtk_adjustment_get_value(adjustment) + papertape->lochstreifen->d * // um eine Lochbreite scrollen
((event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? -1 : 1);
// nach rechts oder links scrollen
/* );
printf("Set to %f\n", gtk_adjustment_get_value(adjustment) + (gdouble)adjustment->step_increment *
(event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? (gdouble)1 : (gdouble)-1);
*/
} else {
// nach oben bzw. unten scrollen
adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(papertape->scroll));
if(adjustment == NULL) { printf("vadjustment = NULL!\n"); return FALSE; }
t = gtk_adjustment_get_value(adjustment) + papertape->lochstreifen->d *
((event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? -1 : 1);
// nach oben oder unten scrollen
}
printf("Scroll psize: %f | Is: %f | up: %f | down: %f | shall be: %f\n", adjustment->page_size, gtk_adjustment_get_value(adjustment), adjustment->lower, adjustment->upper, t);
if(t < adjustment->lower) t = adjustment->lower;
if(t > adjustment->upper - adjustment->page_size) t = adjustment->upper;
gtk_adjustment_set_value(adjustment, t);
// geht nicht:
//g_signal_emit_by_name(lochstreifen_widget, "motion-notify-event"); // Mauszeigerbewegung simulieren => Statuszeile updaten
return FALSE; // return value egal
}
void gtk_paper_tape_change_constants(GtkWidget *menu_widget, GtkPaperTape *papertape) {
// GUI zur Aenderung der Konstanten anzeigen...
printf("Pending...\n");
/// kann soweit genutzt werden.
}
gboolean gtk_paper_tape_expose(GtkPaperTape *papertape, GdkEventExpose *event, gpointer data) {
/**
* One of the most important methods of GtkPaperTape: the expose_event
* callback which makes the LOCHSTREIFEN object paint on the
* GtkDrawingArea. Therefore we get a new cairo context at every call.
**/
cairo_t *cr;
time_t TIME;
time(&TIME);
//LOCHSTREIFEN *l;
// mal testen:
gdk_window_clear_area(papertape->draw->window, // nicht das parent_window sondern GdkWindow!
event->area.x, event->area.y, event->area.width, event->area.height);
cr = gdk_cairo_create(papertape->draw->window);
//l = (LOCHSTREIFEN *)data;
printf("%d Neuzeichnen: x|y = (%d|%d), width*height = %d * %d\n", (int)TIME,
event->area.x, event->area.y,
event->area.width, event->area.height);
// Clipping, um das Neuzeichnen zu beschleunigen. Ist das sinnvoll?
/*cairo_rectangle (cr,
event->area.x, event->area.y,
event->area.width, event->area.height);
cairo_clip (cr);*/
// gdk_cairo_rectangle() gibts uebrigens auch noch!
lochstreifen_set_only_start_area(papertape->lochstreifen,
event->area.x, event->area.y,
event->area.width, event->area.height);
lochstreifen_draw(papertape->lochstreifen, cr);
/*printf("Fertig (real width*height = %d * %d)\n", lochstreifen_get_width(lochstreifen),
lochstreifen_get_height(lochstreifen));
*/
cairo_destroy(cr);
return FALSE;
}
void gtk_paper_tape_redraw(GtkPaperTape *papertape, enum GTK_PAPER_TAPE_SIZE_CHANGED changed) {
/**
* Zentrale Funktion zum Aufrufen um den Lochstreifen manuell neu
* zu zeichnen. Wenn der Parameter TRUE ist, wird signalisiert,
* dass die Groesse des Lochstreifens sich (wie auch immer) geaendert
* hat (neue Daten, Zoom, etc. pp) -- also wird ggf. ein neues
* size request abgesetzt, je nach Zoomeinstellungen (Autozoom!)
**/
if(PAPER_TAPE_SIZE_CHANGED == changed) {
if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(papertape->menu_toggle_fit_screen))) {
// Lochstreifengroesse wird automatisch dem Fenster angepasst,
// dafuer werden die size requests anders gestellt, damit kein nervender
// Scrollbalken fuer die angepasste Dimension kommt.
// siehe transform_fit_lochstreifen
if(lochstreifen_get_orientation(papertape->lochstreifen) == 1)
// Mindestdimensionen: Breite festgelegt durch Daten, Hoehe variabel
gtk_widget_set_size_request(papertape->draw, lochstreifen_get_width(papertape->lochstreifen), -1);
else
gtk_widget_set_size_request(papertape->draw, -1, lochstreifen_get_height(papertape->lochstreifen));
} else {
// Lochstreifengroesse wird nicht automatisch dem Fenster angepasst.
// Ganz normal Breite und Hoehe wuenschen.
gtk_widget_set_size_request(papertape->draw, // neue Groesse erfordern.
lochstreifen_get_width(papertape->lochstreifen),
lochstreifen_get_height(papertape->lochstreifen));
}
} // i have resized...
gtk_widget_queue_draw(GTK_WIDGET(papertape->draw)); // neuzeichnen.
GTK_PAPER_TAPE_UPDATE_GTK; /// testweise mal SOFORT zeichnen.
}
/** *************************************************************************
FAST FUNCTIONS ("ALMOST MACROS")
** *************************************************************************/
GtkWidget *fast_color_menuitem(const GtkWidget *parentmenu, const char *dialog_title, const char *labeltext, GdkColor *initialColor, GtkSizeGroup *label_nice_sizegroup) {
/* Schnell ein Colorchooser-Button inklusive Richtextlabel in einem
Menuitem einbauen. Gibt das Colorchooserbutton-Widget zurück,
initialColor kann NULL sein. GtkSizeGroup auch. */
GtkWidget *menuitem, *menubox, *colorbutton, *label;
menuitem = gtk_menu_item_new();
menubox = gtk_hbox_new(FALSE, 4);
gtk_container_add(GTK_CONTAINER(menuitem), menubox);
gtk_widget_show(menubox);
colorbutton = gtk_color_button_new();
//gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(colorbutton), TRUE); // doch kein Alpha, das bringt nichts.
gtk_color_button_set_title(GTK_COLOR_BUTTON(colorbutton), dialog_title);
gtk_box_pack_start(GTK_BOX(menubox), colorbutton, FALSE, FALSE, 0);
gtk_widget_show(colorbutton);
label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label), labeltext);
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start(GTK_BOX(menubox), label, TRUE, TRUE, 0);
gtk_widget_show(label);
g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(gtk_button_clicked), colorbutton);
if(initialColor != NULL) {
//printf("Farben:\n");
//printf("\tr%i g%i b%i\n", initialColor->red, initialColor->green, initialColor->blue);
gtk_color_button_set_color(GTK_COLOR_BUTTON(colorbutton), initialColor);
}
if(label_nice_sizegroup != NULL) {
gtk_size_group_add_widget(label_nice_sizegroup, label);
}
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
return colorbutton;
}
GdkColor *color_cairo2gdk(cairo_pattern_t *pattern) {
/** Kleine Behelfsfunktion, um von einem Cairopattern die Farbe im GDK-format zu ziehen
ALPHA wird ganz nett ignoriert :) */
GdkColor *c = malloc(sizeof(GdkColor));
double red, green, blue, alpha;
if(pattern != NULL)
cairo_pattern_get_rgba(pattern, &red, &green, &blue, &alpha);
else
{ red=1; green=1; blue=1; } // pattern ist NULL => nehmen mir mal weiss ;-)
c->red = red * 65535; // 2^16
c->green = green * 65535;
c->blue = blue * 65535;
return c;
}
GtkWidget *fast_stock_menuitem(const GtkWidget *parentmenu, const gchar *stock_id) {
/* erzeugt schnell mal ein menuitem mit dem entsprechenden Stock-Dingsda
und gibt es zurueck */
GtkWidget *menuitem;
menuitem = gtk_image_menu_item_new_from_stock(stock_id, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
gtk_widget_show (menuitem);
return menuitem;
}
GtkWidget *fast_menuitem(const GtkWidget *parentmenu, const gchar *label) {
/* erzeugt schnell mal ein menuitem mit der entsprechenden Beschriftung und gibts
zurueck */
GtkWidget *menuitem;
menuitem = gtk_menu_item_new_with_label(label);
gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
gtk_widget_show (menuitem);
return menuitem;
}
GtkWidget *fast_menu_tearoff(const GtkWidget *parentmenu) {
/* Schnell mal ein Abreissitem hinzufuegen */
GtkWidget *menuitem;
menuitem = gtk_tearoff_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
gtk_widget_show (menuitem);
return menuitem;
}
GtkWidget *fast_menu_seperator(const GtkWidget *parentmenu) {
/* Schnell einen Seperator */
GtkWidget *menuitem;
menuitem = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(parentmenu), menuitem);
gtk_widget_show(menuitem);
return menuitem;
}
int fast_get_dpi() {
/**
* Mal schnell die Aufloesung als Ganzzahl zurueckgeben.
* Der Rueckgabewert gibt also die Anzahl der Pixel an, die auf dem Monitor
* einen Zoll breit sein sollten...
**/
GdkScreen *screen = gdk_screen_get_default();
if(screen == NULL) {
printf("Konnte GdkScreen zwecks DPI-Auslesung nicht erkennen!\n");
return -1;
}
gdouble dpi = gdk_screen_get_resolution(screen);
if(dpi < 0) {
printf("Screenresolution (%f) nicht feststellbar\n", dpi);
return -1;
}
return (int)rint(dpi); // noch sauber runden.
}