source: projects/visualisator/gtkpapertape.c @ 9

Last change on this file since 9 was 9, checked in by sven, 11 years ago
  • Recreated Makefiles in visualisator/ and puncher/
  • All /visualisator/ sources now compile with -Wall compiler flag
  • The basic /puncher/ sources compile with -Wall, too.
  • Added Readme for schriften/

Current development is now focussed at /puncher/frontend.gtk.c.

-- Sven @ workstation

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