source: projects/visualisator/gtkpapertape.c @ 6

Last change on this file since 6 was 6, checked in by sven, 11 years ago

Completed documentation (everything in english) and licence issues (everything is
GPLv3 now).

File size: 44.4 KB
Line 
1/**
2 *  The GtkPaperTape object
3 *  see gtkpapertape.h for more description and usage information.
4 *
5 *  Copyright (C) 2008  Sven Köppel
6 *
7 *  This program is free software; you can redistribute
8 *  it and/or modify it under the terms of the GNU General
9 *  Public License as published by the Free Software
10 *  Foundation; either version 3 of the License, or (at
11 *  your option) any later version.
12 *
13 *  This program is distributed in the hope that it will
14 *  be useful, but WITHOUT ANY WARRANTY; without even the
15 *  implied warranty of MERCHANTABILITY or FITNESS FOR A
16 *  PARTICULAR PURPOSE. See the GNU General Public License
17 *  for more details.
18 *
19 *  You should have received a copy of the GNU General
20 *  Public License along with this program; if not, see
21 *  <http://www.gnu.org/licenses/>.
22 *
23 **/
24
25
26#include <stdlib.h>
27#include "gtkpapertape.h"
28#include <gtk/gtk.h>
29#include <math.h>
30#include <string.h>
31#define GTK_PAPER_TAPE_UPDATE_GTK  { while (gtk_events_pending ()) gtk_main_iteration (); }
32
33/* internals */
34
35enum GTK_PAPER_TAPE_SIZE_CHANGED {
36        PAPER_TAPE_SIZE_CHANGED,
37        PAPER_TAPE_SIZE_NOT_CHANGED
38};
39
40/**
41 * a dummy structure for g_signal_connect to get both the instance from the
42 * calling GtkMenuButtons and from the current PaperTape and additional user
43 * data.
44 */
45struct GTK_PAPER_TAPE_INSTANCE_AND_DATA {
46        GtkPaperTape *papertape;
47        gpointer data;
48};
49
50enum GTK_PAPER_TAPE_VIEW_ACTION {
51        PAPER_TAPE_ZOOM_IN,
52        PAPER_TAPE_ZOOM_OUT,
53        PAPER_TAPE_ZOOM_100,
54        PAPER_TAPE_ZOOM_FIT,
55        PAPER_TAPE_ROTATE_CLOCKWISE,
56        PAPER_TAPE_ROTATE_ANTICLOCKWISE,
57        PAPER_TAPE_MIRROR_HORIZONTALLY,
58        PAPER_TAPE_MIRROR_VERTICALLY,
59        PAPER_TAPE_TOGGLE_STATUS_BAR
60};
61
62struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION {
63        GtkPaperTape *papertape;
64        enum GTK_PAPER_TAPE_VIEW_ACTION action;
65};
66
67struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* gtk_paper_tape_instance_and_data(GtkPaperTape *papertape, gpointer data) {
68        struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* a = g_new(struct GTK_PAPER_TAPE_INSTANCE_AND_DATA, 1);
69        a->papertape = papertape;
70        a->data = data;
71        return a;
72};
73
74struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION* gtk_paper_tape_instance_and_action(GtkPaperTape *papertape, enum GTK_PAPER_TAPE_VIEW_ACTION action) {
75        struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION* a = g_new(struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION, 1);
76        a->papertape = papertape;
77        a->action = action;
78        return a;
79};
80
81// Ereignisse:
82//gtk_paper_tape_expose // expose_event
83//gtk_paper_tape_hover // motion-notify (mausbewegungen => Meldung cursorposition => aktuelles byte hovern)
84//gtk_paper_tape_scroll // scroll-event (Scrollbewegungen => weitergeben an SCROLLfenster)
85
86// Menues (EXTERN GEBEN)
87gboolean gtk_paper_tape_export(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* d);
88void gtk_paper_tape_set_colors(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* d);
89
90
91// Signals
92gboolean gtk_paper_tape_export_png(GtkWidget *widget, GtkPaperTape *papertape);
93gboolean gtk_paper_tape_export_svg(GtkWidget *widget, GtkPaperTape *papertape);
94
95// Zeichenfunktionen, werden so bleiben
96gboolean gtk_paper_tape_expose(GtkPaperTape *papertape, GdkEventExpose *event, gpointer data);
97gboolean gtk_paper_tape_scroll(GtkPaperTape *papertape, GdkEventScroll *event, gpointer user_data);
98gboolean gtk_paper_tape_hover(GtkPaperTape *papertape, GdkEventMotion *event, gpointer egal);
99
100void gtk_paper_tape_change_view(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION* d);
101
102void change_constant_gui(GtkWidget *widget, gpointer data);
103void  gtk_paper_tape_redraw(GtkPaperTape *papertape, enum GTK_PAPER_TAPE_SIZE_CHANGED changed);
104void gtk_paper_tape_change_constants(GtkWidget *menu_widget, GtkPaperTape *papertape);
105
106void gtk_paper_tape_message(GtkPaperTape *papertape, char *msg);
107
108//
109gboolean gtk_paper_tape_null_bytes_dialog(GtkWidget *menu_widget, GtkPaperTape *papertape);
110void gtk_paper_tape_scroll_to(GtkPaperTape* papertape, int byte_number);
111
112
113
114
115
116static GtkWidgetClass *parent_class = NULL;
117
118
119static void gtk_paper_tape_class_init (GtkPaperTapeClass *class) {
120        printf("Class Init machen (==> Signale verbinden).\n");
121       
122        GtkObjectClass *object_class;
123        GtkWidgetClass *widget_class;
124
125        object_class = (GtkObjectClass*) class;
126        widget_class = (GtkWidgetClass*) class;
127
128        parent_class = gtk_type_class (gtk_widget_get_type ());
129
130/*
131  widget_class->realize = gtk_dial_realize;
132  widget_class->expose_event = gtk_dial_expose;
133  widget_class->size_request = gtk_dial_size_request;
134  widget_class->size_allocate = gtk_dial_size_allocate;
135  widget_class->button_press_event = gtk_dial_button_press;
136  widget_class->button_release_event = gtk_dial_button_release;
137  widget_class->motion_notify_event = gtk_dial_motion_notify;
138*/
139
140}
141
142static void gtk_paper_tape_init (GtkPaperTape *widget) {
143        // init LOCHSTREIFEN object
144        widget->lochstreifen = lochstreifen_new();
145       
146        // So jetzt aber Inhalt: Das Hauptwidget.
147        lochstreifen_set_d(widget->lochstreifen, 20);
148       
149        // the statusbar:
150        widget->statusbar = gtk_statusbar_new();
151       
152        // the drawing area where the LOCHSTREIFEN object
153        // is supposed to draw on
154        widget->draw = gtk_drawing_area_new();
155        printf("INIT: Wanted size: %d * %d\n",
156                lochstreifen_get_width(widget->lochstreifen),
157                lochstreifen_get_height(widget->lochstreifen) // erst mal spasseshalber
158        );
159        gtk_widget_set_size_request(widget->draw,
160                lochstreifen_get_width(widget->lochstreifen),
161                lochstreifen_get_height(widget->lochstreifen)); // erst mal spasseshalber
162        gtk_widget_show(widget->draw);
163
164        // GtkDrawingAreas aren't capable to scroll,
165        // so we add a viewport
166        widget->scroll = gtk_scrolled_window_new(NULL, NULL);
167        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(widget->scroll), widget->draw);
168        // Scrollbalken nur anzeigen wenn benoetigt
169        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget->scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
170        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget->scroll), GTK_SHADOW_NONE);
171        gtk_widget_show(widget->scroll);
172       
173        // connect signals
174        g_signal_connect_swapped(G_OBJECT(widget->draw), "expose_event", G_CALLBACK(gtk_paper_tape_expose), widget);
175        gtk_widget_add_events(widget->draw, GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK); // Mauscursor kriegen, Mausscrolling kriegen
176        g_signal_connect_swapped(G_OBJECT(widget->draw), "motion-notify-event", G_CALLBACK(gtk_paper_tape_hover), widget);
177        g_signal_connect_swapped(G_OBJECT(widget->draw), "scroll-event", G_CALLBACK(gtk_paper_tape_scroll), widget);
178
179}
180
181GtkWidget* gtk_paper_tape_new (GtkWidget *parent_window) {
182        GtkPaperTape *papertape = g_object_new (gtk_paper_tape_get_type (), NULL);
183        papertape->parent_window = parent_window;
184        return GTK_WIDGET(papertape);
185}
186
187GType gtk_paper_tape_get_type () {
188        static GType paper_tape_type = 0;
189        if (!paper_tape_type) {
190                static const GTypeInfo paper_tape_info = {
191                        sizeof (GtkPaperTapeClass),
192                        NULL, // base_init
193                        NULL, // base_finalize
194                        (GClassInitFunc) gtk_paper_tape_class_init,
195                        NULL, // class finalize
196                        NULL, // class data
197                        sizeof (GtkPaperTape),
198                        0,    // n_preallocs
199                        (GInstanceInitFunc) gtk_paper_tape_init,
200                };
201                paper_tape_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkPaperTape", &paper_tape_info, 0);
202        }
203        return paper_tape_type;
204}
205
206GtkWidget *gtk_paper_tape_get_whole_box(GtkPaperTape *papertape) {
207        /**
208         * This will generate a vbox which contains papertape->scroll
209         * and papertape->statusbar. Feel free to use this method to
210         * add this vbox right into your application or use
211         * papertape->scroll / papertape->statusbar directly.
212         **/
213       
214        GtkWidget *box;
215        box = gtk_vbox_new(FALSE, 0);
216       
217        gtk_box_pack_start(GTK_BOX(box), papertape->scroll, TRUE, TRUE, 0);
218        gtk_box_pack_start(GTK_BOX(box), papertape->statusbar, FALSE, TRUE, 0);
219       
220        gtk_widget_show_all(box);
221        return GTK_WIDGET(box);
222}
223
224/** Adds controls to export the current view to the target menu */
225void gtk_paper_tape_menu_export(GtkPaperTape *papertape, GtkWidget *target_menu) {
226        g_signal_connect(G_OBJECT(fast_menuitem(target_menu, "Grafik als PNG exportieren...")),
227                "activate", G_CALLBACK(gtk_paper_tape_export), gtk_paper_tape_instance_and_data(papertape, "PNG"));
228        g_signal_connect(G_OBJECT(fast_menuitem(target_menu, "Grafik als SVG exportieren...")),
229                "activate", G_CALLBACK(gtk_paper_tape_export), gtk_paper_tape_instance_and_data(papertape, "SVG"));
230}
231
232/** Adds controls to change the view to the target menu */
233void gtk_paper_tape_menu_view(GtkPaperTape *papertape, GtkWidget *target_menu) {
234        GtkWidget *menu;
235        menu = target_menu;
236        GtkWidget *menuitem;
237       
238       
239        fast_menu_tearoff(menu);
240        g_signal_connect(G_OBJECT(fast_stock_menuitem(target_menu, GTK_STOCK_ZOOM_IN)),
241                "activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ZOOM_IN)); // "+"
242       
243        g_signal_connect(G_OBJECT(fast_stock_menuitem(target_menu, GTK_STOCK_ZOOM_OUT)),
244                "activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ZOOM_OUT)); //"-");
245        g_signal_connect(G_OBJECT(fast_stock_menuitem(target_menu, GTK_STOCK_ZOOM_100)),
246                "activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ZOOM_100)); //"=");
247        menuitem = gtk_check_menu_item_new_with_mnemonic("Groesse dem Fenster _anpassen");
248        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), FALSE);
249        g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(gtk_paper_tape_change_view),
250                gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ZOOM_FIT)); // "[");
251        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
252        gtk_widget_show (menuitem);
253        papertape->menu_toggle_fit_screen = menuitem; // auch hier global speichern, weil es in einer Funktion gebraucht wird.
254       
255        g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_PROPERTIES)), //"Groessenverhaeltnisse aendern...")),
256                "activate", G_CALLBACK(gtk_paper_tape_change_constants), papertape);
257        fast_menu_seperator(menu);
258        g_signal_connect(G_OBJECT(fast_menuitem(menu, "Drehen im Uhrzeigersinn")),
259                "activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ROTATE_CLOCKWISE)); // ">");
260        g_signal_connect(G_OBJECT(fast_menuitem(menu, "Drehen gegen den Uhrzeigersinn")),
261                "activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_ROTATE_ANTICLOCKWISE)); //"<");
262        g_signal_connect(G_OBJECT(fast_menuitem(menu, "Horizontal spiegeln (Datenreihenfolge umkehren)")),
263                "activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_MIRROR_HORIZONTALLY)); //"|");
264        g_signal_connect(G_OBJECT(fast_menuitem(menu, "Vertikal spiegeln (Bitpositionen drehen)")),
265                "activate", G_CALLBACK(gtk_paper_tape_change_view), gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_MIRROR_VERTICALLY)); //"_");
266        fast_menu_seperator(menu);
267        g_signal_connect(G_OBJECT(fast_menuitem(menu, "Anzahl Nullbytes einstellen...")),
268                "activate", G_CALLBACK(gtk_paper_tape_null_bytes_dialog), papertape);
269        menuitem = gtk_check_menu_item_new_with_mnemonic("_Statusleiste anzeigen");
270        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
271        g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(gtk_paper_tape_change_view),
272                gtk_paper_tape_instance_and_action(papertape, PAPER_TAPE_TOGGLE_STATUS_BAR)); // "S");
273        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
274        gtk_widget_show (menuitem);
275}
276
277/** Adds controls to change the colors to the target menu */
278void gtk_paper_tape_menu_colors(GtkPaperTape *papertape, GtkWidget *target_menu) {
279        GtkSizeGroup *color_sizes; // Damit Farbmenue einheitlich aussieht
280        GtkWidget *menu;
281        menu = target_menu;
282       
283        color_sizes = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
284        fast_menu_tearoff(menu);
285        g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe des Lochstreifens waehlen",
286                "Farbe des <b>Lochstreifens</b>", color_cairo2gdk(papertape->lochstreifen->streifenbg), color_sizes)),
287                "color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->streifenbg)));
288        //gtk_color_button_set_color(GTK_COLOR_BUTTON(menuitem), color_cairo2gdk(papertape->lochstreifen->streifenbg));
289       
290        fast_menu_seperator(menu);
291        g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Loecher waehlen",
292                "Farbe der <b>Loecher</b> waehlen\n(binaere Einsen)", color_cairo2gdk(papertape->lochstreifen->punched), color_sizes)),
293                "color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->punched)));
294        fast_menu_seperator(menu);
295        g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Nicht-Loecher waehlen",
296                "Farbe der <b>Nullen</b> waehlen\n(nicht gelochte Bits)", color_cairo2gdk(papertape->lochstreifen->notpunched), color_sizes)),
297                "color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->notpunched)));
298        fast_menu_seperator(menu);
299        g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Lochstreifen-Fuehrungsloecher waehlen",
300                "Farbe der <b>Fuehrungs-loecher</b>\nwaehlen (kleine Loecher)", color_cairo2gdk(papertape->lochstreifen->fuehrung), color_sizes)),
301                "color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->fuehrung)));
302        fast_menu_seperator(menu);
303        g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Hintergrund waehlen",
304                "<b>Hintergrundfarbe</b> waehlen\n(um dem Streifen rum)", color_cairo2gdk(papertape->lochstreifen->hintergrund), color_sizes)),
305                "color-set", G_CALLBACK(gtk_paper_tape_set_colors), gtk_paper_tape_instance_and_data(papertape, &(papertape->lochstreifen->hintergrund)));
306}
307
308/**
309 * Die Exportfunktion fuer Lochstreifen, AUCH FUER EXTERN GEDACHT. Vielleicht sollte man dazu
310 * diese ungemuetliche Struktur rechts ersetzen und aus der ganzen Sache zwei Funktionen machen.
311 * (eine event callback fuer menuitems und eine menschenlesbar exportierbare)
312 *
313 **/
314gboolean gtk_paper_tape_export(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* d) {
315        gchar *format = (gchar*)(d->data);
316        cairo_surface_t *surface;
317        cairo_t *cr;
318        cairo_status_t status;
319        GtkWidget *chooser;
320        char *filename;
321
322        // gewuenschten Dateinamen kriegen
323        chooser = gtk_file_chooser_dialog_new(
324                g_strdup_printf("Dateiname fuer %s-Export auswaehlen", format),
325                GTK_WINDOW(d->papertape->parent_window),
326                GTK_FILE_CHOOSER_ACTION_SAVE,
327                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
328                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
329                NULL
330        );
331        gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser), TRUE);
332
333        if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) {
334                filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
335                gtk_widget_destroy(chooser);
336        } else {
337                gtk_paper_tape_message(d->papertape, "Export abgebrochen!");
338                gtk_widget_destroy(chooser);
339                return;
340        }
341
342        lochstreifen_flush_only_start_area(d->papertape->lochstreifen); // damit der ganze Lochstreifen gezeichnet wird,  vgl. expose_lochstreifen
343        if(strcmp(format, "PNG") == 0) {
344                // PNG erstellen
345                surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
346                        lochstreifen_get_width(d->papertape->lochstreifen),
347                        lochstreifen_get_height(d->papertape->lochstreifen)
348                );
349                cr = cairo_create(surface);
350                lochstreifen_draw(d->papertape->lochstreifen, cr);
351                status = cairo_surface_write_to_png (surface, filename);
352                /** TODO: Progressbar im Filechooser hinzufuegen à la GIMP-"speichern"!
353                          Ordentliche Messagebox bei Erfolg! */
354                gtk_paper_tape_message(d->papertape,
355                        g_strdup_printf("PNG-Datei (%d x %d Pixel, 24bit) wurde exportiert: %s",
356                        lochstreifen_get_width(d->papertape->lochstreifen),
357                        lochstreifen_get_height(d->papertape->lochstreifen),
358                        cairo_status_to_string(status))
359                );
360        } else if(strcmp(format, "SVG") == 0) {
361                // SVG erstellen
362                double width,height;
363                // SVG-Surface erwartet Breite in Punkt, 1pt = 1/72 inch
364                width = 72 * lochstreifen_get_width(d->papertape->lochstreifen) / (double)fast_get_dpi();
365                height = 72 * lochstreifen_get_height(d->papertape->lochstreifen) / (double)fast_get_dpi();
366                // irgendwie spinnt der Compiler und meint ohne den folgenden unsinnigen
367                // exipliten Typcastings "Warnung: Zuweisung erzeugt Zeiger von Ganzzahl ohne Typkonvertierung"
368                surface = (cairo_surface_t *)cairo_svg_surface_create((char*)filename, (double)width, (double)height);
369                       
370                cr = cairo_create(surface);
371                lochstreifen_draw(d->papertape->lochstreifen, cr);
372
373                gtk_paper_tape_message(d->papertape,
374                        g_strdup_printf("SVG-Datei (%.2f x %.2f Punkt) wurde exportiert: %s",
375                                width, height,
376                                cairo_status_to_string(cairo_surface_status(surface))
377                        )
378                );
379        } else {
380                /** richtigen fehler machen! */
381                gtk_paper_tape_message(d->papertape, "Gewuenschter Exporttyp nicht feststellbar!");
382        }
383
384        cairo_surface_destroy(surface);
385        cairo_destroy(cr);
386        g_free(filename);
387} //gtk_paper_tape_export
388
389gboolean gtk_paper_tape_null_bytes_dialog(GtkWidget *menu_widget, GtkPaperTape *papertape) {
390        /**
391         * This method displays a dialog where there can be selected
392         * the number of null bytes which shall be painted before/after
393         * the paper tape.
394         * It's called as a callback from the `view' menu
395         *
396         **/
397        GtkWidget *dialog, *table, *input_start, *input_end, *label;
398        GtkWidget *box;
399       
400        dialog = gtk_message_dialog_new(GTK_WINDOW(papertape->parent_window),
401                GTK_DIALOG_DESTROY_WITH_PARENT,
402                GTK_MESSAGE_QUESTION,
403                GTK_BUTTONS_OK_CANCEL,
404                "Nullbytes");
405        gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
406                "Am Anfang und am Ende der angezeigten Datei koennen\n"
407                "zusaetzliche \"virtuelle\" Nullbytes angezeigt werden,\n"
408                "die in der Datei nicht gespeichert sind. Je nach Farbeinstellung\n"
409                "sieht man nur Fuehrungsloecher - damit sieht der\n"
410                "Lochstreifen realistischer aus.");
411       
412        input_start = gtk_spin_button_new_with_range(0., 10000., 1.);
413        gtk_spin_button_set_value(GTK_SPIN_BUTTON(input_start), (gdouble)papertape->lochstreifen->empty_start);
414        input_end = gtk_spin_button_new_with_range(0., 10000., 1.);
415        gtk_spin_button_set_value(GTK_SPIN_BUTTON(input_end), (gdouble)papertape->lochstreifen->empty_end);
416       
417        box = gtk_table_new(2, 2, FALSE);
418        gtk_table_set_row_spacings(GTK_TABLE(box), 5);
419        label = gtk_label_new("Nullbytes am Anfang: ");
420        gtk_table_attach_defaults(GTK_TABLE(box), label, 0, 1, 0, 1); 
421        gtk_table_attach_defaults(GTK_TABLE(box), input_start, 1, 2, 0, 1); 
422        label = gtk_label_new("Nullbytes am Ende: ");
423        gtk_table_attach_defaults(GTK_TABLE(box), label, 0, 1, 1, 2); 
424        gtk_table_attach_defaults(GTK_TABLE(box), input_end, 1, 2, 1, 2); 
425       
426        gtk_container_add(GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), box);
427        gtk_widget_show_all (dialog);
428       
429        if(gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
430                papertape->lochstreifen->empty_start = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(input_start));
431                papertape->lochstreifen->empty_end = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(input_end));
432                gtk_paper_tape_message(papertape,
433                        g_strdup_printf("Vorne werden nun %d Nullbytes angezeigt, hinten %d.",
434                                papertape->lochstreifen->empty_start, papertape->lochstreifen->empty_end)
435                );
436       
437                gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_CHANGED);
438        } else
439                gtk_paper_tape_message(papertape, "Anzahl der Nullbytes wurde nicht veraendert.");
440        gtk_widget_destroy (dialog);
441}
442
443
444gboolean gtk_paper_tape_read_from_file(GtkPaperTape* papertape, char *filename) {
445        /**
446         * This method will make GtkPaperTape open the file `filename',
447         * read in its contents as data and repaint itself.
448         * Will return TRUE if success.
449         *
450         **/
451
452        int length; gchar *data;
453        gboolean ret;
454        GError *err;
455
456        ret = g_file_get_contents(filename, &data, &length, &err);
457
458        if(ret == FALSE) {
459                GtkWidget *error;
460                error = gtk_message_dialog_new (GTK_WINDOW(papertape->parent_window),
461                        GTK_DIALOG_DESTROY_WITH_PARENT,
462                        GTK_MESSAGE_ERROR,
463                        GTK_BUTTONS_OK,
464                        "Error loading file '%s': %s",
465                        filename, err->message);
466                gtk_dialog_run (GTK_DIALOG (error));
467                gtk_widget_destroy (error);
468                return FALSE;
469        }
470
471        gtk_paper_tape_set_data(papertape, length, (byte_t *)data);
472        return TRUE;
473}
474
475void gtk_paper_tape_set_data(GtkPaperTape* papertape, int length, byte_t *data) {
476        lochstreifen_set_data(papertape->lochstreifen, length, data, -1, -1);
477        gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_CHANGED);
478}
479
480void gtk_paper_tape_null_bytes(GtkPaperTape *papertape, int empty_start, int empty_end) {
481        if(empty_start >= 0) papertape->lochstreifen->empty_start = empty_start;
482        if(empty_end >= 0) papertape->lochstreifen->empty_end = empty_end;
483        gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_CHANGED);
484}
485
486void gtk_paper_tape_set_highlight(GtkPaperTape *papertape, int byte_number, gboolean scroll_to) {
487        /**
488         * highlight the byte number `byte_number', nothing more.
489         * Scrolls to byte `byte_number' and highlights it.
490         **/
491       
492        lochstreifen_set_highlight(papertape->lochstreifen, byte_number);
493        if(scroll_to)
494                gtk_paper_tape_scroll_to(papertape, byte_number);
495        gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
496}
497
498void gtk_paper_tape_remove_highlight(GtkPaperTape *papertape) {
499        /**
500         * No more highlight byte.
501         *
502         **/
503        papertape->lochstreifen->highlight_color = NULL;
504        gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
505}
506
507void gtk_paper_tape_scroll_to(GtkPaperTape* papertape, int byte_number) {
508        /**
509         * Scroll to byte `byte_number', which means: CENTER it in the
510         * viewport!
511         *
512         * see also: gtk_paper_tape_scroll (Callback for mouse wheel scrolling)
513         *           gtk_paper_tape_set_highlight (highlights byte)
514         *
515         **/
516        GtkAdjustment* adjustment;
517        gdouble where;
518       
519        // je nach Ausrichtung -- lochstreifen_get_orientation(papertape->lochstreifen)
520        if(papertape->lochstreifen->drehung % 2 == 0) {
521                // Lochstreifen ist horizontal ausgreichtet
522                adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(papertape->scroll));
523                if(adjustment == NULL) { printf("hadjustment = NULL!\n"); return; }
524        } else {
525                // nach oben bzw. unten scrollen
526                adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(papertape->scroll));
527                if(adjustment == NULL) { printf("vadjustment = NULL!\n"); return; }
528        }
529
530        where = (gdouble)lochstreifen_coordinate_by_byte(papertape->lochstreifen, byte_number)
531                - adjustment->page_size / 2; // to get the byte in the middle of the viewport!
532
533       
534        //printf("To: %d / %d\n", lochstreifen_coordinate_by_byte(papertape->lochstreifen, byte_number), lochstreifen_get_width(papertape->lochstreifen));
535        printf("Scroll is: %f | up: %f | down: %f | TO: %f\n", gtk_adjustment_get_value(adjustment), adjustment->lower, adjustment->upper, where);
536       
537        // ggf. hier ein neuzeichnen fordern, weil nicht gescrollt wird!
538        if(where < adjustment->lower) {
539                where = adjustment->lower;
540                gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
541        }
542        if(where > adjustment->upper - adjustment->page_size) {
543                where = adjustment->upper;
544                gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
545        }
546        gtk_adjustment_set_value(adjustment, where);
547}
548
549/**
550 * Called as Event if window where gtkpapertape is nested is resized
551 * (attached to signal from drawing widget)
552 *
553 **/
554gboolean gtk_paper_tape_fit(GtkWidget *draw_widget, GdkEventConfigure *event, GtkPaperTape *papertape) {
555        // Bei Groessenveraenderungen des Lochstreifenwidgets wird dies
556        // aufgerufen, wenn "an Bildschirmgroesse anpassen" gewuenscht ist
557       
558        // Das Scrollwidget davon unterrichten, nur noch in einer Richtung zu scrollen, damit
559        // es beim Verkleinern des Fensters nicht meint, genug Platz zu haben und sich die Lochstreifen-
560        // groesse gar nicht anpasst.
561        // muss bloederweise hier gemacht werden, weil man ja nachtraeglich die Ausrichtung des
562        // Lochstreifens aendern koennte.
563        int orientation = lochstreifen_get_orientation(papertape->lochstreifen); // 1 = horizontal
564       
565        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtk_widget_get_parent(gtk_widget_get_parent(papertape->draw))),
566                orientation == 1 ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER,
567                orientation == 1 ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC);
568       
569        // Mindestgroessenberechnungen nach redraw_lochstreifen verlagert.
570        if(lochstreifen_get_orientation(papertape->lochstreifen) == 1) {
571                // horizontal ausgerichteter Lochstreifen => Hoehe fixieren.
572                lochstreifen_set_d_by_height(papertape->lochstreifen, event->height);
573                // Mindestdimensionssetzen verlagert nach redraw_lochstreifen,
574                // damit nicht Datengroessenaenderung die Sachen wieder kaputtmachen
575                // Mindestdimensionen: Breite festgelegt durch Daten, Hoehe variabel
576        } else {
577                // vertikal ausgerichtet => Breite fixieren
578                lochstreifen_set_d_by_width(papertape->lochstreifen, event->width);
579                // set size request verschoben nach redraw_lochstreifen, siehe oben
580        }
581       
582        gtk_paper_tape_redraw(papertape, PAPER_TAPE_SIZE_CHANGED);
583        return FALSE;
584}
585
586/**
587 * Callback function for most of the GtkPaperTape Callback menus. The special structure
588 * GTK_PAPER_TAPE_INSTANCE_AND_ACTION encapsulates the GtkPaperTape instance and the
589 * wanted action (an enum structure) and is generated by the function gtk_paper_tape_instance_and_action
590 * (see above).
591 *
592 **/
593void gtk_paper_tape_change_view(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_ACTION* d) {
594        gchar *status_comment = NULL; // = NULL weil er's sonst nicht rafft, wenn nicht alloziiert
595        enum GTK_PAPER_TAPE_STATUS_TYPE {
596                PAPER_TAPE_SIMPLE_TEXT,
597                PAPER_TAPE_ZOOM,
598                PAPER_TAPE_ROTATION
599        } status_type;
600        //byte_t status_type = 0; // 0 = nix ersetzen, 1 = Ansicht in % einsetzen, 2 = Drehausrichtung einsetzen
601       
602        switch(d->action) {
603                case PAPER_TAPE_ZOOM_IN:
604                        //if(action == '+') {
605                        lochstreifen_set_d(d->papertape->lochstreifen, d->papertape->lochstreifen->d + 3); // vergroessern
606                        status_comment = "Ansicht vergroessert auf %d%%";
607                        status_type = PAPER_TAPE_ZOOM;
608                        gtk_check_menu_item_set_active(
609                                GTK_CHECK_MENU_ITEM(d->papertape->menu_toggle_fit_screen), FALSE); // nicht automatisch anpassen
610                        break;
611                               
612                case PAPER_TAPE_ZOOM_OUT:
613                        //} else if(action == '-') {
614                        if(d->papertape->lochstreifen->d - 3 < 1) {
615                                gtk_paper_tape_message(d->papertape, "Der Lochstreifen kann nicht kleiner gezeichnet werden!");
616                                return;
617                        } else {
618                                lochstreifen_set_d(d->papertape->lochstreifen, d->papertape->lochstreifen->d - 3); // verkleinern
619                                status_comment = "Ansicht verkleinert auf %d%%";
620                                status_type = PAPER_TAPE_ZOOM;
621                                gtk_check_menu_item_set_active(
622                                        GTK_CHECK_MENU_ITEM(d->papertape->menu_toggle_fit_screen), FALSE); // nicht automatisch anpassen
623                                break;
624                        }
625                       
626                case PAPER_TAPE_ZOOM_100:
627                        //} else if(action == '=') { // 100% = "Lebensgroesse"
628                        lochstreifen_set_d(d->papertape->lochstreifen,
629                                (int)rint((float)fast_get_dpi()/(float)16)); // ein Loch ist etwa 1/16 Zoll breit
630                        status_comment = "Wirkliche Lebensgroesse eingestellt (kann bei falscher Monitoreinstellung abweichen)";
631                        status_type = PAPER_TAPE_SIMPLE_TEXT;
632                        gtk_check_menu_item_set_active(
633                                GTK_CHECK_MENU_ITEM(d->papertape->menu_toggle_fit_screen), FALSE); // nicht automatisch anpassen
634                        break;
635                       
636                case PAPER_TAPE_ZOOM_FIT:
637                        //} else if(action == '[') { // an Bildschirmgroesse anpassen
638                        if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_widget))) {
639                                // Checkbox wurde aktiviert
640                                g_signal_connect(G_OBJECT(d->papertape->draw), "configure_event", G_CALLBACK(gtk_paper_tape_fit), d->papertape);
641                                // den Container resizen damit die erste Anpassung inkraft tritt
642                                gtk_widget_queue_resize_no_redraw(d->papertape->draw); // neugezeichnet wird unten.
643                                status_comment = "Lochstreifen passt sich nun stets dem Anzeigebereich an";
644                                status_type = PAPER_TAPE_SIMPLE_TEXT;
645                                break;
646                        } else {
647                                // Checkbox wurde deaktiviert
648                                g_signal_handlers_disconnect_by_func(G_OBJECT(d->papertape->draw),
649                                        G_CALLBACK(gtk_paper_tape_fit), d->papertape);
650                                // Scrollwidget wieder zuruecksetzen.
651                                gtk_scrolled_window_set_policy(
652                                        GTK_SCROLLED_WINDOW(gtk_widget_get_parent(gtk_widget_get_parent(d->papertape->draw))),
653                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
654                                return; // nichts ausgeben, weil auch durch andere Sachen aufgerufen
655                        }
656                       
657                case PAPER_TAPE_TOGGLE_STATUS_BAR:
658                        // } else if(action == 'S') { // Statusleiste einblenden/ausblenden
659                        if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_widget))) {
660                                // Checkbox wurde aktiviert
661                                gtk_widget_show(d->papertape->statusbar);
662                                status_comment = "Statusleiste wird (wieder) angezeigt.";
663                                status_type = PAPER_TAPE_SIMPLE_TEXT;
664                        } else gtk_widget_hide(d->papertape->statusbar);
665                        break;
666                       
667                case PAPER_TAPE_ROTATE_CLOCKWISE:
668                        //} else if(action == '>') { // Drehen mit Uhrzeigersinn
669                        //lochstreifen_rotate(lochstreifen);
670                        lochstreifen_set_direction(d->papertape->lochstreifen, 4, -1,-1);
671                        status_comment = "Lochstreifen im Uhrzeigersinn gedreht (%s)";
672                        status_type = PAPER_TAPE_ROTATION;
673                        break;
674                       
675                case PAPER_TAPE_ROTATE_ANTICLOCKWISE:
676                        //} else if(action == '<') { // Drehen gegen Uhrzeigersinn
677                        lochstreifen_set_direction(d->papertape->lochstreifen, 5, -1,-1);
678                        status_comment = "Lochstreifen gegen Uhrzeigersinn gedreht (%s)";
679                        status_type = PAPER_TAPE_ROTATION;
680                        break;
681                       
682                case PAPER_TAPE_MIRROR_HORIZONTALLY:
683                        //} else if(action == '_') { // horizontal Spiegeln
684                        lochstreifen_set_direction(d->papertape->lochstreifen, -1, 2, -1);
685                        status_comment = "Lochstreifen wurde vertikal gespiegelt (Datenreihenfolge umgedreht, d.h. links und rechts vertauscht)";
686                        status_type = PAPER_TAPE_SIMPLE_TEXT;
687                        break;
688                       
689                case PAPER_TAPE_MIRROR_VERTICALLY:
690                        //} else if(action == '|') { // vertikal spiegeln
691                        lochstreifen_set_direction(d->papertape->lochstreifen, -1, -1, 2);
692                        status_comment = "Lochstreifen wurde horizontal gespiegelt (unten und oben vertauscht)";
693                        status_type = PAPER_TAPE_SIMPLE_TEXT;
694                        break;
695        } // switch(d->action)
696
697        gtk_paper_tape_redraw(d->papertape, PAPER_TAPE_SIZE_CHANGED);
698
699        // prepare output for statusbar
700        if(status_comment == NULL || strlen(status_comment) == 0) return; // nothing to say
701        switch(status_type) {
702                case PAPER_TAPE_SIMPLE_TEXT:
703                        gtk_paper_tape_message(d->papertape, status_comment);
704                        break;
705                       
706                case PAPER_TAPE_ZOOM:
707                        // % von realer Groesse angeben.
708                        gtk_paper_tape_message(d->papertape,
709                                g_strdup_printf(status_comment,
710                                        (int)((float)d->papertape->lochstreifen->d / ((float)fast_get_dpi()/(float)16) * 100)) // siehe action=='='
711                        );
712                        break;
713                       
714                case PAPER_TAPE_ROTATION: {
715                        // Ausrichtung angeben als String
716                        char *ausrichtung;
717                        switch(d->papertape->lochstreifen->drehung) {
718                                case 0: ausrichtung = "horizontal, von links nach rechts"; break;
719                                case 1: ausrichtung = "vertikal, von oben nach unten"; break;
720                                case 2: ausrichtung = "horizontal, von rechts nach links"; break;
721                                case 3: ausrichtung = "vertikal, von unten nach oben"; break;
722                                default: ausrichtung = "*** ERROR - ungueltige Ausrichtung";
723                        }
724                        gtk_paper_tape_message(d->papertape,
725                                g_strdup_printf(status_comment, ausrichtung)
726                        );
727                        }
728                        break;
729        } // switch(status_type)
730       
731} /* gtk_paper_tape_change_view */
732
733
734void gtk_paper_tape_set_colors(GtkWidget *menu_widget, struct GTK_PAPER_TAPE_INSTANCE_AND_DATA* d) {
735        cairo_pattern_t **target = (cairo_pattern_t **)(d->data); // Doppellink noetig weil Adressaenderung
736        GdkColor color;
737        gtk_color_button_get_color(GTK_COLOR_BUTTON(menu_widget), &color);
738
739    /*
740    double red, green, blue;
741    printf("Farben gewaehlt: R=%i G=%i B=%i\n",color.red, color.green, color.blue);
742        red = ((double)color.red / (double)G_MAXUINT16); // G_MAXUINT16 = 2^16 weil Farben = guint16
743        green = ((double)color.green / (double)G_MAXUINT16);
744        blue = ((double)color.blue / (double)G_MAXUINT16);
745
746    printf("Farben == R=%f G=%f B=%f\n",
747        red,green,blue);
748        */
749
750        //if(*target != NULL) cairo_pattern_destroy(*target); // damit nicht sinnlos Speicher zugemuellt wird
751        *target = cairo_pattern_create_rgb(
752                // darauf muss man erst mal kommen: Vorher in double casten, denn sonst
753                // berechnet er einen unsigned short (=guint16), der einfach auf 0 ab oder
754                // auf 1 hochrundet. (double)(guint16/G_MAXUNIT16) gibt also stets 0,000 oder 1,000.
755                (double)((double)color.red / (double)G_MAXUINT16), // G_MAXUINT16 = 2^16 weil Farben = guint16
756                (double)((double)color.green / (double)G_MAXUINT16),
757                (double)((double)color.blue / (double)G_MAXUINT16)
758        );
759        //printf("%s\n", cairo_status_to_string(cairo_pattern_status(*target)));
760
761        GdkColor *g = color_cairo2gdk(*target);
762        //printf("Farben zurueck: R=%d G=%d B=%d\n", g->red, g->green, g->blue);
763        //gtk_color_button_get_alpha(GTK_COLOR_BUTTON(widget)) / 65535);
764        gtk_paper_tape_redraw(d->papertape, PAPER_TAPE_SIZE_NOT_CHANGED);
765        //gtk_color_button_get_alpha(GTK_COLOR_BUTTON(widget)) / 65535);
766
767        // noch mal schnell RGB-Wert fuer Statuszeile ausgeben
768        gtk_paper_tape_message(d->papertape,
769                g_strdup_printf("Farbe geaendert auf RGB (%d|%d|%d)",
770                (int)((double)color.red / (double)G_MAXUINT16 * 255),
771                (int)((double)color.red / (double)G_MAXUINT16 * 255),
772                (int)((double)color.red / (double)G_MAXUINT16 * 255)
773                )
774        );
775}
776
777
778gboolean gtk_paper_tape_hover(GtkPaperTape *papertape, GdkEventMotion *event, gpointer egal) {
779        /**
780         * display the values of the hovered byte in the statusbar while moving the mouse
781         * over the paper tape widget
782         *
783         **/
784        gchar *msg;
785        int x,y,byte;
786       
787        // wait 4 seconds after an "important" message before spamming the
788        // statusbar with the mouse-on-byte-x informations
789        if(papertape->last_statusbar_update != NULL &&
790                g_timer_elapsed(papertape->last_statusbar_update, NULL) < 4) return;
791       
792        // flush statusbar for every case
793        gtk_statusbar_pop(GTK_STATUSBAR(papertape->statusbar), 0);
794        byte = lochstreifen_byte_by_coordinate(papertape->lochstreifen, (int)event->x, (int)event->y);
795        if(byte > 0) {
796                gchar *msg; byte_t value,bit;
797                value = papertape->lochstreifen->data[byte-1]; // weil ab 1 zaehlen => -1 weil array ab 0 zaehlt
798       
799                // Bitdarstellung selbst machen (printf hat sowas wohl nicht)
800                char bitdarstellung[9]; bitdarstellung[8] = '\0'; // Nullterminierung
801                for(bit=0; bit<8; bit++) {bitdarstellung[bit] = (((value >> bit) & 0x01) == 0x01) ? '1':'0';}
802       
803                msg = g_strdup_printf("Byte %03d von %03d: Wert dez=%03d bin=%s okt=%03o hex=%02X",
804                                byte, papertape->lochstreifen->data_length,
805                                value, bitdarstellung, value, value);
806                gtk_statusbar_push(GTK_STATUSBAR(papertape->statusbar), 0, msg);
807                g_free(msg);
808        } else if(byte == 0) {
809                //gtk_statusbar_push(statusbar, 0, "Zeiger befindet sich außerhalb");
810                // Zeiger befindet sich ausserhalb. Nichts hier ausgeben.
811                // d.h. Statusbar wurde exiplit geleert und bleibt leer.
812        } else if(byte == -1) {
813                gtk_statusbar_push(GTK_STATUSBAR(papertape->statusbar), 0,
814                "Mauszeiger befindet sich auf den zusaetzlichen Nullbytes.");
815        }
816       
817        return FALSE;
818}
819
820void gtk_paper_tape_message(GtkPaperTape *papertape, char *msg) {
821        /**
822         * Push short messages to the statusbar. These messages will be visible
823         * for at least 4 seconds. Afterwards the movements of the mouse cursor
824         * above the papertape widget will spam the statusbar widget again. There's
825         * a GTimer to measure this timeout of 4 seconds.
826         *
827         **/
828         
829        gtk_statusbar_pop(GTK_STATUSBAR(papertape->statusbar), 0);
830        gtk_statusbar_push(GTK_STATUSBAR(papertape->statusbar), 0, msg);
831        // Statusbar-Timer erst hier einrichten, damit nach Programmstart man nicht
832        // erst 4 Sekunden warten muss.
833        if(papertape->last_statusbar_update == NULL)
834                papertape->last_statusbar_update = g_timer_new(); // wird automatisch gestartet
835        else
836                g_timer_start(papertape->last_statusbar_update); // Timer zuruecksetzen
837}
838
839
840gboolean gtk_paper_tape_scroll(GtkPaperTape *papertape, GdkEventScroll *event, gpointer user_data) {
841        /**
842        Beim Scrollen auf dem Widget wird das GTK_SCROLLED_WINDOW (user_data) gescrollt, je nach
843        Ausrichtung des Streifens. Nur eindimensionales Scrolling, nicht 2d. (wer hat schon eine
844        Apple Mighty Mouse ;-) )
845        **/
846        GtkAdjustment* adjustment;
847        gdouble t;
848        if(papertape->lochstreifen->drehung % 2 == 0) {
849                // Lochstreifen ist horizontal ausgreichtet
850                adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(papertape->scroll));
851                if(adjustment == NULL) { printf("hadjustment = NULL!\n"); return FALSE; }
852                t = gtk_adjustment_get_value(adjustment) + papertape->lochstreifen->d * // um eine Lochbreite scrollen
853                        ((event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? -1 : 1);
854                        // nach rechts oder links scrollen
855        /*        );
856                printf("Set to %f\n", gtk_adjustment_get_value(adjustment) + (gdouble)adjustment->step_increment *
857                        (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? (gdouble)1 : (gdouble)-1);
858        */
859        } else {
860                // nach oben bzw. unten scrollen
861                adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(papertape->scroll));
862                if(adjustment == NULL) { printf("vadjustment = NULL!\n"); return FALSE; }
863                t = gtk_adjustment_get_value(adjustment) + papertape->lochstreifen->d * 
864                ((event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? -1 : 1);
865                // nach oben oder unten scrollen
866        }
867        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);
868       
869        if(t < adjustment->lower) t = adjustment->lower;
870        if(t > adjustment->upper - adjustment->page_size) t = adjustment->upper;
871        gtk_adjustment_set_value(adjustment, t);
872        // geht nicht:
873        //g_signal_emit_by_name(lochstreifen_widget, "motion-notify-event"); // Mauszeigerbewegung simulieren => Statuszeile updaten
874}
875
876
877
878
879void gtk_paper_tape_change_constants(GtkWidget *menu_widget, GtkPaperTape *papertape) {
880    // GUI zur Aenderung der Konstanten anzeigen...
881    printf("Pending...\n");
882    /// kann soweit genutzt werden.
883}
884
885
886gboolean gtk_paper_tape_expose(GtkPaperTape *papertape, GdkEventExpose *event, gpointer data) {
887        /**
888         * One of the most important methods of GtkPaperTape: the expose_event
889         * callback which makes the LOCHSTREIFEN object paint on the
890         * GtkDrawingArea. Therefore we get a new cairo context at every call.
891         **/
892        cairo_t *cr;
893        time_t TIME;
894        time(&TIME);
895        //LOCHSTREIFEN *l;
896       
897        // mal testen:
898        gdk_window_clear_area(papertape->draw->window, // nicht das parent_window sondern GdkWindow!
899                event->area.x, event->area.y, event->area.width, event->area.height);
900       
901        cr = gdk_cairo_create(papertape->draw->window);
902        //l = (LOCHSTREIFEN *)data;
903       
904        printf("%d Neuzeichnen: x|y = (%d|%d), width*height = %d * %d\n", TIME,
905                                event->area.x, event->area.y,
906                                event->area.width, event->area.height);
907        // Clipping, um das Neuzeichnen zu beschleunigen. Ist das sinnvoll?
908        /*cairo_rectangle (cr,
909                                event->area.x, event->area.y,
910                                event->area.width, event->area.height);
911        cairo_clip (cr);*/
912       
913        // gdk_cairo_rectangle() gibts uebrigens auch noch!
914       
915        lochstreifen_set_only_start_area(papertape->lochstreifen,
916                                event->area.x, event->area.y,
917                                event->area.width, event->area.height);
918       
919        lochstreifen_draw(papertape->lochstreifen, cr);
920        /*printf("Fertig (real width*height = %d * %d)\n", lochstreifen_get_width(lochstreifen),
921                lochstreifen_get_height(lochstreifen));
922        */
923        cairo_destroy(cr);
924        return FALSE;
925}
926
927void  gtk_paper_tape_redraw(GtkPaperTape *papertape, enum GTK_PAPER_TAPE_SIZE_CHANGED changed) {
928        /**
929        * Zentrale Funktion zum Aufrufen um den Lochstreifen manuell neu
930        * zu zeichnen. Wenn der Parameter TRUE ist, wird signalisiert,
931        * dass die Groesse des Lochstreifens sich (wie auch immer) geaendert
932        * hat (neue Daten, Zoom, etc. pp) -- also wird ggf. ein neues
933        * size request abgesetzt, je nach Zoomeinstellungen (Autozoom!)
934        **/
935        if(PAPER_TAPE_SIZE_CHANGED == changed) {
936                if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(papertape->menu_toggle_fit_screen))) {
937                        // Lochstreifengroesse wird automatisch dem Fenster angepasst,
938                        // dafuer werden die size requests anders gestellt, damit kein nervender
939                        // Scrollbalken fuer die angepasste Dimension kommt.
940                        // siehe transform_fit_lochstreifen
941                        if(lochstreifen_get_orientation(papertape->lochstreifen) == 1)
942                                // Mindestdimensionen: Breite festgelegt durch Daten, Hoehe variabel
943                                gtk_widget_set_size_request(papertape->draw, lochstreifen_get_width(papertape->lochstreifen), -1);
944                        else
945                                gtk_widget_set_size_request(papertape->draw, -1, lochstreifen_get_height(papertape->lochstreifen));
946                } else {
947                        // Lochstreifengroesse wird nicht automatisch dem Fenster angepasst.
948                        // Ganz normal Breite und Hoehe wuenschen.
949                        gtk_widget_set_size_request(papertape->draw, // neue Groesse erfordern.
950                                lochstreifen_get_width(papertape->lochstreifen),
951                                lochstreifen_get_height(papertape->lochstreifen));
952                }
953        } // i have resized...
954        gtk_widget_queue_draw(GTK_WIDGET(papertape->draw)); // neuzeichnen.
955        GTK_PAPER_TAPE_UPDATE_GTK; /// testweise mal SOFORT zeichnen.
956}
957
958
959/** *************************************************************************
960       FAST FUNCTIONS ("ALMOST MACROS")
961 ** *************************************************************************/
962
963GtkWidget *fast_color_menuitem(const GtkWidget *parentmenu, const char *dialog_title, const char *labeltext, GdkColor *initialColor, GtkSizeGroup *label_nice_sizegroup) {
964    /* Schnell ein Colorchooser-Button inklusive Richtextlabel in einem
965       Menuitem einbauen. Gibt das Colorchooserbutton-Widget zurück,
966       initialColor kann NULL sein. GtkSizeGroup auch. */
967    GtkWidget *menuitem, *menubox, *colorbutton, *label;
968    menuitem = gtk_menu_item_new();
969    menubox = gtk_hbox_new(FALSE, 4);
970    gtk_container_add(GTK_CONTAINER(menuitem), menubox);
971    gtk_widget_show(menubox);
972
973    colorbutton = gtk_color_button_new();
974    //gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(colorbutton), TRUE); // doch kein Alpha, das bringt nichts.
975    gtk_color_button_set_title(GTK_COLOR_BUTTON(colorbutton), dialog_title);
976    gtk_box_pack_start(GTK_BOX(menubox), colorbutton, FALSE, FALSE, 0);
977    gtk_widget_show(colorbutton);
978
979    label = gtk_label_new(NULL);
980    gtk_label_set_markup(GTK_LABEL(label), labeltext);
981    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
982    gtk_box_pack_start(GTK_BOX(menubox), label, TRUE, TRUE, 0);
983    gtk_widget_show(label);
984
985    g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(gtk_button_clicked), colorbutton);
986
987    if(initialColor != NULL) {
988        //printf("Farben:\n");
989        //printf("\tr%i g%i b%i\n", initialColor->red, initialColor->green, initialColor->blue);
990        gtk_color_button_set_color(GTK_COLOR_BUTTON(colorbutton), initialColor);
991    }
992    if(label_nice_sizegroup != NULL) {
993        gtk_size_group_add_widget(label_nice_sizegroup, label);
994    }
995
996    gtk_widget_show (menuitem);
997    gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
998    return colorbutton;
999}
1000
1001GdkColor *color_cairo2gdk(cairo_pattern_t *pattern) {
1002    /** Kleine Behelfsfunktion, um von einem Cairopattern die Farbe im GDK-format zu ziehen
1003        ALPHA wird ganz nett ignoriert :) */
1004    GdkColor *c = malloc(sizeof(GdkColor));
1005    double red, green, blue, alpha;
1006    if(pattern != NULL)
1007        cairo_pattern_get_rgba(pattern, &red, &green, &blue, &alpha);
1008    else
1009        { red=1; green=1; blue=1; } // pattern ist NULL => nehmen mir mal weiss ;-)
1010    c->red = red * 65535; // 2^16
1011    c->green = green * 65535;
1012    c->blue = blue * 65535;
1013    return c;
1014}
1015
1016GtkWidget *fast_stock_menuitem(const GtkWidget *parentmenu, const gchar *stock_id) {
1017    /* erzeugt schnell mal ein menuitem mit dem entsprechenden Stock-Dingsda
1018       und gibt es zurueck */
1019    GtkWidget *menuitem;
1020    menuitem = gtk_image_menu_item_new_from_stock(stock_id, NULL);
1021    gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
1022    gtk_widget_show (menuitem);
1023    return menuitem;
1024}
1025
1026GtkWidget *fast_menuitem(const GtkWidget *parentmenu, const gchar *label) {
1027    /* erzeugt schnell mal ein menuitem mit der entsprechenden Beschriftung und gibts
1028       zurueck */
1029    GtkWidget *menuitem;
1030    menuitem = gtk_menu_item_new_with_label(label);
1031    gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
1032    gtk_widget_show (menuitem);
1033    return menuitem;
1034}
1035
1036
1037GtkWidget *fast_menu_tearoff(const GtkWidget *parentmenu) {
1038    /* Schnell mal ein Abreissitem hinzufuegen */
1039    GtkWidget *menuitem;
1040    menuitem = gtk_tearoff_menu_item_new ();
1041    gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
1042    gtk_widget_show (menuitem);
1043    return menuitem;
1044}
1045
1046GtkWidget *fast_menu_seperator(const GtkWidget *parentmenu) {
1047    /* Schnell einen Seperator */
1048    GtkWidget *menuitem;
1049    menuitem = gtk_separator_menu_item_new();
1050    gtk_menu_shell_append(GTK_MENU_SHELL(parentmenu), menuitem);
1051    gtk_widget_show(menuitem);
1052    return menuitem;
1053}
1054
1055int fast_get_dpi() {
1056    /**
1057     * Mal schnell die Aufloesung als Ganzzahl zurueckgeben.
1058     * Der Rueckgabewert gibt also die Anzahl der Pixel an, die auf dem Monitor
1059     * einen Zoll breit sein sollten...
1060     **/
1061    GdkScreen *screen = gdk_screen_get_default();
1062    if(screen == NULL) {
1063        printf("Konnte GdkScreen zwecks DPI-Auslesung nicht erkennen!\n");
1064        return;
1065    }
1066
1067    gdouble dpi = gdk_screen_get_resolution(screen);
1068    if(dpi < 0) {
1069        printf("Screenresolution (%f) nicht feststellbar\n", dpi);
1070        return;
1071    }
1072    return (int)rint(dpi); // noch sauber runden.
1073}
1074
Note: See TracBrowser for help on using the repository browser.
© 2008 - 2013 technikum29 • Sven Köppel • Some rights reserved
Powered by Trac
Expect where otherwise noted, content on this site is licensed under a Creative Commons 3.0 License