Changeset 3 in projects


Ignore:
Timestamp:
Jul 19, 2008, 2:16:27 PM (11 years ago)
Author:
sven
Message:

Das Lochstreifenvisualisierungsprogramm wurde komplett umgeschrieben. Die
Datei gtkprogram.c wurde aufgeteilt in ein neues Objekt, GtkPaperTape,
welches alle Zeichenoperationen mit dem Lowlevel-Objekt LOCHSTREIFEN
abgleicht sowie zahlreiche GUI-Menues und aehnliches zur Verfuegung stellt.
Letztlich sieht die Verzeichnisstruktur jetzt so aus:

  • lochstreifen.c, lochstreifen.h: Das LOCHSTREIFEN Cairo-Zeichenobjekt
  • gtkpapertape.c, gtkpapertape.h: Das GtkPaperTape GTK-Widget
  • gtk.c: Ein GTK-Programm, welches das GtkPaperTape-Widget in einem Fenster anzeigt.
  • cli.c: Ein Kommandozeilenprogramm, welches ein Kommandozeileninterface (per Aufrufparameter) fuer das LOCHSTREIFEN-Objekt bietet
Location:
visualisator
Files:
2 added
1 deleted
4 edited
1 copied
2 moved

Legend:

Unmodified
Added
Removed
  • visualisator/Makefile

    r1 r3  
    1212        gcc -o binary $(CAIROFLAGS) create-image.c $(BACKEND)
    1313       
    14 gtk: gtkprogram.c $(BACKEND)
    15         gcc -o gtkprogram $(GTKFLAGS) gtkprogram.c $(BACKEND)
     14gtk: gtk.c $(BACKEND)
     15        gcc -o gtkprogram $(GTKFLAGS) gtkpapertape.c gtk.c $(BACKEND)
    1616
    1717
  • visualisator/gtk-ohne-widget.c

    r1 r3  
    3737#include <string.h> // strlen()
    3838#include <gtk/gtk.h>
    39 #include "lochstreifen.h"
     39#include "gtkpapertape.h"
    4040
    4141#include <time.h> // NUR WAEHREND DEBUGGING
     
    4545// Muss leider global sein, damit ein Zugriff von mehreren Funktionen moeglich ist
    4646GtkWidget *window; // Hauptfenster
    47 GtkWidget *lochstreifen_widget, *lochstreifen_statusbar; // Widget, das Lochstreifen haelt, Statusleiste
    48 LOCHSTREIFEN *lochstreifen; // das Lochstreifen-Objekt
    4947GTimer *last_statusbar_update; // fuer Statusbar-Prioritaetenbehandlung
    5048GtkWidget *fit_screen_toggle; // Auswahlbox im Ansichtmenue, um Autozoom umzuschalten.
     
    8785void message_error(gchar *heading, gchar *text);
    8886
    89 GtkWidget *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 
    99 GtkWidget *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 
    110 GtkWidget *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 
    119 GtkWidget *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 
    128 void 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 
    158 gboolean 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 
    224 gboolean 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 
    276 gboolean 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 
    312 int 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 
    332 gboolean 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 
    364 void 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 
    451 void change_constant_gui(GtkWidget *widget, gpointer data) {
    452     // GUI zur Aenderung der Konstanten anzeigen...
    453     printf("Pending...\n");
    454 }
    455 
    456 GdkColor *color_cairo2gdk(cairo_pattern_t *pattern);
    45787
    45888gboolean open_lochstreifen_file(char *filename) {
     
    544174}
    545175
    546 void 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 
    586 GtkWidget *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 
    624 GdkColor *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 }
    638176
    639177gboolean startup_sequence_loop(gpointer state) { //real_data_length) {
     
    706244}
    707245
    708 gboolean 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 
    749 void 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 
    765 gboolean 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 
    800246int main(int argc, char *argv[]) {
    801247    GtkWidget *mainbox;
     
    831277        "activate", G_CALLBACK(open_lochstreifen_file_dialog), window);
    832278    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");
     279    gtk_paper_tape_menu_export(menu);
    837280    fast_menu_seperator(menu);
    838281    g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_QUIT)),
     
    846289    // Zweites Menue: Ansicht
    847290    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);
     291    gtk_paper_tape_menu_view(menu);
    883292
    884293    menuitem = gtk_menu_item_new_with_mnemonic("_Ansicht");
     
    889298    // Drittes Menue: Farben
    890299    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));
     300    gtk_paper_tape_menu_colors(menu);
    914301
    915302    menuitem = gtk_menu_item_new_with_mnemonic("_Farben");
     
    918305    gtk_widget_show (menuitem);
    919306
    920     // So jetzt aber Inhalt: Das Hauptwidget.
    921     lochstreifen_set_d(lochstreifen, 20);
     307
    922308
    923309    // Daten einlesen.
     
    963349    }
    964350
    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);
     351    papertape = gtk_paper_tape_new();
     352    gtk_box_pack_start(GTK_BOX(mainbox), papertape->scroll, TRUE, TRUE, 0);
     353
     354    /// signal für Statusbar verbinden zu lokaler Funktion!
    983355
    984356    // Statusbar hinzufuegen.
  • visualisator/gtk.c

    r1 r3  
    11/**
    2  * GTK-Programm, welches mit dem per Cairo gezeichneten Lochstreifen
    3  * Dateien visualisieren kann.
     2 * A simple example GTK program which uses all features of the GtkPaperTape
     3 * widget in one window. No extra features, except of:
    44 *
    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.
     5 *   * handling of command line options by using the glib parser, thus
     6 *   * being able to read from stdin or any files
     7 *   * menu item to open files in the GUI
    148 *
    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.
    209 *
    21  * Das Programm ist bewusst unaufdringlich gehalten; die meisten Nachrichten
    22  * werden ueber die Statuszeile ausgegeben. Die Bedienung erfolgt massgeblich
    23  * ueber das Menue.
     10 * rewritten on 21.06.2008
    2411 *
    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
    3212 *
    3313 **/
    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
    46 GtkWidget *window; // Hauptfenster
    47 GtkWidget *lochstreifen_widget, *lochstreifen_statusbar; // Widget, das Lochstreifen haelt, Statusleiste
    48 LOCHSTREIFEN *lochstreifen; // das Lochstreifen-Objekt
    49 GTimer *last_statusbar_update; // fuer Statusbar-Prioritaetenbehandlung
    50 GtkWidget *fit_screen_toggle; // Auswahlbox im Ansichtmenue, um Autozoom umzuschalten.
    51 gboolean 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 
    59 int startsequence_length = 205;
    60 byte_t startsequence_data[] = {
    61 0xfc, 0x01, 0x02, 0x01, 0xfc, 0x00, 0x81, 0xff, 0x81, 0x00, 0xff,
    62 0x01, 0x01, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0x18, 0xc7, 0x00,
    63 0xff, 0x81, 0xff, 0x00, 0xff, 0x80, 0x60, 0x80, 0xff, 0x00, 0xff,
    64 0x80, 0x60, 0x80, 0xff, 0x00, 0xff, 0x91, 0x91, 0x00, 0xff, 0x40,
    65 0x3c, 0x02, 0xff, 0x00, 0x00, 0x00, 0x83, 0xb9, 0xc1, 0x00, 0xff,
    66 0x01, 0xff, 0x00, 0xff, 0x80, 0x60, 0x80, 0xff, 0x00, 0x00, 0x00,
    67 0x81, 0xff, 0x81, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x80,
    68 0xff, 0x80, 0x00, 0xff, 0x91, 0x91, 0x00, 0xff, 0x88, 0xf7, 0x00,
    69 0xff, 0x88, 0x88, 0xff, 0x00, 0xff, 0x18, 0xc7, 0x00, 0x80, 0xff,
    70 0x80, 0x00, 0x81, 0xff, 0x81, 0x00, 0xfc, 0x01, 0xfc, 0x00, 0xff,
    71 0x91, 0x91, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x00, 0x00,
    72 0xff, 0x01, 0x01, 0x00, 0xff, 0x81, 0xff, 0x00, 0xff, 0x81, 0x81,
    73 0x00, 0xff, 0x10, 0xff, 0x00, 0xf0, 0x91, 0x9f, 0x00, 0x80, 0xff,
    74 0x80, 0x00, 0xff, 0x88, 0xf7, 0x00, 0xff, 0x91, 0x91, 0x00, 0x81,
    75 0xff, 0x81, 0x00, 0xff, 0x90, 0x90, 0x00, 0xff, 0x91, 0x91, 0x00,
    76 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x83, 0xb9, 0xc1, 0x00, 0xff,
    77 0x91, 0x91, 0x00, 0x81, 0xff, 0x81, 0x00, 0xff, 0x81, 0x81, 0x00,
    78 0xff, 0x10, 0xff, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0xff,
    79 0x91, 0x91, 0x00, 0xff, 0x88, 0xf7, 0x00
    80 };
    81 
    82 // zum Testen:
    83 /*int startsequence_length = 4;
    84 byte_t startsequence_data[] = { 0x00, 0x01, 0x02, 0x03 };*/
    85 
    86 void message_statusbar(char *msg);
    87 void message_error(gchar *heading, gchar *text);
    88 
    89 GtkWidget *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 
    99 GtkWidget *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 }
    10814
    10915
    110 GtkWidget *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 }
     16#include <gtk/gtk.h>
     17#include "gtkpapertape.h"
    11818
    119 GtkWidget *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 }
     19#define _(a) (a)
    12720
    128 void 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 }
     21gboolean open_file_dialog(GtkWidget *menuitem, GtkPaperTape* papertape) {
     22        /**
     23         * displays an "open file" dialog which runs gtk_paper_tape_read_from_file() if success,
     24         * is run by the menu item "open file"
     25         **/
    15726
    158 gboolean 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;
     27        GtkWidget *chooser;
     28        chooser = gtk_file_chooser_dialog_new(
     29                _("Select (binary) file to display as a paper tape"),
     30                GTK_WINDOW(papertape->parent_window),
     31                GTK_FILE_CHOOSER_ACTION_OPEN,
     32                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     33                GTK_STOCK_OPEN, GTK_RESPONSE_OK,
     34                NULL);
    16435
    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);
     36        if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) {
     37                char *filename;
     38                FILE *file;
    17439
    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);
     40                filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (chooser));
     41                if(filename == NULL) {
     42                        g_printf(_("No file selected to open.\n"));
     43                        gtk_widget_destroy(GTK_WIDGET(chooser));
     44                        return FALSE;
     45                } else {
     46                        gtk_paper_tape_read_from_file(papertape, filename);
     47                        g_free(filename);
     48                        gtk_widget_destroy(GTK_WIDGET(chooser));
     49                        return TRUE;
     50                }
     51        }
     52        gtk_widget_destroy(chooser);
     53        return FALSE;
     54} // open_file_dialog
    17955
    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);
     56int main(int argc, char *argv[]) {
     57        GtkWidget *window, *mainbox, *statusbar;
     58        GtkPaperTape *papertape;
     59        gtk_init(&argc, &argv);
     60       
     61        // main window generation
     62        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
     63        gtk_window_set_default_size(GTK_WINDOW(window), 600, 600);
     64        gtk_window_set_title(GTK_WINDOW(window), "GtkPaperTape Demo");
     65        g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
     66       
     67        // generate main box (menubar + papertape whole widget)
     68        mainbox = gtk_vbox_new(FALSE, 0);
     69        gtk_container_add(GTK_CONTAINER(window), mainbox);
     70        gtk_widget_show(mainbox);
     71       
     72        // main widget generation
     73        papertape = GTK_PAPER_TAPE(gtk_paper_tape_new(window));
     74       
     75        // menus start
     76        {
     77                GtkWidget *menu, *menubar, *menuitem;
     78                // menus
     79                menubar = gtk_menu_bar_new();
     80                gtk_box_pack_start(GTK_BOX(mainbox), menubar, FALSE, TRUE, 0);
     81                gtk_widget_show(menubar);
     82       
     83                // FIRST menu: File {{{
     84                menu = gtk_menu_new();
     85                // Oeffnen
     86                g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_OPEN)),
     87                        "activate", G_CALLBACK(open_file_dialog), papertape);
     88                fast_menu_seperator(menu);
     89                gtk_paper_tape_menu_export(papertape, menu);
     90                fast_menu_seperator(menu);
     91                g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_QUIT)),
     92                        "activate", G_CALLBACK(gtk_main_quit), NULL);
     93       
     94                menuitem = gtk_menu_item_new_with_mnemonic("_Datei");
     95                gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
     96                gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
     97                gtk_widget_show (menuitem);
     98       
     99                // Zweites Menue: Ansicht
     100                menu = gtk_menu_new();
     101                gtk_paper_tape_menu_view(papertape, menu);
     102       
     103                menuitem = gtk_menu_item_new_with_mnemonic("_Ansicht");
     104                gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
     105                gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
     106                gtk_widget_show (menuitem);
     107       
     108                // Drittes Menue: Farben
     109                menu = gtk_menu_new();
     110                gtk_paper_tape_menu_colors(papertape, menu);
     111       
     112                menuitem = gtk_menu_item_new_with_mnemonic("_Farben");
     113                gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
     114                gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
     115                gtk_widget_show (menuitem);
     116        }
     117        // menus end
    205118
    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 
    224 gboolean 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 
    276 gboolean 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 
    312 int 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 
    332 gboolean 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 
    364 void 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 
    451 void change_constant_gui(GtkWidget *widget, gpointer data) {
    452     // GUI zur Aenderung der Konstanten anzeigen...
    453     printf("Pending...\n");
    454 }
    455 
    456 GdkColor *color_cairo2gdk(cairo_pattern_t *pattern);
    457 
    458 gboolean 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 
    493 gboolean 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 
    534 void 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 
    546 void 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 }
     119        // attach papertape whole widget to mainbox
     120        gtk_box_pack_start(GTK_BOX(mainbox),
     121                GTK_WIDGET(gtk_paper_tape_get_whole_box(GTK_PAPER_TAPE(papertape))),
     122                TRUE, TRUE, 0);
    584123
    585124
    586 GtkWidget *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);
     125        // parse command options
     126        {
     127                char *filename = NULL;
     128                gboolean read_from_stdin = FALSE;
     129                GError *error = NULL;
     130                GOptionContext *context;
     131                byte_t show_mystart = 0; // eigene Startprozedur anzeigen?
     132                GOptionEntry option_entries[] = {
     133                        { "filename", 'f', 0, G_OPTION_ARG_FILENAME, &filename, _("file to open"), NULL },
     134                        { "stdin", 's', 0, G_OPTION_ARG_NONE, &read_from_stdin, _("read from standard input"), NULL },
     135                        { NULL }
     136                };
    595137
    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);
     138                context = g_option_context_new(_(" - GtkPaperTape visualisation"));
     139                g_option_context_add_main_entries(context, option_entries, NULL);
     140                g_option_context_add_group(context, gtk_get_option_group(TRUE));
     141                g_option_context_parse(context, &argc, &argv, &error);
    601142
    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);
     143                if(read_from_stdin) {
     144                        printf(_("%s: Reading data from standard input, will open window after EOF.\n"), argv[0]);
     145                        byte_t *data;
     146                        int length = file_get_contents(stdin, &data);
     147                        gtk_paper_tape_set_data(papertape, length, data);
     148                } else if(filename != NULL) {
     149                        // eine Datei einlesen
     150                        printf(_("Reading from file '%s'\n"), filename);
     151                        if(!gtk_paper_tape_read_from_file(papertape, argv[1])) {
     152                                printf(_("Failed to read from file!\n"));
     153                                return 1;
     154                        }
     155                }
     156        }
     157        // end of parsing command options
    607158
    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;
     159        gtk_widget_show(window);
     160        gtk_main();
     161        return 0;
    622162}
    623 
    624 GdkColor *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 
    639 gboolean 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 
    677 gboolean 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 
    708 gboolean 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 
    749 void 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 
    765 gboolean 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 
    800 int 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 }
  • visualisator/lochstreifen.c

    r1 r3  
    157157    l->d = 10;
    158158    l->data_length = 0;
     159    l->data = NULL;
     160    l->empty_start = 0;
     161    l->empty_end = 0;
    159162    l->debug = 0;
    160163    l->hintergrund = NULL; // nicht zeichnen
     
    168171    l->spiegelung_hor = 0;
    169172    l->spiegelung_ver = 0;
     173    l->highlight_byte = 0;
     174    l->highlight_color = NULL;
    170175    lochstreifen_flush_only_start_area(l);
    171176    return l;
     
    181186    if(empty_start >= 0) l->empty_start = empty_start;
    182187    if(empty_end >= 0) l->empty_end = empty_end;
     188}
     189
     190void lochstreifen_set_highlight(LOCHSTREIFEN* l, int byte_number) {
     191    l->highlight_byte = byte_number;
     192    if(l->highlight_color == NULL)
     193        l->highlight_color = cairo_pattern_create_rgb(242/255, 255/255, 0);
    183194}
    184195
     
    357368    else if(byte > l->data_length) return -1; // im Emptyendbereich
    358369    else return byte; // im gueltigen Bereich -- Byte gefunden!
    359 
     370}
     371
     372int lochstreifen_coordinate_by_byte(LOCHSTREIFEN *l, int byte) {
     373    /**
     374     * Anhang eines Bytes (fuers Datenarray, ab 0 zaehlend) wird -- abhaengig nach aktueller
     375     * Ausrichtung des Lochstreifens -- die Koordinate des Bytes in der Dimension der
     376     * Ausrichtung des Lochstreifens zurueckgegeben..
     377     * Liegt der Lochstreifen horizontal, so wird die x-Koordinate zurueckgegeben. Liegt er
     378     * vertikal, dann die y-Koordinate.
     379     *
     380     **/
     381     int xy;
     382     //if(lochstreifen_get_orientation(l)) { // horizontal = 1 | vertical = 0
     383        xy = l->margin + l->abriss + (l->d + l->bspace) * (byte + l->empty_start);
     384     //}
     385
     386     return xy;
     387     // TODO! Richtige implementierung!
    360388}
    361389
     
    375403    x1 = l->only_start_x; y1 = l->only_start_y;
    376404    x2 = x1 + width; y2 = y1 + height;
    377    
     405
    378406    /*int x1,y1,x2,y2,x3,y3,x4,y4;
    379407    x1 = l->only_start_x; y1 = l->only_start_y;
     
    412440         cairo_matrix_multiply(&matrix, &matrix, &temp);
    413441    }
    414    
     442
    415443    //if(l->drehung > 0) {
    416444        /*cairo_matrix_rotate(&matrix, l->drehung/2 *M_PI);
     
    428456    cairo_set_matrix(cr, &matrix);
    429457    // -1, 0, 0, 1, width, 0 = spiegelung x-achse
    430    
     458
    431459    //cairo_device_to_user(cr, (double*)&(l->only_start_x), (double*)&(l->only_start_y));
    432460    cairo_device_to_user(cr, (double*)&(x1), (double*)&(y1));
     
    508536        if(l->only_width != 0 && x > l->only_start_x + l->only_width) break;
    509537
     538        // ggf. Byte hervorheben
     539        if(byte == l->highlight_byte && l->highlight_color != NULL) {
     540            cairo_set_source(cr, l->highlight_color);
     541            cairo_rectangle(cr, x - l->bspace/2, l->margin, l->df*2 + l->bspace, height - 2*l->margin);
     542            cairo_fill(cr);
     543        }
     544
    510545        // Bits des entsprechenden Bytes durchgehen
    511546        for(loch=0, y = l->margin + l->padding; loch < 8; y += l->d + l->hspace, loch++) {
  • visualisator/lochstreifen.h

    r1 r3  
    5151    int only_height;  // gezeichnet.
    5252
     53
     54    /* Feature: Ein bestimmtes Byte hervorheben */
     55    int highlight_byte; // Nummer des Bytes (von 0)
     56    cairo_pattern_t *highlight_color; // Farben => wenn NULL, kein Highlight
     57
    5358    /* Debugausgaben machen => wenn debug != 0 ist! */
    5459    byte_t debug;
Note: See TracChangeset for help on using the changeset viewer.
© 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