1 | /** |
---|
2 | * GTK-Programm, welches mit dem per Cairo gezeichneten Lochstreifen |
---|
3 | * Dateien visualisieren kann. |
---|
4 | * |
---|
5 | * Dabei wird die Flexibilitaet des Cairo-Zeichners voll ausgeschoepft: |
---|
6 | * Beliebige Dateien koennen zum Anzeigen geoeffnet werden, der Loch- |
---|
7 | * streifen kann in saemtlichen Farben frei variiert werden, die |
---|
8 | * anzuzeigenden Emptybytes koennen beliebig gesetzt werden. |
---|
9 | * Die Darstellung kann jederzeit in jede Richtung gedreht und gespiegelt |
---|
10 | * werden, ausserdem ist stufenloser Zoom sowie eine automatische |
---|
11 | * Groessenanpassung moeglich, sodass der Lochstreifen sich immer genau an die |
---|
12 | * Fenstergroesse anpasst und man sinnvoll einfach ueber die Daten "scrollen" |
---|
13 | * kann. |
---|
14 | * |
---|
15 | * Die aktuelle Ansicht des Lochstreifens kann einfach als PNG-Bild oder |
---|
16 | * SVG-Vektorgrafik in eine Datei exportiert werden. Die Aenderung der Datei |
---|
17 | * à la "Hexeditor" ist nicht vorgesehen, dafuer werden aber beim Ueberfahren |
---|
18 | * des Lochstreifens in der Statusleiste die Werte des Bytes, ueber dem man |
---|
19 | * sich befindet, aufgeschluesselt. |
---|
20 | * |
---|
21 | * Das Programm ist bewusst unaufdringlich gehalten; die meisten Nachrichten |
---|
22 | * werden ueber die Statuszeile ausgegeben. Die Bedienung erfolgt massgeblich |
---|
23 | * ueber das Menue. |
---|
24 | * |
---|
25 | * Je nach Kommandozeilenparameter wird von STDIN gelesen oder eine Datei |
---|
26 | * geoeffnet (mehr Informationen mit Aufruf --help). |
---|
27 | * Wird gar kein Parameter angegeben, dann wird eine kleine Startanimation |
---|
28 | * angezeigt, die von der Perfomance und Flexibilitaet des Zeichenprogramms |
---|
29 | * zeugt ;-) |
---|
30 | * |
---|
31 | * -- Sven, November 2007 |
---|
32 | * |
---|
33 | **/ |
---|
34 | #include <stdio.h> |
---|
35 | #include <stdlib.h> |
---|
36 | #include <math.h> // rint() |
---|
37 | #include <string.h> // strlen() |
---|
38 | #include <gtk/gtk.h> |
---|
39 | #include "lochstreifen.h" |
---|
40 | |
---|
41 | #include <time.h> // NUR WAEHREND DEBUGGING |
---|
42 | |
---|
43 | #define WINDOW_TITLE "Lochstreifen-Visualisierung" /* Standardtitel */ |
---|
44 | |
---|
45 | // Muss leider global sein, damit ein Zugriff von mehreren Funktionen moeglich ist |
---|
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 | } |
---|
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); |
---|
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 | } |
---|
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 | } |
---|
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 | } |
---|