source: projects/visualisator/gtkprogram.c @ 1

Last change on this file since 1 was 1, checked in by sven, 16 years ago

Erstimport

File size: 45.2 KB
Line 
1/**
2 * GTK-Programm, welches mit dem per Cairo gezeichneten Lochstreifen
3 * Dateien visualisieren kann.
4 *
5 * Dabei wird die Flexibilitaet des Cairo-Zeichners voll ausgeschoepft:
6 * Beliebige Dateien koennen zum Anzeigen geoeffnet werden, der Loch-
7 * streifen kann in saemtlichen Farben frei variiert werden, die
8 * anzuzeigenden Emptybytes koennen beliebig gesetzt werden.
9 * Die Darstellung kann jederzeit in jede Richtung gedreht und gespiegelt
10 * werden, ausserdem ist stufenloser Zoom sowie eine automatische
11 * Groessenanpassung moeglich, sodass der Lochstreifen sich immer genau an die
12 * Fenstergroesse anpasst und man sinnvoll einfach ueber die Daten "scrollen"
13 * kann.
14 *
15 * Die aktuelle Ansicht des Lochstreifens kann einfach als PNG-Bild oder
16 * SVG-Vektorgrafik in eine Datei exportiert werden. Die Aenderung der Datei
17 * à la "Hexeditor" ist nicht vorgesehen, dafuer werden aber beim Ueberfahren
18 * des Lochstreifens in der Statusleiste die Werte des Bytes, ueber dem man
19 * sich befindet, aufgeschluesselt.
20 *
21 * Das Programm ist bewusst unaufdringlich gehalten; die meisten Nachrichten
22 * werden ueber die Statuszeile ausgegeben. Die Bedienung erfolgt massgeblich
23 * ueber das Menue.
24 *
25 * Je nach Kommandozeilenparameter wird von STDIN gelesen oder eine Datei
26 * geoeffnet (mehr Informationen mit Aufruf --help).
27 * Wird gar kein Parameter angegeben, dann wird eine kleine Startanimation
28 * angezeigt, die von der Perfomance und Flexibilitaet des Zeichenprogramms
29 * zeugt ;-)
30 *
31 * -- Sven, November 2007
32 *
33 **/
34#include <stdio.h>
35#include <stdlib.h>
36#include <math.h> // rint()
37#include <string.h> // strlen()
38#include <gtk/gtk.h>
39#include "lochstreifen.h"
40
41#include <time.h> // NUR WAEHREND DEBUGGING
42
43#define WINDOW_TITLE "Lochstreifen-Visualisierung" /* Standardtitel */
44
45// Muss leider global sein, damit ein Zugriff von mehreren Funktionen moeglich ist
46GtkWidget *window; // Hauptfenster
47GtkWidget *lochstreifen_widget, *lochstreifen_statusbar; // Widget, das Lochstreifen haelt, Statusleiste
48LOCHSTREIFEN *lochstreifen; // das Lochstreifen-Objekt
49GTimer *last_statusbar_update; // fuer Statusbar-Prioritaetenbehandlung
50GtkWidget *fit_screen_toggle; // Auswahlbox im Ansichtmenue, um Autozoom umzuschalten.
51gboolean startsequence_running = FALSE; // Ist TRUE, wenn die Startanimation laeuft
52
53// Die Daten, die bei der Startsequenz angezeigt werden, liegen hier als Bytearray
54// vor. Generierung mit
55// $ [generatorprogramm] | hexdump -e '11/1 "0x%02x, " "\n"'
56// "0x  , "-Zeichen wegschneiden.
57// Laenge feststellen mit wc -c
58
59int startsequence_length = 205;
60byte_t startsequence_data[] = {
610xfc, 0x01, 0x02, 0x01, 0xfc, 0x00, 0x81, 0xff, 0x81, 0x00, 0xff,
620x01, 0x01, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0x18, 0xc7, 0x00,
630xff, 0x81, 0xff, 0x00, 0xff, 0x80, 0x60, 0x80, 0xff, 0x00, 0xff,
640x80, 0x60, 0x80, 0xff, 0x00, 0xff, 0x91, 0x91, 0x00, 0xff, 0x40,
650x3c, 0x02, 0xff, 0x00, 0x00, 0x00, 0x83, 0xb9, 0xc1, 0x00, 0xff,
660x01, 0xff, 0x00, 0xff, 0x80, 0x60, 0x80, 0xff, 0x00, 0x00, 0x00,
670x81, 0xff, 0x81, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x80,
680xff, 0x80, 0x00, 0xff, 0x91, 0x91, 0x00, 0xff, 0x88, 0xf7, 0x00,
690xff, 0x88, 0x88, 0xff, 0x00, 0xff, 0x18, 0xc7, 0x00, 0x80, 0xff,
700x80, 0x00, 0x81, 0xff, 0x81, 0x00, 0xfc, 0x01, 0xfc, 0x00, 0xff,
710x91, 0x91, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x00, 0x00,
720xff, 0x01, 0x01, 0x00, 0xff, 0x81, 0xff, 0x00, 0xff, 0x81, 0x81,
730x00, 0xff, 0x10, 0xff, 0x00, 0xf0, 0x91, 0x9f, 0x00, 0x80, 0xff,
740x80, 0x00, 0xff, 0x88, 0xf7, 0x00, 0xff, 0x91, 0x91, 0x00, 0x81,
750xff, 0x81, 0x00, 0xff, 0x90, 0x90, 0x00, 0xff, 0x91, 0x91, 0x00,
760xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x83, 0xb9, 0xc1, 0x00, 0xff,
770x91, 0x91, 0x00, 0x81, 0xff, 0x81, 0x00, 0xff, 0x81, 0x81, 0x00,
780xff, 0x10, 0xff, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0xff,
790x91, 0x91, 0x00, 0xff, 0x88, 0xf7, 0x00
80};
81
82// zum Testen:
83/*int startsequence_length = 4;
84byte_t startsequence_data[] = { 0x00, 0x01, 0x02, 0x03 };*/
85
86void message_statusbar(char *msg);
87void message_error(gchar *heading, gchar *text);
88
89GtkWidget *fast_stock_menuitem(const GtkWidget *parentmenu, const gchar *stock_id) {
90    /* erzeugt schnell mal ein menuitem mit dem entsprechenden Stock-Dingsda
91       und gibt es zurueck */
92    GtkWidget *menuitem;
93    menuitem = gtk_image_menu_item_new_from_stock(stock_id, NULL);
94    gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
95    gtk_widget_show (menuitem);
96    return menuitem;
97}
98
99GtkWidget *fast_menuitem(const GtkWidget *parentmenu, const gchar *label) {
100    /* erzeugt schnell mal ein menuitem mit der entsprechenden Beschriftung und gibts
101       zurueck */
102    GtkWidget *menuitem;
103    menuitem = gtk_menu_item_new_with_label(label);
104    gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
105    gtk_widget_show (menuitem);
106    return menuitem;
107}
108
109
110GtkWidget *fast_menu_tearoff(const GtkWidget *parentmenu) {
111    /* Schnell mal ein Abreissitem hinzufuegen */
112    GtkWidget *menuitem;
113    menuitem = gtk_tearoff_menu_item_new ();
114    gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
115    gtk_widget_show (menuitem);
116    return menuitem;
117}
118
119GtkWidget *fast_menu_seperator(const GtkWidget *parentmenu) {
120    /* Schnell einen Seperator */
121    GtkWidget *menuitem;
122    menuitem = gtk_separator_menu_item_new();
123    gtk_menu_shell_append(GTK_MENU_SHELL(parentmenu), menuitem);
124    gtk_widget_show(menuitem);
125    return menuitem;
126}
127
128void redraw_lochstreifen(gboolean i_have_resized_the_lochstreifen) {
129    /**
130     * Zentrale Funktion zum Aufrufen um den Lochstreifen manuell neu
131     * zu zeichnen. Wenn der Parameter TRUE ist, wird signalisiert,
132     * dass die Groesse des Lochstreifens sich (wie auch immer) geaendert
133     * hat (neue Daten, Zoom, etc. pp) -- also wird ggf. ein neues
134     * size request abgesetzt, je nach Zoomeinstellungen (Autozoom!)
135     **/
136    if(i_have_resized_the_lochstreifen) {
137        if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(fit_screen_toggle))) {
138            // Lochstreifengroesse wird automatisch dem Fenster angepasst,
139            // dafuer werden die size requests anders gestellt, damit kein nervender
140            // Scrollbalken fuer die angepasste Dimension kommt.
141            // siehe transform_fit_lochstreifen
142           if(lochstreifen_get_orientation(lochstreifen) == 1)
143               // Mindestdimensionen: Breite festgelegt durch Daten, Hoehe variabel
144               gtk_widget_set_size_request(lochstreifen_widget, lochstreifen_get_width(lochstreifen), -1);
145           else
146               gtk_widget_set_size_request(lochstreifen_widget, -1, lochstreifen_get_height(lochstreifen));
147        } else {
148            // Lochstreifengroesse wird nicht automatisch dem Fenster angepasst.
149            // Ganz normal Breite und Hoehe wuenschen.
150            gtk_widget_set_size_request(lochstreifen_widget, // neue Groesse erfordern.
151                lochstreifen_get_width(lochstreifen),
152                lochstreifen_get_height(lochstreifen));
153        }
154    } // i have resized...
155    gtk_widget_queue_draw(lochstreifen_widget); // neuzeichnen.
156}
157
158gboolean export_lochstreifen(GtkWidget *widget, gchar *format) {
159    cairo_surface_t *surface;
160    cairo_t *cr;
161    cairo_status_t status;
162    GtkWidget *chooser;
163    char *filename;
164
165    // gewuenschten Dateinamen kriegen
166    chooser = gtk_file_chooser_dialog_new(
167        g_strdup_printf("Dateiname fuer %s-Export auswaehlen", format),
168        GTK_WINDOW(window),
169        GTK_FILE_CHOOSER_ACTION_SAVE,
170        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
171        GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
172        NULL);
173    gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser), TRUE);
174
175    if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) {
176        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
177    }
178    gtk_widget_destroy(chooser);
179
180    lochstreifen_flush_only_start_area(lochstreifen); // damit der ganze Lochstreifen gezeichnet wird,  vgl. expose_lochstreifen
181    if(strcmp(format, "PNG") == 0) {
182        // PNG erstellen
183        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
184            lochstreifen_get_width(lochstreifen),
185            lochstreifen_get_height(lochstreifen)
186        );
187        cr = cairo_create(surface);
188        lochstreifen_draw(lochstreifen, cr);
189        status = cairo_surface_write_to_png (surface, filename);
190        message_statusbar(
191            g_strdup_printf("PNG-Datei (%d x %d Pixel, 24bit) wurde exportiert: %s",
192                lochstreifen_get_width(lochstreifen),
193                lochstreifen_get_height(lochstreifen),
194                cairo_status_to_string(status))
195        );
196    } else if(strcmp(format, "SVG") == 0) {
197        // SVG erstellen
198        double width,height;
199        // SVG-Surface erwartet Breite in Punkt, 1pt = 1/72 inch
200        width = 72 * lochstreifen_get_width(lochstreifen) / (double)fast_get_dpi();
201        height = 72 * lochstreifen_get_height(lochstreifen) / (double)fast_get_dpi();
202        // irgendwie spinnt der Compiler und meint ohne den folgenden unsinnigen
203        // exipliten Typcastings "Warnung: Zuweisung erzeugt Zeiger von Ganzzahl ohne Typkonvertierung"
204        surface = (cairo_surface_t *)cairo_svg_surface_create((char*)filename, (double)width, (double)height);
205
206        cr = cairo_create(surface);
207        lochstreifen_draw(lochstreifen, cr);
208
209        message_statusbar(
210            g_strdup_printf("SVG-Datei (%.2f x %.2f Punkt) wurde exportiert: %s",
211                width, height,
212                cairo_status_to_string(cairo_surface_status(surface))
213            )
214        );
215    } else {
216        message_statusbar("Gewuenschter Exporttyp nicht feststellbar!");
217    }
218
219    cairo_surface_destroy(surface);
220    cairo_destroy(cr);
221    g_free(filename);
222}
223
224gboolean null_bytes_dialog (GtkWidget *widget, gpointer data) {
225    GtkWidget *dialog, *table, *input_start, *input_end, *label;
226    GtkWidget *box;
227
228    dialog = gtk_message_dialog_new(GTK_WINDOW(window),
229        GTK_DIALOG_DESTROY_WITH_PARENT,
230        GTK_MESSAGE_QUESTION,
231        GTK_BUTTONS_OK_CANCEL,
232        "Nullbytes");
233    gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
234        "Am Anfang und am Ende der angezeigten Datei koennen\n"
235        "zusaetzliche \"virtuelle\" Nullbytes angezeigt werden,\n"
236        "die in der Datei nicht gespeichert sind. Je nach Farbeinstellung\n"
237        "sieht man nur Fuehrungsloecher - damit sieht der\n"
238        "Lochstreifen realistischer aus.");
239
240    input_start = gtk_spin_button_new_with_range(0., 10000., 1.);
241    gtk_spin_button_set_value(GTK_SPIN_BUTTON(input_start), (gdouble)lochstreifen->empty_start);
242    input_end = gtk_spin_button_new_with_range(0., 10000., 1.);
243    gtk_spin_button_set_value(GTK_SPIN_BUTTON(input_end), (gdouble)lochstreifen->empty_end);
244
245    box = gtk_table_new(2, 2, FALSE);
246    gtk_table_set_row_spacings(GTK_TABLE(box), 5);
247    label = gtk_label_new("Nullbytes am Anfang: ");
248    gtk_table_attach_defaults(GTK_TABLE(box), label,
249       0, 1, 0, 1); 
250    gtk_table_attach_defaults(GTK_TABLE(box), input_start,
251       1, 2, 0, 1); 
252    label = gtk_label_new("Nullbytes am Ende: ");
253    gtk_table_attach_defaults(GTK_TABLE(box), label,
254       0, 1, 1, 2); 
255    gtk_table_attach_defaults(GTK_TABLE(box), input_end,
256       1, 2, 1, 2); 
257
258    gtk_container_add(GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
259                       box);
260    gtk_widget_show_all (dialog);
261
262    if(gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
263        lochstreifen->empty_start = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(input_start));
264        lochstreifen->empty_end = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(input_end));
265        message_statusbar(
266            g_strdup_printf("Vorne werden nun %d Nullbytes angezeigt, hinten %d.",
267                lochstreifen->empty_start, lochstreifen->empty_end)
268        );
269
270        redraw_lochstreifen(TRUE);
271    } else
272        message_statusbar("Anzahl der Nullbytes wurde nicht veraendert.");
273    gtk_widget_destroy (dialog);
274}
275
276gboolean expose_lochstreifen(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
277    /* Calllback für das "expose_event" - Zeichenevent */
278    cairo_t *cr;
279    time_t TIME;
280    time(&TIME);
281    //LOCHSTREIFEN *l;
282   
283    // mal testen:
284    gdk_window_clear_area(widget->window,
285        event->area.x, event->area.y, event->area.width, event->area.height);
286   
287    cr = gdk_cairo_create(widget->window);
288    //l = (LOCHSTREIFEN *)data;
289
290    printf("%d Neuzeichnen: x|y = (%d|%d), width*height = %d * %d\n", TIME,
291                        event->area.x, event->area.y,
292                        event->area.width, event->area.height);
293    // Clipping, um das Neuzeichnen zu beschleunigen. Ist das sinnvoll?
294    /*cairo_rectangle (cr,
295                        event->area.x, event->area.y,
296                        event->area.width, event->area.height);
297    cairo_clip (cr);*/
298   
299   
300    lochstreifen_set_only_start_area(lochstreifen,
301                        event->area.x, event->area.y,
302                        event->area.width, event->area.height);
303     
304    lochstreifen_draw(lochstreifen, cr);
305    /*printf("Fertig (real width*height = %d * %d)\n", lochstreifen_get_width(lochstreifen),
306        lochstreifen_get_height(lochstreifen));
307    */
308    cairo_destroy(cr);
309    return FALSE;
310}
311
312int fast_get_dpi() {
313    /**
314     * Mal schnell die Aufloesung als Ganzzahl zurueckgeben.
315     * Der Rueckgabewert gibt also die Anzahl der Pixel an, die auf dem Monitor
316     * einen Zoll breit sein sollten...
317     **/
318    GdkScreen *screen = gdk_screen_get_default();
319    if(screen == NULL) {
320        printf("Konnte GdkScreen zwecks DPI-Auslesung nicht erkennen!\n");
321        return;
322    }
323
324    gdouble dpi = gdk_screen_get_resolution(screen);
325    if(dpi < 0) {
326        printf("Screenresolution (%f) nicht feststellbar\n", dpi);
327        return;
328    }
329    return (int)rint(dpi); // noch sauber runden.
330}
331
332gboolean transform_fit_lochstreifen(GtkWidget *widget, GdkEventConfigure *event, gpointer data) {
333    // Bei Groessenveraenderungen des Lochstreifenwidgets wird dies
334    // aufgerufen, wenn "an Bildschirmgroesse anpassen" gewuenscht ist
335
336    // Das Scrollwidget davon unterrichten, nur noch in einer Richtung zu scrollen, damit
337    // es beim Verkleinern des Fensters nicht meint, genug Platz zu haben und sich die Lochstreifen-
338    // groesse gar nicht anpasst.
339    // muss bloederweise hier gemacht werden, weil man ja nachtraeglich die Ausrichtung des
340    // Lochstreifens aendern koennte.
341    int orientation = lochstreifen_get_orientation(lochstreifen); // 1 = horizontal
342
343    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtk_widget_get_parent(gtk_widget_get_parent(lochstreifen_widget))),
344        orientation == 1 ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER,
345        orientation == 1 ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC);
346
347    // Mindestgroessenberechnungen nach redraw_lochstreifen verlagert.
348    if(lochstreifen_get_orientation(lochstreifen) == 1) {
349        // horizontal ausgerichteter Lochstreifen => Hoehe fixieren.
350        lochstreifen_set_d_by_height(lochstreifen, event->height);
351        // Mindestdimensionssetzen verlagert nach redraw_lochstreifen,
352        // damit nicht Datengroessenaenderung die Sachen wieder kaputtmachen
353        // Mindestdimensionen: Breite festgelegt durch Daten, Hoehe variabel
354    } else {
355        // vertikal ausgerichtet => Breite fixieren
356        lochstreifen_set_d_by_width(lochstreifen, event->width);
357        // set size request verschoben nach redraw_lochstreifen, siehe oben
358    }
359
360    redraw_lochstreifen(TRUE);
361    return FALSE;
362}
363
364void transform_lochstreifen(GtkWidget *widget, gpointer data) {
365    char action = *((char *)data);
366    char *status_comment;
367    byte_t status_type = 0; // 0 = nix ersetzen, 1 = Ansicht in % einsetzen, 2 = Drehausrichtung einsetzen
368    if(action == '+') {
369        lochstreifen_set_d(lochstreifen, lochstreifen->d + 3); // vergroessern
370        status_comment = "Ansicht vergroessert auf %d%%";
371        status_type = 1;
372        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(fit_screen_toggle), FALSE); // nicht automatisch anpassen
373    } else if(action == '-') {
374        if(lochstreifen->d - 3 < 1) {
375            message_statusbar("Der Lochstreifen kann nicht kleiner gezeichnet werden!");
376            return;
377        }
378        lochstreifen_set_d(lochstreifen, lochstreifen->d - 3); // verkleinern
379        status_comment = "Ansicht verkleinert auf %d%%";
380        status_type = 1;
381        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(fit_screen_toggle), FALSE); // nicht automatisch anpassen
382    } else if(action == '=') { // 100% = "Lebensgroesse2
383        lochstreifen_set_d(lochstreifen, (int)rint((float)fast_get_dpi()/(float)16)); // ein Loch ist etwa 1/16 Zoll breit
384        status_comment = "Wirkliche Lebensgroesse eingestellt (kann bei falscher Monitoreinstellung abweichen)";
385        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(fit_screen_toggle), FALSE); // nicht automatisch anpassen
386    } else if(action == '[') { // an Bildschirmgroesse anpassen
387        if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
388            // Checkbox wurde aktiviert
389            g_signal_connect(G_OBJECT(lochstreifen_widget), "configure_event", G_CALLBACK(transform_fit_lochstreifen), NULL);
390            // den Container resizen damit die erste Anpassung inkraft tritt
391            gtk_widget_queue_resize_no_redraw(lochstreifen_widget); // neugezeichnet wird unten.
392            status_comment = "Lochstreifen passt sich nun stets dem Anzeigebereich an";
393        } else {
394            // Checkbox wurde deaktiviert
395            g_signal_handlers_disconnect_by_func(G_OBJECT(lochstreifen_widget), G_CALLBACK(transform_fit_lochstreifen), NULL);
396            // Scrollwidget wieder zuruecksetzen.
397            gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtk_widget_get_parent(gtk_widget_get_parent(lochstreifen_widget))),
398                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
399
400            return;
401            // nichts ausgeben, weil auch durch andere Sachen aufgerufen
402        }
403    } else if(action == 'S') { // Statusleiste einblenden/ausblenden
404        if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
405             // Checkbox wurde aktiviert
406             gtk_widget_show(lochstreifen_statusbar);
407             status_comment = "Statusleiste wird (wieder) angezeigt.";
408        } else gtk_widget_hide(lochstreifen_statusbar);
409    } else if(action == '>') { // Drehen mit Uhrzeigersinn
410        //lochstreifen_rotate(lochstreifen);
411        lochstreifen_set_direction(lochstreifen, 4, -1,-1);
412        status_comment = "Lochstreifen im Uhrzeigersinn gedreht (%s)";
413        status_type = 2;
414    } else if(action == '<') { // Drehen gegen Uhrzeigersinn
415        lochstreifen_set_direction(lochstreifen, 5, -1,-1);
416        status_comment = "Lochstreifen gegen Uhrzeigersinn gedreht (%s)";
417        status_type = 2;
418    } else if(action == '_') { // horizontal Spiegeln
419        lochstreifen_set_direction(lochstreifen, -1, 2, -1);
420        status_comment = "Lochstreifen wurde vertikal gespiegelt (Datenreihenfolge umgedreht, d.h. links und rechts vertauscht)";
421    } else if(action == '|') { // vertikal spiegeln
422        lochstreifen_set_direction(lochstreifen, -1, -1, 2);
423        status_comment = "Lochstreifen wurde horizontal gespiegelt (unten und oben vertauscht)";
424    }
425    redraw_lochstreifen(TRUE);
426
427    // Ausgaben fuer Statuszeile vorbereiten
428    if(status_comment == NULL || strlen(status_comment) == 0) return; // gibt nichts zu sagen
429    if(status_type == 1) {
430        // % von realer Groesse angeben.
431        message_statusbar(
432            g_strdup_printf(status_comment, (int)((float)lochstreifen->d / ((float)fast_get_dpi()/(float)16) * 100)) // siehe action=='='
433        );
434    } else if(status_type == 2) {
435        // Ausrichtung angeben als String
436        char *ausrichtung;
437        switch(lochstreifen->drehung) {
438            case 0: ausrichtung = "horizontal, von links nach rechts"; break;
439            case 1: ausrichtung = "vertikal, von oben nach unten"; break;
440            case 2: ausrichtung = "horizontal, von rechts nach links"; break;
441            case 3: ausrichtung = "vertikal, von unten nach oben"; break;
442            default: ausrichtung = "*** ERROR - ungueltige Ausrichtung";
443        }
444        message_statusbar(
445            g_strdup_printf(status_comment, ausrichtung)
446        );
447    } else // status_type == 0
448        message_statusbar(status_comment);
449}
450
451void change_constant_gui(GtkWidget *widget, gpointer data) {
452    // GUI zur Aenderung der Konstanten anzeigen...
453    printf("Pending...\n");
454}
455
456GdkColor *color_cairo2gdk(cairo_pattern_t *pattern);
457
458gboolean open_lochstreifen_file(char *filename) {
459    /**
460     * Oeffnet die gewuenschte Datei.
461     * Bei Fehler wird FALSE zurueckgegeben
462     **/
463     int length; gchar *data;
464     gboolean ret;
465     GError *err;
466
467     ret = g_file_get_contents(filename, &data, &length, &err);
468
469     if(ret == FALSE) {
470          message_error("Fehler beim Oeffnen",
471              g_strdup_printf("Konnte die Datei '%s' nicht oeffnen: %s", filename,
472                  err->message)
473          );
474          return;
475     }
476
477     if(startsequence_running) {
478         // Startsequenz (wenn Programm ohne Lochstreifendatei gestartet wird) laeuft noch
479         // beenden, in dem startsequence_running auf FALSE gesetzt wird.
480         startsequence_running = FALSE;
481     }
482
483     lochstreifen_set_data(lochstreifen, length, (byte_t *)data, -1, -1);
484     message_statusbar("Die Datei wurde geoeffnet.");
485     gtk_window_set_title(GTK_WINDOW (window),
486         g_strdup_printf("%s - %s", basename(filename), WINDOW_TITLE)
487    );
488
489     // Neuzeichnen, usw.
490     redraw_lochstreifen(TRUE);
491}
492
493gboolean open_lochstreifen_file_dialog(GtkWidget *widget, GtkWidget *parentWindow) {
494    /**
495     * Zeigt den Datei-Oeffnen-Dialog an (als Callback-Funktion fuer das Datei-Menue)
496     *
497     *
498     **/
499    GtkWidget *chooser;
500    chooser = gtk_file_chooser_dialog_new(
501        "(Binaer-)datei zur Darstellung als Lochstreifen auswaehlen",
502        GTK_WINDOW(parentWindow),
503        GTK_FILE_CHOOSER_ACTION_OPEN,
504        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
505        GTK_STOCK_OPEN, GTK_RESPONSE_OK,
506        NULL);
507    if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) {
508        char *filename;
509        FILE *file;
510
511        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (chooser));
512        if(filename == NULL) {
513            message_error("Keine Datei geoeffnet.", NULL);
514            gtk_widget_destroy(GTK_WIDGET(chooser));
515            return FALSE;
516        }
517
518        /*file = fopen(filename, "r");
519        if(file == NULL) {
520            message_error("Fehler beim Oeffnen",
521                g_strdup_printf("Konnte die Datei '%s' nicht oeffnen: %s", filename,
522                    g_strerror (errno))
523            );
524            return;
525        }
526        open_lochstreifen_file(file);*/
527        open_lochstreifen_file(filename);
528        g_free(filename);
529    }
530    gtk_widget_destroy(chooser);
531    return FALSE;
532}
533
534void message_error(gchar *heading, gchar *text) {
535    GtkWidget *dialog;
536    dialog = gtk_message_dialog_new(GTK_WINDOW(window),
537        GTK_DIALOG_MODAL,
538        GTK_MESSAGE_ERROR,
539        GTK_BUTTONS_OK,
540        heading);
541    if(text != NULL) gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), text);
542    gtk_dialog_run(GTK_DIALOG(dialog));
543    gtk_widget_destroy(GTK_WIDGET(dialog));
544}
545
546void colorize_lochstreifen(GtkWidget *widget, gpointer data) {
547    cairo_pattern_t **target = (cairo_pattern_t **)data; // Doppellink noetig weil Adressaenderung
548    GdkColor color;
549    gtk_color_button_get_color(GTK_COLOR_BUTTON(widget), &color);
550    /*double red, green, blue;
551    printf("Farben gewaehlt: R=%i G=%i B=%i\n",color.red, color.green, color.blue);
552        red = ((double)color.red / (double)G_MAXUINT16); // G_MAXUINT16 = 2^16 weil Farben = guint16
553        green = ((double)color.green / (double)G_MAXUINT16);
554        blue = ((double)color.blue / (double)G_MAXUINT16);
555
556    printf("Farben == R=%f G=%f B=%f\n",
557        red,green,blue);*/
558
559    //if(*target != NULL) cairo_pattern_destroy(*target); // damit nicht sinnlos Speicher zugemuellt wird
560    *target = cairo_pattern_create_rgb(
561        // darauf muss man erst mal kommen: Vorher in double casten, denn sonst
562        // berechnet er einen unsigned short (=guint16), der einfach auf 0 ab oder
563        // auf 1 hochrundet. (double)(guint16/G_MAXUNIT16) gibt also stets 0,000 oder 1,000.
564        (double)((double)color.red / (double)G_MAXUINT16), // G_MAXUINT16 = 2^16 weil Farben = guint16
565        (double)((double)color.green / (double)G_MAXUINT16),
566        (double)((double)color.blue / (double)G_MAXUINT16));
567    //printf("%s\n", cairo_status_to_string(cairo_pattern_status(*target)));
568
569    GdkColor *g = color_cairo2gdk(*target);
570    //printf("Farben zurueck: R=%d G=%d B=%d\n", g->red, g->green, g->blue);
571        //gtk_color_button_get_alpha(GTK_COLOR_BUTTON(widget)) / 65535);
572    redraw_lochstreifen(FALSE);
573    //    gtk_color_button_get_alpha(GTK_COLOR_BUTTON(widget)) / 65535);
574
575    // noch mal schnell RGB-Wert fuer Statuszeile ausgeben
576    message_statusbar(
577        g_strdup_printf("Farbe geaendert auf RGB (%d|%d|%d)",
578        (int)((double)color.red / (double)G_MAXUINT16 * 255),
579        (int)((double)color.red / (double)G_MAXUINT16 * 255),
580        (int)((double)color.red / (double)G_MAXUINT16 * 255)
581        )
582    );
583}
584
585
586GtkWidget *fast_color_menuitem(const GtkWidget *parentmenu, const char *dialog_title, const char *labeltext, GdkColor *initialColor, GtkSizeGroup *label_nice_sizegroup) {
587    /* Schnell ein Colorchooser-Button inklusive Richtextlabel in einem
588       Menuitem einbauen. Gibt das Colorchooserbutton-Widget zurück,
589       initialColor kann NULL sein. GtkSizeGroup auch. */
590    GtkWidget *menuitem, *menubox, *colorbutton, *label;
591    menuitem = gtk_menu_item_new();
592    menubox = gtk_hbox_new(FALSE, 4);
593    gtk_container_add(GTK_CONTAINER(menuitem), menubox);
594    gtk_widget_show(menubox);
595
596    colorbutton = gtk_color_button_new();
597    //gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(colorbutton), TRUE); // doch kein Alpha, das bringt nichts.
598    gtk_color_button_set_title(GTK_COLOR_BUTTON(colorbutton), dialog_title);
599    gtk_box_pack_start(GTK_BOX(menubox), colorbutton, FALSE, FALSE, 0);
600    gtk_widget_show(colorbutton);
601
602    label = gtk_label_new(NULL);
603    gtk_label_set_markup(GTK_LABEL(label), labeltext);
604    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
605    gtk_box_pack_start(GTK_BOX(menubox), label, TRUE, TRUE, 0);
606    gtk_widget_show(label);
607
608    g_signal_connect_swapped(menuitem, "activate", G_CALLBACK(gtk_button_clicked), colorbutton);
609
610    if(initialColor != NULL) {
611        //printf("Farben:\n");
612        //printf("\tr%i g%i b%i\n", initialColor->red, initialColor->green, initialColor->blue);
613        gtk_color_button_set_color(GTK_COLOR_BUTTON(colorbutton), initialColor);
614    }
615    if(label_nice_sizegroup != NULL) {
616        gtk_size_group_add_widget(label_nice_sizegroup, label);
617    }
618
619    gtk_widget_show (menuitem);
620    gtk_menu_shell_append (GTK_MENU_SHELL (parentmenu), menuitem);
621    return colorbutton;
622}
623
624GdkColor *color_cairo2gdk(cairo_pattern_t *pattern) {
625    /** Kleine Behelfsfunktion, um von einem Cairopattern die Farbe im GDK-format zu ziehen
626        ALPHA wird ganz nett ignoriert :) */
627    GdkColor *c = malloc(sizeof(GdkColor));
628    double red, green, blue, alpha;
629    if(pattern != NULL)
630        cairo_pattern_get_rgba(pattern, &red, &green, &blue, &alpha);
631    else
632        { red=1; green=1; blue=1; } // pattern ist NULL => nehmen mir mal weiss ;-)
633    c->red = red * 65535; // 2^16
634    c->green = green * 65535;
635    c->blue = blue * 65535;
636    return c;
637}
638
639gboolean startup_sequence_loop(gpointer state) { //real_data_length) {
640    // siehe startup_sequence
641    //int state = *((int *)state_pointer);
642
643    // Schleife wird deaktiviert, in dem startsequence_running auf false gesetzt wird.
644    if(!startsequence_running)
645        return FALSE;
646
647    if(*((int*)state) <= startsequence_length) {
648        lochstreifen->data++; // Zeiger zeigt aufs naechse Byte
649        *((int*)state) += 1;
650    } else {
651        lochstreifen->data -= startsequence_length;
652        *((int*)state) = 0;
653    }
654
655
656    // einfach erst mal die Lochstreifendaten um eins verschieben.
657    /*if(lochstreifen->data_length < length) {
658        lochstreifen->data_length++;
659    }*/
660    /*
661    byte_t buf;
662    int x;
663    for(x = 1; x < lochstreifen->data_length;x++) {
664        buf = lochstreifen->data[x-1];
665        lochstreifen->data[x-1] = lochstreifen->data[x];
666        lochstreifen->data[x] = buf;
667    }*/
668
669    // neuzeichnen.
670    redraw_lochstreifen(TRUE);
671    printf("Neugezeichnet.\n");
672
673    return TRUE;
674    // true zurueckgeben, wenns weitergehen soll.
675}
676
677gboolean startup_sequence(GtkWidget *widget, gpointer datas) {
678    /**
679     * Die kleine "Startup-Sequenz" wird gezeigt, wenn es keine Daten anzuzeigen gibt.
680     * Dabei scrollt ein Text (eigene Darstellung) als Lochstreifen durch die Gegend.
681     *
682     **/
683    // Integer, um aktuelle Position zu speichern
684    int *state = malloc(sizeof(int));
685    *state = 0;
686    // Array mit gerade 2x Datenarray hintereinander erstellen.
687    byte_t *bufdata = malloc(startsequence_length*2); // Buffer mit doppelter Laenge erstellen
688    memcpy(bufdata, startsequence_data, startsequence_length); // Bufarray fuellen
689    memcpy(bufdata+startsequence_length, startsequence_data, startsequence_length); // und ein zweites mal
690    /*int x;
691    byte_t *data = malloc(sizeof(byte_t)*256);
692    for(x=0;x<256;x++) {
693        data[x] = (byte_t) (255-x);
694    }
695    */
696
697    /*int length_ = 256;
698    for(x=0; x<length_; x++) {
699        printf("%i von %i: 0x%x (=%c)\n", x, length_, data[x], data[x]);
700    }*/
701
702    lochstreifen_set_data(lochstreifen, startsequence_length, bufdata, 0, 0); // schon mal den ersten Wert.
703    //*length = 256;
704    g_timeout_add(1000, startup_sequence_loop, state); //length); // zur Main Loop hinzufuegen.
705    return FALSE;
706}
707
708gboolean update_statusbar(GtkWidget *lochstreifenwidget, GdkEventMotion *event, gpointer egal) {
709    /**
710     * In der Statuszeile beim Ueberfahren des Lochstreifens die aktuellen
711     * Werte anzeigen :-)
712     *
713     **/
714    gchar *msg;
715    int x,y,byte;
716
717    // erst nach 4 Sekunden einer "wichtigen" Nachricht wieder die Statuszeile
718    // mit diesen weniger wichtigen Infos befoelkern.
719    if(last_statusbar_update != NULL && g_timer_elapsed(last_statusbar_update, NULL) < 4) return;
720
721    // Statuszeile auf jeden Fall leeren.
722    gtk_statusbar_pop(GTK_STATUSBAR(lochstreifen_statusbar), 0);
723    byte = lochstreifen_byte_by_coordinate(lochstreifen, (int)event->x, (int)event->y);
724    if(byte > 0) {
725        gchar *msg; byte_t value,bit;
726        value = lochstreifen->data[byte-1]; // weil ab 1 zaehlen => -1 weil array ab 0 zaehlt
727
728        // Bitdarstellung selbst machen (printf hat sowas wohl nicht)
729        char bitdarstellung[9]; bitdarstellung[8] = '\0'; // Nullterminierung
730        for(bit=0; bit<8; bit++) {bitdarstellung[bit] = (((value >> bit) & 0x01) == 0x01) ? '1':'0';}
731
732        msg = g_strdup_printf("Byte %03d von %03d: Wert dez=%03d bin=%s okt=%03o hex=%02X",
733                              byte, lochstreifen->data_length,
734                              value, bitdarstellung, value, value);
735        gtk_statusbar_push(GTK_STATUSBAR(lochstreifen_statusbar), 0, msg);
736        g_free(msg);
737    } else if(byte == 0) {
738        //gtk_statusbar_push(statusbar, 0, "Zeiger befindet sich außerhalb");
739        // Zeiger befindet sich ausserhalb. Nichts hier ausgeben.
740        // d.h. Statusbar wurde exiplit geleert und bleibt leer.
741    } else if(byte == -1) {
742        gtk_statusbar_push(GTK_STATUSBAR(lochstreifen_statusbar), 0,
743            "Mauszeiger befindet sich auf den zusaetzlichen Nullbytes.");
744    }
745
746    return FALSE;
747}
748
749void message_statusbar(char *msg) {
750    /**
751     * Kurze Nachrichten in der Statuszeile anzeigen.
752     * Diese Nachrichten bleiben mindestens 4 Sekunden lang sichtbar, bevor z.B.
753     * die Position des Mauscursors wieder erscheint. Dafuer dient der Timer.
754     **/
755    gtk_statusbar_pop(GTK_STATUSBAR(lochstreifen_statusbar), 0);
756    gtk_statusbar_push(GTK_STATUSBAR(lochstreifen_statusbar), 0, msg);
757    // Statusbar-Timer erst hier einrichten, damit nach Programmstart man nicht
758    // erst 4 Sekunden warten muss.
759    if(last_statusbar_update == NULL)
760        last_statusbar_update = g_timer_new(); // wird automatisch gestartet
761    else
762        g_timer_start(last_statusbar_update); // Timer zuruecksetzen
763}
764
765gboolean scroll_lochstreifen(GtkWidget *lochstreifenwidget, GdkEventScroll *event, gpointer user_data) {
766    /**
767      Beim Scrollen auf dem Widget wird das GTK_SCROLLED_WINDOW (user_data) gescrollt, je nach
768      Ausrichtung des Streifens. Nur eindimensionales Scrolling, nicht 2d. (wer hat schon eine
769      Apple Mighty Mouse ;-) )
770     **/
771    GtkAdjustment* adjustment;
772    gdouble t;
773    if(lochstreifen->drehung % 2 == 0) {
774        // Lochstreifen ist horizontal ausgreichtet
775        adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(user_data));
776        if(adjustment == NULL) { printf("hadjustment = NULL!\n"); return FALSE; }
777        t = gtk_adjustment_get_value(adjustment) + lochstreifen->d * // um eine Lochbreite scrollen
778                  ((event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? -1 : 1);
779                  // nach rechts oder links scrollen
780/*        );
781        printf("Set to %f\n", gtk_adjustment_get_value(adjustment) + (gdouble)adjustment->step_increment *
782                  (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? (gdouble)1 : (gdouble)-1);*/
783    } else {
784        // nach oben bzw. unten scrollen
785        adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(user_data));
786        if(adjustment == NULL) { printf("vadjustment = NULL!\n"); return FALSE; }
787        t = gtk_adjustment_get_value(adjustment) + lochstreifen->d * 
788            ((event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) ? -1 : 1);
789          // nach oben oder unten scrollen
790    }
791    printf("Scroll Is: %f | up: %f | down: %f | shall be: %f\n", gtk_adjustment_get_value(adjustment), adjustment->lower, adjustment->upper, t);
792
793    if(t < adjustment->lower) t = adjustment->lower;
794    if(t > adjustment->upper) t = adjustment->upper;
795    gtk_adjustment_set_value(adjustment, t);
796    // geht nicht:
797    //g_signal_emit_by_name(lochstreifen_widget, "motion-notify-event"); // Mauszeigerbewegung simulieren => Statuszeile updaten
798}
799
800int main(int argc, char *argv[]) {
801    GtkWidget *mainbox;
802    GtkWidget *menubar, *menuitem, *menu;
803    GtkSizeGroup *color_sizes; // Damit Farbmenue einheitlich aussieht
804    GtkWidget *scroll;
805    GtkWidget *statusbar;
806    lochstreifen = lochstreifen_new(); // muss hier bereit zugewiesen werden weil benutzt
807
808    gtk_init (&argc, &argv);
809
810    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
811    gtk_window_set_default_size (GTK_WINDOW (window), 600, 600);
812    gtk_window_set_title(GTK_WINDOW (window), WINDOW_TITLE);
813    g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
814
815    mainbox = gtk_vbox_new(FALSE, 0);
816    gtk_container_add(GTK_CONTAINER (window), mainbox);
817    gtk_widget_show(mainbox);
818
819    statusbar = gtk_statusbar_new(); // erst nach Inhaltswidget hinzufuegen
820    lochstreifen_statusbar = statusbar; // quick & dirty global machen...
821
822    /* Menü */
823    menubar = gtk_menu_bar_new();
824    gtk_box_pack_start (GTK_BOX (mainbox), menubar, FALSE, TRUE, 0);
825    gtk_widget_show(menubar);
826
827    // Erstes Menue: Datei
828    menu = gtk_menu_new();
829    // Oeffnen
830    g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_OPEN)),
831        "activate", G_CALLBACK(open_lochstreifen_file_dialog), window);
832    fast_menu_seperator(menu);
833    g_signal_connect(G_OBJECT(fast_menuitem(menu, "Grafik als PNG exportieren...")),
834        "activate", G_CALLBACK(export_lochstreifen), "PNG");
835    g_signal_connect(G_OBJECT(fast_menuitem(menu, "Grafik als SVG exportieren...")),
836        "activate", G_CALLBACK(export_lochstreifen), "SVG");
837    fast_menu_seperator(menu);
838    g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_QUIT)),
839        "activate", G_CALLBACK(gtk_main_quit), NULL);
840
841    menuitem = gtk_menu_item_new_with_mnemonic("_Datei");
842    gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
843    gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
844    gtk_widget_show (menuitem);
845
846    // Zweites Menue: Ansicht
847    menu = gtk_menu_new();
848    fast_menu_tearoff(menu);
849    g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_ZOOM_IN)),
850        "activate", G_CALLBACK(transform_lochstreifen), "+");
851    g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_ZOOM_OUT)),
852        "activate", G_CALLBACK(transform_lochstreifen), "-");
853    g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_ZOOM_100)),
854        "activate", G_CALLBACK(transform_lochstreifen), "=");
855    //g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_ZOOM_FIT)),
856    //    "activate", G_CALLBACK(transform_lochstreifen), "[");
857    menuitem = gtk_check_menu_item_new_with_mnemonic("Groesse dem Fenster _anpassen");
858    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), FALSE);
859    g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(transform_lochstreifen), "[");
860    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
861    gtk_widget_show (menuitem);
862    fit_screen_toggle = menuitem; // auch hier global speichern, weil es in einer Funktion gebraucht wird.
863
864    g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_PROPERTIES)), //"Groessenverhaeltnisse aendern...")),
865        "activate", G_CALLBACK(change_constant_gui), NULL);
866    fast_menu_seperator(menu);
867    g_signal_connect(G_OBJECT(fast_menuitem(menu, "Drehen im Uhrzeigersinn")),
868        "activate", G_CALLBACK(transform_lochstreifen), ">");
869    g_signal_connect(G_OBJECT(fast_menuitem(menu, "Drehen gegen den Uhrzeigersinn")),
870        "activate", G_CALLBACK(transform_lochstreifen), "<");
871    g_signal_connect(G_OBJECT(fast_menuitem(menu, "Horizontal spiegeln (Datenreihenfolge umkehren)")),
872        "activate", G_CALLBACK(transform_lochstreifen), "|");
873    g_signal_connect(G_OBJECT(fast_menuitem(menu, "Vertikal spiegeln (Bitpositionen drehen)")),
874        "activate", G_CALLBACK(transform_lochstreifen), "_");
875    fast_menu_seperator(menu);
876    g_signal_connect(G_OBJECT(fast_menuitem(menu, "Anzahl Nullbytes einstellen...")),
877        "activate", G_CALLBACK(null_bytes_dialog), NULL);
878    menuitem = gtk_check_menu_item_new_with_mnemonic("_Statusleiste anzeigen");
879    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
880    g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(transform_lochstreifen), "S");
881    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
882    gtk_widget_show (menuitem);
883
884    menuitem = gtk_menu_item_new_with_mnemonic("_Ansicht");
885    gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
886    gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
887    gtk_widget_show (menuitem);
888
889    // Drittes Menue: Farben
890    menu = gtk_menu_new();
891    color_sizes = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
892    fast_menu_tearoff(menu);
893    g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe des Lochstreifens waehlen",
894        "Farbe des <b>Lochstreifens</b>", color_cairo2gdk(lochstreifen->streifenbg), color_sizes)),
895        "color-set", G_CALLBACK(colorize_lochstreifen), &(lochstreifen->streifenbg));
896    //gtk_color_button_set_color(GTK_COLOR_BUTTON(menuitem), color_cairo2gdk(lochstreifen->streifenbg));
897
898    fast_menu_seperator(menu);
899    g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Loecher waehlen",
900        "Farbe der <b>Loecher</b> waehlen\n(binaere einsen)", color_cairo2gdk(lochstreifen->punched), color_sizes)),
901        "color-set", G_CALLBACK(colorize_lochstreifen), &(lochstreifen->punched));
902    fast_menu_seperator(menu);
903    g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Nicht-Loecher waehlen",
904         "Farbe der <b>Nullen</b> waehlen\n(nicht gelochte Bits)", color_cairo2gdk(lochstreifen->notpunched), color_sizes)),
905        "color-set", G_CALLBACK(colorize_lochstreifen), &(lochstreifen->notpunched));
906    fast_menu_seperator(menu);
907    g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Lochstreifen-Fuehrungsloecher waehlen",
908        "Farbe der <b>Fuehrungs-loecher</b>\nwaehlen (kleine Loecher)", color_cairo2gdk(lochstreifen->fuehrung), color_sizes)),
909        "color-set", G_CALLBACK(colorize_lochstreifen), &(lochstreifen->fuehrung));
910    fast_menu_seperator(menu);
911    g_signal_connect(G_OBJECT(fast_color_menuitem(menu, "Farbe der Hintergrund waehlen",
912        "<b>Hintergrundfarbe</b> waehlen\n(um dem Streifen rum)", color_cairo2gdk(lochstreifen->hintergrund), color_sizes)),
913        "color-set", G_CALLBACK(colorize_lochstreifen), &(lochstreifen->hintergrund));
914
915    menuitem = gtk_menu_item_new_with_mnemonic("_Farben");
916    gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
917    gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
918    gtk_widget_show (menuitem);
919
920    // So jetzt aber Inhalt: Das Hauptwidget.
921    lochstreifen_set_d(lochstreifen, 20);
922
923    // Daten einlesen.
924    {
925        char *filename = NULL;
926        gboolean read_from_stdin = FALSE;
927        gboolean no_splash = FALSE;
928        GError *error = NULL;
929        GOptionContext *context;
930        byte_t show_mystart = 0; // eigene Startprozedur anzeigen?
931        GOptionEntry option_entries[] = {
932           { "filename", 'f', 0, G_OPTION_ARG_FILENAME, &filename, "Datei zu oeffnen", NULL },
933           { "stdin", 's', 0, G_OPTION_ARG_NONE, &read_from_stdin, "Von Standardeingabe lesen", NULL },
934           { "no-splash", 'n', 0, G_OPTION_ARG_NONE, &no_splash,   "Keine Startanimation anzeigen, wenn ohne Datei gestartet", NULL },
935           { NULL }
936        };
937
938        context = g_option_context_new(" - Lochstreifenvisualisierung");
939        g_option_context_add_main_entries(context, option_entries, NULL);
940        g_option_context_add_group(context, gtk_get_option_group(TRUE));
941        g_option_context_parse(context, &argc, &argv, &error);
942
943        if(read_from_stdin) {
944            printf("%s: Lese Daten von Standardeingabe, erst nach EOF wird Fenster geoeffnet.\n",argv[0]);
945            byte_t *data;
946            int length = file_get_contents(stdin, &data);
947            lochstreifen_set_data(lochstreifen, length, data, 6, 6);
948        } else if(filename != NULL) {
949             // eine Datei einlesen
950             printf("Von Datei %s lesen\n", filename);
951             if(!open_lochstreifen_file(argv[1]))
952                 show_mystart = 1;
953        } else if(!no_splash) {
954            // Splash (=Start)-Sequenz anzeigen
955            show_mystart = 1;
956        }
957
958        // Daten wurde eingelesen -- oder auch nicht:
959        if(show_mystart != 0) {
960            g_signal_connect_after(G_OBJECT(window), "show", G_CALLBACK(startup_sequence), NULL);
961            startsequence_running = show_mystart;
962        } else startsequence_running = FALSE;
963    }
964
965    lochstreifen_widget = gtk_drawing_area_new();
966    gtk_widget_set_size_request (GTK_WIDGET (lochstreifen_widget),
967        lochstreifen_get_width(lochstreifen),
968        lochstreifen_get_height(lochstreifen)); // erst mal spasseshalber
969
970    scroll = gtk_scrolled_window_new(NULL, NULL);
971    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), lochstreifen_widget); // weil nicht scrollable
972    // Scrollbalken nur anzeigen wenn benoetigt
973    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
974    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_NONE);
975    gtk_box_pack_start(GTK_BOX(mainbox), scroll, TRUE, TRUE, 0);
976    gtk_widget_show(scroll);
977
978    gtk_widget_show(lochstreifen_widget);
979    g_signal_connect(G_OBJECT(lochstreifen_widget), "expose_event", G_CALLBACK(expose_lochstreifen), NULL);
980    gtk_widget_add_events(lochstreifen_widget, GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK); // Mauscursor kriegen, Mausscrolling kriegen
981    g_signal_connect(G_OBJECT(lochstreifen_widget), "motion-notify-event", G_CALLBACK(update_statusbar), NULL);
982    g_signal_connect(G_OBJECT(lochstreifen_widget), "scroll-event", G_CALLBACK(scroll_lochstreifen), scroll);
983
984    // Statusbar hinzufuegen.
985    // Ich haette ja gerne eine maechtigere Statusbar, aber das geht wohl
986    // leider nicht. Ein paar Versuche, das Label dort wenigstens Pango-Formatierungs-Faehig
987    // zu machen:
988    /*gtk_container_forall(GTK_CONTAINER(statusbar), G_CALLBACK(fast_nicer_statusbar), NULL);
989    gboolean fast_nicer_statusbar(GtkWidget *statusbar_child, gpointer egal) {
990        GList *children;
991        gpointer child;
992        children = gtk_container_get_children(GTK_CONTAINER(statusbar));
993       
994        gtk_container_forall                (GtkContainer *container,
995                                                         GtkCallback callback,
996                                                         gpointer callback_data);
997       
998        //printf("Checke Kindelement von statusbar (%d)\n", g_list_length(children));
999        //while((child = g_list_next(children)) != NULL) {
1000            if(GTK_IS_LABEL(statusbar_child)) {
1001                printf("LABEL GEFUNDEN!\n");
1002                gtk_label_set_use_markup(GTK_LABEL(statusbar_child), TRUE); // :-)
1003            }
1004            printf("Fand ein %s-Objekt.\n", G_OBJECT_TYPE_NAME(statusbar_child));
1005        //}
1006        //printf("Fertig.\n");
1007    }    */
1008    gtk_box_pack_start(GTK_BOX(mainbox), statusbar, FALSE, TRUE, 0);
1009    gtk_widget_show(statusbar);
1010
1011    // TEST DEBUG:
1012    //gtk_widget_set_redraw_on_allocate(lochstreifen_widget, TRUE);
1013    //gtk_widget_set_double_buffered(lochstreifen_widget, FALSE);
1014
1015    gtk_widget_show(window);
1016    gtk_main();
1017    return 0;
1018}
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