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 "gtkpapertape.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 | GTimer *last_statusbar_update; // fuer Statusbar-Prioritaetenbehandlung |
---|
48 | GtkWidget *fit_screen_toggle; // Auswahlbox im Ansichtmenue, um Autozoom umzuschalten. |
---|
49 | gboolean startsequence_running = FALSE; // Ist TRUE, wenn die Startanimation laeuft |
---|
50 | |
---|
51 | // Die Daten, die bei der Startsequenz angezeigt werden, liegen hier als Bytearray |
---|
52 | // vor. Generierung mit |
---|
53 | // $ [generatorprogramm] | hexdump -e '11/1 "0x%02x, " "\n"' |
---|
54 | // "0x , "-Zeichen wegschneiden. |
---|
55 | // Laenge feststellen mit wc -c |
---|
56 | |
---|
57 | int startsequence_length = 205; |
---|
58 | byte_t startsequence_data[] = { |
---|
59 | 0xfc, 0x01, 0x02, 0x01, 0xfc, 0x00, 0x81, 0xff, 0x81, 0x00, 0xff, |
---|
60 | 0x01, 0x01, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0x18, 0xc7, 0x00, |
---|
61 | 0xff, 0x81, 0xff, 0x00, 0xff, 0x80, 0x60, 0x80, 0xff, 0x00, 0xff, |
---|
62 | 0x80, 0x60, 0x80, 0xff, 0x00, 0xff, 0x91, 0x91, 0x00, 0xff, 0x40, |
---|
63 | 0x3c, 0x02, 0xff, 0x00, 0x00, 0x00, 0x83, 0xb9, 0xc1, 0x00, 0xff, |
---|
64 | 0x01, 0xff, 0x00, 0xff, 0x80, 0x60, 0x80, 0xff, 0x00, 0x00, 0x00, |
---|
65 | 0x81, 0xff, 0x81, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x80, |
---|
66 | 0xff, 0x80, 0x00, 0xff, 0x91, 0x91, 0x00, 0xff, 0x88, 0xf7, 0x00, |
---|
67 | 0xff, 0x88, 0x88, 0xff, 0x00, 0xff, 0x18, 0xc7, 0x00, 0x80, 0xff, |
---|
68 | 0x80, 0x00, 0x81, 0xff, 0x81, 0x00, 0xfc, 0x01, 0xfc, 0x00, 0xff, |
---|
69 | 0x91, 0x91, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x00, 0x00, |
---|
70 | 0xff, 0x01, 0x01, 0x00, 0xff, 0x81, 0xff, 0x00, 0xff, 0x81, 0x81, |
---|
71 | 0x00, 0xff, 0x10, 0xff, 0x00, 0xf0, 0x91, 0x9f, 0x00, 0x80, 0xff, |
---|
72 | 0x80, 0x00, 0xff, 0x88, 0xf7, 0x00, 0xff, 0x91, 0x91, 0x00, 0x81, |
---|
73 | 0xff, 0x81, 0x00, 0xff, 0x90, 0x90, 0x00, 0xff, 0x91, 0x91, 0x00, |
---|
74 | 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0x83, 0xb9, 0xc1, 0x00, 0xff, |
---|
75 | 0x91, 0x91, 0x00, 0x81, 0xff, 0x81, 0x00, 0xff, 0x81, 0x81, 0x00, |
---|
76 | 0xff, 0x10, 0xff, 0x00, 0xff, 0x40, 0x3c, 0x02, 0xff, 0x00, 0xff, |
---|
77 | 0x91, 0x91, 0x00, 0xff, 0x88, 0xf7, 0x00 |
---|
78 | }; |
---|
79 | |
---|
80 | // zum Testen: |
---|
81 | /*int startsequence_length = 4; |
---|
82 | byte_t startsequence_data[] = { 0x00, 0x01, 0x02, 0x03 };*/ |
---|
83 | |
---|
84 | void message_statusbar(char *msg); |
---|
85 | void message_error(gchar *heading, gchar *text); |
---|
86 | |
---|
87 | |
---|
88 | gboolean open_lochstreifen_file(char *filename) { |
---|
89 | /** |
---|
90 | * Oeffnet die gewuenschte Datei. |
---|
91 | * Bei Fehler wird FALSE zurueckgegeben |
---|
92 | **/ |
---|
93 | int length; gchar *data; |
---|
94 | gboolean ret; |
---|
95 | GError *err; |
---|
96 | |
---|
97 | ret = g_file_get_contents(filename, &data, &length, &err); |
---|
98 | |
---|
99 | if(ret == FALSE) { |
---|
100 | message_error("Fehler beim Oeffnen", |
---|
101 | g_strdup_printf("Konnte die Datei '%s' nicht oeffnen: %s", filename, |
---|
102 | err->message) |
---|
103 | ); |
---|
104 | return; |
---|
105 | } |
---|
106 | |
---|
107 | if(startsequence_running) { |
---|
108 | // Startsequenz (wenn Programm ohne Lochstreifendatei gestartet wird) laeuft noch |
---|
109 | // beenden, in dem startsequence_running auf FALSE gesetzt wird. |
---|
110 | startsequence_running = FALSE; |
---|
111 | } |
---|
112 | |
---|
113 | lochstreifen_set_data(lochstreifen, length, (byte_t *)data, -1, -1); |
---|
114 | message_statusbar("Die Datei wurde geoeffnet."); |
---|
115 | gtk_window_set_title(GTK_WINDOW (window), |
---|
116 | g_strdup_printf("%s - %s", basename(filename), WINDOW_TITLE) |
---|
117 | ); |
---|
118 | |
---|
119 | // Neuzeichnen, usw. |
---|
120 | redraw_lochstreifen(TRUE); |
---|
121 | } |
---|
122 | |
---|
123 | gboolean open_lochstreifen_file_dialog(GtkWidget *widget, GtkWidget *parentWindow) { |
---|
124 | /** |
---|
125 | * Zeigt den Datei-Oeffnen-Dialog an (als Callback-Funktion fuer das Datei-Menue) |
---|
126 | * |
---|
127 | * |
---|
128 | **/ |
---|
129 | GtkWidget *chooser; |
---|
130 | chooser = gtk_file_chooser_dialog_new( |
---|
131 | "(Binaer-)datei zur Darstellung als Lochstreifen auswaehlen", |
---|
132 | GTK_WINDOW(parentWindow), |
---|
133 | GTK_FILE_CHOOSER_ACTION_OPEN, |
---|
134 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
---|
135 | GTK_STOCK_OPEN, GTK_RESPONSE_OK, |
---|
136 | NULL); |
---|
137 | if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) { |
---|
138 | char *filename; |
---|
139 | FILE *file; |
---|
140 | |
---|
141 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (chooser)); |
---|
142 | if(filename == NULL) { |
---|
143 | message_error("Keine Datei geoeffnet.", NULL); |
---|
144 | gtk_widget_destroy(GTK_WIDGET(chooser)); |
---|
145 | return FALSE; |
---|
146 | } |
---|
147 | |
---|
148 | /*file = fopen(filename, "r"); |
---|
149 | if(file == NULL) { |
---|
150 | message_error("Fehler beim Oeffnen", |
---|
151 | g_strdup_printf("Konnte die Datei '%s' nicht oeffnen: %s", filename, |
---|
152 | g_strerror (errno)) |
---|
153 | ); |
---|
154 | return; |
---|
155 | } |
---|
156 | open_lochstreifen_file(file);*/ |
---|
157 | open_lochstreifen_file(filename); |
---|
158 | g_free(filename); |
---|
159 | } |
---|
160 | gtk_widget_destroy(chooser); |
---|
161 | return FALSE; |
---|
162 | } |
---|
163 | |
---|
164 | void message_error(gchar *heading, gchar *text) { |
---|
165 | GtkWidget *dialog; |
---|
166 | dialog = gtk_message_dialog_new(GTK_WINDOW(window), |
---|
167 | GTK_DIALOG_MODAL, |
---|
168 | GTK_MESSAGE_ERROR, |
---|
169 | GTK_BUTTONS_OK, |
---|
170 | heading); |
---|
171 | if(text != NULL) gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), text); |
---|
172 | gtk_dialog_run(GTK_DIALOG(dialog)); |
---|
173 | gtk_widget_destroy(GTK_WIDGET(dialog)); |
---|
174 | } |
---|
175 | |
---|
176 | |
---|
177 | gboolean startup_sequence_loop(gpointer state) { //real_data_length) { |
---|
178 | // siehe startup_sequence |
---|
179 | //int state = *((int *)state_pointer); |
---|
180 | |
---|
181 | // Schleife wird deaktiviert, in dem startsequence_running auf false gesetzt wird. |
---|
182 | if(!startsequence_running) |
---|
183 | return FALSE; |
---|
184 | |
---|
185 | if(*((int*)state) <= startsequence_length) { |
---|
186 | lochstreifen->data++; // Zeiger zeigt aufs naechse Byte |
---|
187 | *((int*)state) += 1; |
---|
188 | } else { |
---|
189 | lochstreifen->data -= startsequence_length; |
---|
190 | *((int*)state) = 0; |
---|
191 | } |
---|
192 | |
---|
193 | |
---|
194 | // einfach erst mal die Lochstreifendaten um eins verschieben. |
---|
195 | /*if(lochstreifen->data_length < length) { |
---|
196 | lochstreifen->data_length++; |
---|
197 | }*/ |
---|
198 | /* |
---|
199 | byte_t buf; |
---|
200 | int x; |
---|
201 | for(x = 1; x < lochstreifen->data_length;x++) { |
---|
202 | buf = lochstreifen->data[x-1]; |
---|
203 | lochstreifen->data[x-1] = lochstreifen->data[x]; |
---|
204 | lochstreifen->data[x] = buf; |
---|
205 | }*/ |
---|
206 | |
---|
207 | // neuzeichnen. |
---|
208 | redraw_lochstreifen(TRUE); |
---|
209 | printf("Neugezeichnet.\n"); |
---|
210 | |
---|
211 | return TRUE; |
---|
212 | // true zurueckgeben, wenns weitergehen soll. |
---|
213 | } |
---|
214 | |
---|
215 | gboolean startup_sequence(GtkWidget *widget, gpointer datas) { |
---|
216 | /** |
---|
217 | * Die kleine "Startup-Sequenz" wird gezeigt, wenn es keine Daten anzuzeigen gibt. |
---|
218 | * Dabei scrollt ein Text (eigene Darstellung) als Lochstreifen durch die Gegend. |
---|
219 | * |
---|
220 | **/ |
---|
221 | // Integer, um aktuelle Position zu speichern |
---|
222 | int *state = malloc(sizeof(int)); |
---|
223 | *state = 0; |
---|
224 | // Array mit gerade 2x Datenarray hintereinander erstellen. |
---|
225 | byte_t *bufdata = malloc(startsequence_length*2); // Buffer mit doppelter Laenge erstellen |
---|
226 | memcpy(bufdata, startsequence_data, startsequence_length); // Bufarray fuellen |
---|
227 | memcpy(bufdata+startsequence_length, startsequence_data, startsequence_length); // und ein zweites mal |
---|
228 | /*int x; |
---|
229 | byte_t *data = malloc(sizeof(byte_t)*256); |
---|
230 | for(x=0;x<256;x++) { |
---|
231 | data[x] = (byte_t) (255-x); |
---|
232 | } |
---|
233 | */ |
---|
234 | |
---|
235 | /*int length_ = 256; |
---|
236 | for(x=0; x<length_; x++) { |
---|
237 | printf("%i von %i: 0x%x (=%c)\n", x, length_, data[x], data[x]); |
---|
238 | }*/ |
---|
239 | |
---|
240 | lochstreifen_set_data(lochstreifen, startsequence_length, bufdata, 0, 0); // schon mal den ersten Wert. |
---|
241 | //*length = 256; |
---|
242 | g_timeout_add(1000, startup_sequence_loop, state); //length); // zur Main Loop hinzufuegen. |
---|
243 | return FALSE; |
---|
244 | } |
---|
245 | |
---|
246 | int main(int argc, char *argv[]) { |
---|
247 | GtkWidget *mainbox; |
---|
248 | GtkWidget *menubar, *menuitem, *menu; |
---|
249 | GtkSizeGroup *color_sizes; // Damit Farbmenue einheitlich aussieht |
---|
250 | GtkWidget *scroll; |
---|
251 | GtkWidget *statusbar; |
---|
252 | lochstreifen = lochstreifen_new(); // muss hier bereit zugewiesen werden weil benutzt |
---|
253 | |
---|
254 | gtk_init (&argc, &argv); |
---|
255 | |
---|
256 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
---|
257 | gtk_window_set_default_size (GTK_WINDOW (window), 600, 600); |
---|
258 | gtk_window_set_title(GTK_WINDOW (window), WINDOW_TITLE); |
---|
259 | g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK (gtk_main_quit), NULL); |
---|
260 | |
---|
261 | mainbox = gtk_vbox_new(FALSE, 0); |
---|
262 | gtk_container_add(GTK_CONTAINER (window), mainbox); |
---|
263 | gtk_widget_show(mainbox); |
---|
264 | |
---|
265 | statusbar = gtk_statusbar_new(); // erst nach Inhaltswidget hinzufuegen |
---|
266 | lochstreifen_statusbar = statusbar; // quick & dirty global machen... |
---|
267 | |
---|
268 | /* Menü */ |
---|
269 | menubar = gtk_menu_bar_new(); |
---|
270 | gtk_box_pack_start (GTK_BOX (mainbox), menubar, FALSE, TRUE, 0); |
---|
271 | gtk_widget_show(menubar); |
---|
272 | |
---|
273 | // Erstes Menue: Datei |
---|
274 | menu = gtk_menu_new(); |
---|
275 | // Oeffnen |
---|
276 | g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_OPEN)), |
---|
277 | "activate", G_CALLBACK(open_lochstreifen_file_dialog), window); |
---|
278 | fast_menu_seperator(menu); |
---|
279 | gtk_paper_tape_menu_export(menu); |
---|
280 | fast_menu_seperator(menu); |
---|
281 | g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_QUIT)), |
---|
282 | "activate", G_CALLBACK(gtk_main_quit), NULL); |
---|
283 | |
---|
284 | menuitem = gtk_menu_item_new_with_mnemonic("_Datei"); |
---|
285 | gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu); |
---|
286 | gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem); |
---|
287 | gtk_widget_show (menuitem); |
---|
288 | |
---|
289 | // Zweites Menue: Ansicht |
---|
290 | menu = gtk_menu_new(); |
---|
291 | gtk_paper_tape_menu_view(menu); |
---|
292 | |
---|
293 | menuitem = gtk_menu_item_new_with_mnemonic("_Ansicht"); |
---|
294 | gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu); |
---|
295 | gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem); |
---|
296 | gtk_widget_show (menuitem); |
---|
297 | |
---|
298 | // Drittes Menue: Farben |
---|
299 | menu = gtk_menu_new(); |
---|
300 | gtk_paper_tape_menu_colors(menu); |
---|
301 | |
---|
302 | menuitem = gtk_menu_item_new_with_mnemonic("_Farben"); |
---|
303 | gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu); |
---|
304 | gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem); |
---|
305 | gtk_widget_show (menuitem); |
---|
306 | |
---|
307 | |
---|
308 | |
---|
309 | // Daten einlesen. |
---|
310 | { |
---|
311 | char *filename = NULL; |
---|
312 | gboolean read_from_stdin = FALSE; |
---|
313 | gboolean no_splash = FALSE; |
---|
314 | GError *error = NULL; |
---|
315 | GOptionContext *context; |
---|
316 | byte_t show_mystart = 0; // eigene Startprozedur anzeigen? |
---|
317 | GOptionEntry option_entries[] = { |
---|
318 | { "filename", 'f', 0, G_OPTION_ARG_FILENAME, &filename, "Datei zu oeffnen", NULL }, |
---|
319 | { "stdin", 's', 0, G_OPTION_ARG_NONE, &read_from_stdin, "Von Standardeingabe lesen", NULL }, |
---|
320 | { "no-splash", 'n', 0, G_OPTION_ARG_NONE, &no_splash, "Keine Startanimation anzeigen, wenn ohne Datei gestartet", NULL }, |
---|
321 | { NULL } |
---|
322 | }; |
---|
323 | |
---|
324 | context = g_option_context_new(" - Lochstreifenvisualisierung"); |
---|
325 | g_option_context_add_main_entries(context, option_entries, NULL); |
---|
326 | g_option_context_add_group(context, gtk_get_option_group(TRUE)); |
---|
327 | g_option_context_parse(context, &argc, &argv, &error); |
---|
328 | |
---|
329 | if(read_from_stdin) { |
---|
330 | printf("%s: Lese Daten von Standardeingabe, erst nach EOF wird Fenster geoeffnet.\n",argv[0]); |
---|
331 | byte_t *data; |
---|
332 | int length = file_get_contents(stdin, &data); |
---|
333 | lochstreifen_set_data(lochstreifen, length, data, 6, 6); |
---|
334 | } else if(filename != NULL) { |
---|
335 | // eine Datei einlesen |
---|
336 | printf("Von Datei %s lesen\n", filename); |
---|
337 | if(!open_lochstreifen_file(argv[1])) |
---|
338 | show_mystart = 1; |
---|
339 | } else if(!no_splash) { |
---|
340 | // Splash (=Start)-Sequenz anzeigen |
---|
341 | show_mystart = 1; |
---|
342 | } |
---|
343 | |
---|
344 | // Daten wurde eingelesen -- oder auch nicht: |
---|
345 | if(show_mystart != 0) { |
---|
346 | g_signal_connect_after(G_OBJECT(window), "show", G_CALLBACK(startup_sequence), NULL); |
---|
347 | startsequence_running = show_mystart; |
---|
348 | } else startsequence_running = FALSE; |
---|
349 | } |
---|
350 | |
---|
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! |
---|
355 | |
---|
356 | // Statusbar hinzufuegen. |
---|
357 | // Ich haette ja gerne eine maechtigere Statusbar, aber das geht wohl |
---|
358 | // leider nicht. Ein paar Versuche, das Label dort wenigstens Pango-Formatierungs-Faehig |
---|
359 | // zu machen: |
---|
360 | /*gtk_container_forall(GTK_CONTAINER(statusbar), G_CALLBACK(fast_nicer_statusbar), NULL); |
---|
361 | gboolean fast_nicer_statusbar(GtkWidget *statusbar_child, gpointer egal) { |
---|
362 | GList *children; |
---|
363 | gpointer child; |
---|
364 | children = gtk_container_get_children(GTK_CONTAINER(statusbar)); |
---|
365 | |
---|
366 | gtk_container_forall (GtkContainer *container, |
---|
367 | GtkCallback callback, |
---|
368 | gpointer callback_data); |
---|
369 | |
---|
370 | //printf("Checke Kindelement von statusbar (%d)\n", g_list_length(children)); |
---|
371 | //while((child = g_list_next(children)) != NULL) { |
---|
372 | if(GTK_IS_LABEL(statusbar_child)) { |
---|
373 | printf("LABEL GEFUNDEN!\n"); |
---|
374 | gtk_label_set_use_markup(GTK_LABEL(statusbar_child), TRUE); // :-) |
---|
375 | } |
---|
376 | printf("Fand ein %s-Objekt.\n", G_OBJECT_TYPE_NAME(statusbar_child)); |
---|
377 | //} |
---|
378 | //printf("Fertig.\n"); |
---|
379 | } */ |
---|
380 | gtk_box_pack_start(GTK_BOX(mainbox), statusbar, FALSE, TRUE, 0); |
---|
381 | gtk_widget_show(statusbar); |
---|
382 | |
---|
383 | // TEST DEBUG: |
---|
384 | //gtk_widget_set_redraw_on_allocate(lochstreifen_widget, TRUE); |
---|
385 | //gtk_widget_set_double_buffered(lochstreifen_widget, FALSE); |
---|
386 | |
---|
387 | gtk_widget_show(window); |
---|
388 | gtk_main(); |
---|
389 | return 0; |
---|
390 | } |
---|