source: projects/puncher/frontend.gtk.c @ 8

Last change on this file since 8 was 8, checked in by sven, 11 years ago

Puncher subsystem development. The Linux and dummy backends should work right
now, the cli frontend should work, too. GTK frontend still in development.
Windows backend still... in preparation ;-)

-- Sven @ workstation

File size: 13.1 KB
Line 
1/**
2 * The GTK frontend to the puncher backends.
3 *
4 * It uses GtkPaperTape to display the current open file and
5 * serves several controls to control the punch process.
6 *
7 * started at 14.06.2008
8 * (c) Sven
9 *
10 *
11 *
12 **/
13
14
15#include <gtk/gtk.h>
16#include <math.h>
17#include <stdlib.h>
18#include "backend.h"
19#include "../visualisator/gtkpapertape.h"
20
21#define _(a) a
22
23/* while punching in the gtk main loop, update the gui! */
24#define UPDATE_GTK_GUI  { while (gtk_events_pending ()) gtk_main_iteration (); }
25
26/* The master structure... */
27struct puncher_frontend {
28        /* The state of our punching process */
29        enum PUNCHER_FRONTEND_PROGRESS_STATE {
30                PROGRESS_RUNNING,
31                PROGRESS_PAUSED,
32                PROGRESS_STOPPED
33        } state;
34       
35        /* Various GtkWidgets: */
36        GtkWidget *progress_bar;                // The GtkProgressBar
37       
38        // Label Grid:
39        GtkWidget *progress_holes_total;        // counts ALL available bytes
40        GtkWidget *progress_holes_punched;      // counts the already punched bytes
41        GtkWidget *progress_holes_todo;         // counts the bytes which must be punched
42       
43        GtkWidget *progress_total_time;         // displays the calculated total punch time
44        GtkWidget *progress_estimated_time;     // displays the estimated time until finish
45        GtkWidget *progress_elapsed_time;       // displays the elapsed time since beginning
46       
47        GtkWidget *progress_time_per_hole;      // displays the time needed for one hole
48        GtkWidget *progress_frequency;          // displays the amount of holes per second
49
50        // Master buttons
51        GtkWidget *button_start;                // The button to start punching
52        GtkWidget *button_pause;                // The button to pause punching
53        GtkWidget *button_stop;                 // The button to stop punching
54
55        // The papertape context
56        GtkPaperTape *papertape;
57
58        // This is supposed to be a link to the data
59        // saved somewhere in GtkPaperTape/LOCHSTREIFEN*.
60        // Don't even think about to free() it. The memory
61        // leak doesn't hurt ;-)
62        int length;
63        byte_t *data;
64
65        int punched;  // the already punched holes
66        GTimer *timer; // the timer which measures
67} d;
68
69gboolean progress_update_time();
70
71void puncher_frontend_prepare(GtkWidget *window) {
72        /**
73         * This function will prepare the big struct puncher_frontend which is
74         * globally known as "d"
75         *
76         **/
77
78        d.state = PROGRESS_STOPPED;
79        d.length = 0;
80        d.data = 0;
81        d.punched = 0;
82       
83        d.papertape = GTK_PAPER_TAPE(gtk_paper_tape_new(window));
84       
85        d.progress_bar = gtk_progress_bar_new();
86       
87        #define PUNCHER_FRONTEND_FAST_LABEL(a) { d.a = gtk_label_new("x"); \
88                gtk_label_set_selectable(GTK_LABEL(d.a), TRUE); }
89        PUNCHER_FRONTEND_FAST_LABEL(progress_holes_total);
90        PUNCHER_FRONTEND_FAST_LABEL(progress_holes_punched);
91        PUNCHER_FRONTEND_FAST_LABEL(progress_holes_todo);
92        PUNCHER_FRONTEND_FAST_LABEL(progress_total_time);
93        PUNCHER_FRONTEND_FAST_LABEL(progress_estimated_time);
94        PUNCHER_FRONTEND_FAST_LABEL(progress_elapsed_time);
95        PUNCHER_FRONTEND_FAST_LABEL(progress_time_per_hole);
96        PUNCHER_FRONTEND_FAST_LABEL(progress_frequency);
97
98        d.button_start = gtk_button_new_with_mnemonic(_("Punch!")); 
99        gtk_button_set_image(GTK_BUTTON(d.button_start),
100                gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON));
101
102        d.button_pause = gtk_button_new_with_mnemonic(_("Pause")); 
103        gtk_button_set_image(GTK_BUTTON(d.button_pause),
104                gtk_image_new_from_stock(GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_BUTTON));
105
106        d.button_stop = gtk_button_new_with_mnemonic(_("Stop")); 
107        gtk_button_set_image(GTK_BUTTON(d.button_stop),
108                gtk_image_new_from_stock(GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_BUTTON));
109
110        d.timer = g_timer_new();
111}
112
113void puncher_frontend_progress_set() {
114        /**
115         * set information about holes
116         *
117         **/
118        gtk_label_set_markup(GTK_LABEL(d.progress_holes_total),
119                g_markup_printf_escaped("Loecher: <b>%3d</b>", d.length)
120        );
121        gtk_label_set_markup(GTK_LABEL(d.progress_holes_punched),
122                g_markup_printf_escaped("Gestanzt: <b>%3d</b>", d.punched)
123        );
124        gtk_label_set_markup(GTK_LABEL(d.progress_holes_todo),
125                g_markup_printf_escaped("Zu Stanzen: <b>%4d</b>", d.length - d.punched)
126        );
127}
128
129gboolean puncher_frontend_puncher_start(GtkWidget *start_widget, gpointer nada) {
130        /**
131         * The callback for the "start punching" button. This will prepare
132         * the gui, prepare observing and progress functions and of course
133         * this will start the low level punching driver.
134         **/
135       
136        // set the values
137        d.state = PROGRESS_RUNNING;
138       
139        // prepare the gui
140        gtk_widget_set_sensitive(d.button_start, FALSE);
141        gtk_widget_set_sensitive(d.button_pause, TRUE);
142        gtk_widget_set_sensitive(d.button_stop, TRUE);
143        gtk_widget_set_sensitive(d.progress_bar, TRUE);
144       
145        // start the progress indicators:
146        //g_timer_start(d.timer);
147        //g_timeout_add(1000, progress_update_time, NULL);
148        // kein Sinn - regelmaessig nach jedem punch machen!
149       
150        // start the punching loop
151        puncher_frontend_punch_loop();
152}
153
154gboolean puncher_frontend_punch_loop() {
155        /**
156         * The Punch main loop, as the gui drives it.
157         * Returns success or not (FALSE).
158         **/
159       
160        int x;
161       
162        for(x = 0; x < d.length; x++) {
163                puncher_frontend_progress_set();
164                puncher_frontend_progress_update_time();
165                gtk_paper_tape_set_highlight(d.papertape, x, TRUE); // TRUE: Abhängig von Checkbox!
166                UPDATE_GTK_GUI;
167                punch_byte(d.data[x]);
168                d.punched++;
169        }
170       
171        gtk_paper_tape_remove_highlight(d.papertape);
172        return TRUE;
173}
174
175gboolean puncher_frontend_progress_update_time() {
176        /**
177         * This does exactly what the low level function "calculate_time" does;
178         * using the power of glib (GTimer). Furthermore additional values are
179         * calculated.
180         *
181         * expects the GTimer to be started.
182         **/
183
184        int percentage = (int)rint(((float)d.punched / (float)d.length) * 100);
185        int sec_since_beginning = (int)rint(g_timer_elapsed(d.timer, NULL));
186        int sec_total;
187        int est = 0;
188       
189        gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(d.progress_bar),
190                (double)d.punched / (double)d.length);
191        gtk_progress_bar_set_text(GTK_PROGRESS_BAR(d.progress_bar),
192                g_strdup_printf("%d%%", percentage)
193        );
194       
195        // calc after first cycle
196        if(sec_since_beginning != 0) {
197                est = (int)rint((float)sec_since_beginning / ((float)d.punched / (float)d.length));
198               
199                gtk_label_set_markup(GTK_LABEL(d.progress_estimated_time),
200                        g_markup_printf_escaped("<b>%d</b> sec", est)
201                );
202        }
203       
204        gtk_label_set_markup(GTK_LABEL(d.progress_elapsed_time),
205                g_markup_printf_escaped("%d", sec_since_beginning)
206        );
207       
208        return !(est == sec_since_beginning && percentage == 100);
209        // true zurueckgeben, wenns weitergehen soll.
210}
211
212
213gboolean puncher_frontend_open_file_dialog(GtkWidget *menuitem, GtkPaperTape* papertape) {
214        /**
215         * displays an "open file" dialog which runs gtk_paper_tape_read_from_file() if success,
216         * is run by the menu item "open file"
217         **/
218
219        GtkWidget *chooser;
220        chooser = gtk_file_chooser_dialog_new(
221                _("Select (binary) file to display as a paper tape"),
222                GTK_WINDOW(papertape->parent_window),
223                GTK_FILE_CHOOSER_ACTION_OPEN,
224                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
225                GTK_STOCK_OPEN, GTK_RESPONSE_OK,
226                NULL);
227
228        if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) {
229                char *filename;
230                FILE *file;
231
232                filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (chooser));
233                if(filename == NULL) {
234                        g_printf(_("No file selected to open.\n"));
235                        gtk_widget_destroy(GTK_WIDGET(chooser));
236                        return FALSE;
237                } else {
238                        gtk_paper_tape_read_from_file(papertape, filename);
239                       
240                        d.length = papertape->lochstreifen->data_length;
241                        d.data = papertape->lochstreifen->data;
242                       
243                        g_free(filename);
244                        gtk_widget_destroy(GTK_WIDGET(chooser));
245                        return TRUE;
246                }
247        }
248        gtk_widget_destroy(chooser);
249        return FALSE;
250} // open_file_dialog
251
252void puncher_frontend_quit_application(GtkWidget *window, gpointer *nada) {
253        puncher_backend_finish();
254        exit(0);
255        // on window close: manage punching interaction!!
256}
257
258int main(int argc, char *argv[]) {
259        GtkWidget *window, *widget, *main_box, *tape_box, *punch_box, *statusbar;
260        gtk_init(&argc, &argv);
261       
262        // main window generation
263        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
264        gtk_window_set_default_size(GTK_WINDOW(window), 600, 600);
265        gtk_window_set_title(GTK_WINDOW(window), _("Papertape Puncher"));
266        g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(puncher_frontend_quit_application), NULL);
267       
268        // above-main-box generation (contains menubar + mainbox)
269        puncher_frontend_prepare(window);
270        widget = gtk_vbox_new(FALSE, 0);
271        gtk_container_add(GTK_CONTAINER(window), widget);
272       
273        // menus generation start
274        {
275                GtkWidget *menu, *menubar, *menuitem;
276                // menus
277                menubar = gtk_menu_bar_new();
278                gtk_box_pack_start(GTK_BOX(widget), menubar, FALSE, TRUE, 0);
279                gtk_widget_show(menubar);
280       
281                // FIRST menu: File {{{
282                menu = gtk_menu_new();
283                // Oeffnen
284                g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_OPEN)),
285                        "activate", G_CALLBACK(puncher_frontend_open_file_dialog), d.papertape);
286                fast_menu_seperator(menu);
287                gtk_paper_tape_menu_export(d.papertape, menu);
288                fast_menu_seperator(menu);
289                g_signal_connect(G_OBJECT(fast_stock_menuitem(menu, GTK_STOCK_QUIT)),
290                        "activate", G_CALLBACK(gtk_main_quit), NULL);
291       
292                menuitem = gtk_menu_item_new_with_mnemonic("_Datei");
293                gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
294                gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
295                gtk_widget_show (menuitem);
296       
297                // Zweites Menue: Ansicht
298                menu = gtk_menu_new();
299                gtk_paper_tape_menu_view(d.papertape, menu);
300       
301                menuitem = gtk_menu_item_new_with_mnemonic("_Ansicht");
302                gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
303                gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
304                gtk_widget_show (menuitem);
305       
306                // Drittes Menue: Farben
307                menu = gtk_menu_new();
308                gtk_paper_tape_menu_colors(d.papertape, menu);
309       
310                menuitem = gtk_menu_item_new_with_mnemonic("_Farben");
311                gtk_menu_item_set_submenu(GTK_MENU_ITEM (menuitem), menu);
312                gtk_menu_shell_append(GTK_MENU_SHELL (menubar), menuitem);
313                gtk_widget_show (menuitem);
314        }
315        // menus end
316       
317        // main box generation
318        main_box = gtk_vbox_new(FALSE, 0);
319        gtk_container_set_border_width(GTK_CONTAINER(main_box), 10);
320        gtk_container_add(GTK_CONTAINER(widget), main_box);
321       
322        // tape box generation
323        widget = gtk_frame_new("udo-14.06.2008-generated-file.bin");
324        gtk_box_pack_start(GTK_BOX(main_box), widget, TRUE, TRUE, 0);
325        tape_box = GTK_WIDGET(gtk_paper_tape_get_whole_box(d.papertape));
326        gtk_container_set_border_width(GTK_CONTAINER(tape_box), 10);
327        gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(d.papertape->statusbar), FALSE);
328        gtk_container_add(GTK_CONTAINER(widget), tape_box);
329
330        // punch box generation
331        widget = gtk_frame_new(_("Punching controls"));
332        gtk_box_pack_start(GTK_BOX(main_box), widget, FALSE, TRUE, 0);
333        punch_box = gtk_vbox_new(FALSE, 0);
334        gtk_container_set_border_width(GTK_CONTAINER(punch_box), 10);
335        gtk_container_add(GTK_CONTAINER(widget), punch_box);
336       
337        // fill punch box:
338        {
339                GtkWidget *label;
340                GtkWidget *holes_box, *progress_box, *button_box;
341               
342                holes_box = gtk_hbox_new(FALSE, 0);
343                gtk_container_add(GTK_CONTAINER(punch_box), holes_box);
344               
345                gtk_container_add(GTK_CONTAINER(holes_box), d.progress_holes_total);
346                gtk_container_add(GTK_CONTAINER(holes_box), d.progress_holes_punched);
347                gtk_container_add(GTK_CONTAINER(holes_box), d.progress_holes_todo);
348
349                progress_box = gtk_hbox_new(FALSE, 0);
350                gtk_container_add(GTK_CONTAINER(punch_box), progress_box);
351               
352                /*label = gtk_label_new("Fortschritt: ");
353                gtk_container_add(GTK_CONTAINER(progress_box), label);*/
354               
355                gtk_box_pack_start(GTK_BOX(progress_box), d.progress_bar, TRUE, TRUE, 0);
356               
357                label = gtk_label_new("Zeit: ");
358                gtk_container_add(GTK_CONTAINER(progress_box), label);
359                gtk_container_add(GTK_CONTAINER(progress_box), d.progress_estimated_time);
360                gtk_container_add(GTK_CONTAINER(progress_box), d.progress_elapsed_time);
361               
362                // Button boxes
363                button_box = gtk_hbutton_box_new();
364                gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_START);
365                gtk_container_add(GTK_CONTAINER(punch_box), button_box);
366               
367                gtk_container_add(GTK_CONTAINER(button_box), d.button_start);
368                g_signal_connect(G_OBJECT(d.button_start), "clicked", G_CALLBACK(gui_puncher_start), NULL);
369                gtk_container_add(GTK_CONTAINER(button_box), d.button_pause);
370                gtk_container_add(GTK_CONTAINER(button_box), d.button_stop);
371        }
372
373        // parse command options
374        {
375                char *filename = NULL;
376                gboolean read_from_stdin = FALSE;
377                GError *error = NULL;
378                GOptionContext *context;
379                byte_t show_mystart = 0; // eigene Startprozedur anzeigen?
380                GOptionEntry option_entries[] = {
381                        { "filename", 'f', 0, G_OPTION_ARG_FILENAME, &filename, _("file to open"), NULL },
382                        { "stdin", 's', 0, G_OPTION_ARG_NONE, &read_from_stdin, _("read from standard input"), NULL },
383                        { NULL }
384                };
385
386                context = g_option_context_new(_(" - GtkPaperTape visualisation"));
387                g_option_context_add_main_entries(context, option_entries, NULL);
388                g_option_context_add_group(context, gtk_get_option_group(TRUE));
389                g_option_context_parse(context, &argc, &argv, &error);
390
391                if(read_from_stdin) {
392                        printf(_("%s: Reading data from standard input, will open window after EOF.\n"), argv[0]);
393                        byte_t *data;
394                        int length = file_get_contents(stdin, &data);
395                        gtk_paper_tape_set_data(d.papertape, length, data);
396                } else if(filename != NULL) {
397                        // eine Datei einlesen
398                        printf(_("Reading from file '%s'\n"), filename);
399                        if(!gtk_paper_tape_read_from_file(d.papertape, argv[1])) {
400                                printf(_("Failed to read from file!\n"));
401                                return 1;
402                        }
403                }
404        }
405        // end of parsing command options
406
407        gtk_widget_show_all(window);
408        gtk_main();
409        return 0;
410}
Note: See TracBrowser for help on using the repository browser.
© 2008 - 2013 technikum29 • Sven Köppel • Some rights reserved
Powered by Trac
Expect where otherwise noted, content on this site is licensed under a Creative Commons 3.0 License