1 /*
2  *    TTTTTTTTTTTTTT  EEEEEEEEEEEEEE  OOOOOOOOOOOOOO
3  *    TTTTTTTTTTTTTT  EEEEEEEEEEEEEE  OOOOOOOOOOOOOO
4  *          TT        EE              OO          OO
5  *          TT        EE              OO          OO
6  *          TT        EE              OO          OO
7  *          TT        EEEEEEEEEE      OO          OO
8  *          TT        EEEEEEEEEE      OO          OO
9  *          TT        EE              OO          OO
10  *          TT        EE              OO          OO
11  *          TT        EE              OO          OO
12  *          TT        EEEEEEEEEEEEEE  OOOOOOOOOOOOOO
13  *          TT        EEEEEEEEEEEEEE  OOOOOOOOOOOOOO
14  *
15  *                  L'�mulateur Thomson TO8
16  *
17  *  Copyright (C) 1997-2017 Gilles F�tis, Eric Botcazou, Alexandre Pukall,
18  *                          J�r�mie Guillaume, Fran�ois Mouret
19  *
20  *  This program is free software; you can redistribute it and/or modify
21  *  it under the terms of the GNU General Public License as published by
22  *  the Free Software Foundation; either version 2 of the License, or
23  *  (at your option) any later version.
24  *
25  *  This program is distributed in the hope that it will be useful,
26  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
27  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  *  GNU General Public License for more details.
29  *
30  *  You should have received a copy of the GNU General Public License
31  *  along with this program; if not, write to the Free Software
32  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
33  */
34 
35 /*
36  *  Module     : linux/ugui/ucass.c
37  *  Version    : 1.8.4
38  *  Cr�� par   : Eric Botcazou juillet 1999
39  *  Modifi� par: Eric Botcazou 19/11/2006
40  *               Gilles F�tis 27/07/2011
41  *               Fran�ois Mouret 07/08/2011 24/03/2012 12/06/2012
42  *                               04/11/2012 31/05/2015
43  *
44  *  Gestion des cassettes.
45  */
46 
47 
48 #ifndef SCAN_DEPEND
49    #include <stdio.h>
50    #include <unistd.h>
51    #include <string.h>
52    #include <libgen.h>
53    #include <gtk/gtk.h>
54 #endif
55 
56 #include "teo.h"
57 #include "std.h"
58 #include "errors.h"
59 #include "media/cass.h"
60 #include "linux/gui.h"
61 
62 #define COUNTER_MAX  999
63 
64 static GtkWidget *combo;
65 static gulong combo_id;
66 static GtkWidget *check_prot;
67 static gulong check_prot_id;
68 static GtkWidget *emptying_button;
69 static gulong emptying_button_id;
70 static GtkWidget *counter_box;
71 static int entry_max=0;
72 static GtkWidget *spinner_cass;
73 static GList *path_list = NULL;
74 
75 
76 
block_all(void)77 static void block_all (void)
78 {
79     g_signal_handler_block (combo, combo_id);
80     g_signal_handler_block (check_prot, check_prot_id);
81     g_signal_handler_block (emptying_button, emptying_button_id);
82 }
83 
84 
85 
unblock_all(void)86 static void unblock_all (void)
87 {
88     g_signal_handler_unblock (combo, combo_id);
89     g_signal_handler_unblock (check_prot, check_prot_id);
90     g_signal_handler_unblock (emptying_button, emptying_button_id);
91 }
92 
93 
94 
95 /* update_params:
96  *  Ajuste les param�tres de cassette.
97  */
update_params(void)98 static void update_params (void)
99 {
100     int combo_index;
101 
102     if (combo_id != 0)
103     {
104         block_all ();
105 
106         combo_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
107 
108         if (combo_index == 0)
109         {
110             gtk_widget_set_sensitive (emptying_button, FALSE);
111             gtk_widget_set_sensitive (check_prot, FALSE);
112             gtk_widget_set_sensitive (counter_box, FALSE);
113         }
114         else
115         {
116             gtk_widget_set_sensitive (emptying_button, TRUE);
117             gtk_widget_set_sensitive (check_prot, TRUE);
118             gtk_widget_set_sensitive (counter_box, TRUE);
119         }
120 
121         unblock_all ();
122     }
123 }
124 
125 
126 
127 /* set_counter_cass:
128  *  Positionne le compteur de cassette.
129  */
set_counter_cass(void)130 static void set_counter_cass (void)
131 {
132    gtk_spin_button_set_value((GtkSpinButton *)spinner_cass, cass_GetCounter());
133 }
134 
135 
136 
137 /* rewind_cass:
138  *  Met le compteur de cassette � 0.
139  */
rewind_cass(void)140 static void rewind_cass (void)
141 {
142    cass_SetCounter(0);
143    set_counter_cass ();
144 }
145 
146 
147 
148 /* eject_cass:
149  *  Ejecte la cassette.
150  */
eject_cass(void)151 static void eject_cass (void)
152 {
153     rewind_cass ();
154     cass_Eject();
155     update_params ();
156 }
157 
158 
159 
160 /* click_rewind_cass:
161  *  Met le compteur de cassette � 0 (callback).
162  */
click_rewind_cass(GtkWidget * button,gpointer data)163 static void click_rewind_cass (GtkWidget *button, gpointer data)
164 {
165     rewind_cass ();
166     (void)button;
167     (void)data;
168 }
169 
170 
171 
172 /* load_cass:
173  *  Charge une cassette.
174  */
load_cass(gchar * filename)175 static int load_cass (gchar *filename)
176 {
177     int ret = cass_Load (filename);
178 
179     rewind_cass();
180     switch (ret)
181     {
182         case TEO_ERROR :
183             ugui_Error (teo_error_msg, wControl);
184             break;
185 
186         case TRUE :
187             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(check_prot), TRUE);
188             teo.cass.write_protect = TRUE;
189             break;
190 
191         default : break;
192     }
193     return ret;
194 }
195 
196 
197 
198 /* toggle_check_cass:
199  *  Gestion de la protection (callback).
200  */
toggle_check_cass(GtkWidget * button,gpointer data)201 static void toggle_check_cass (GtkWidget *button, gpointer data)
202 {
203     if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
204     {
205         cass_SetProtection(TRUE);
206         teo.cass.write_protect = TRUE;
207     }
208     else
209     {
210         teo.cass.write_protect = FALSE;
211 
212         if (cass_SetProtection(FALSE) == TRUE)
213         {
214             ugui_Error ((is_fr?"Ecriture impossible sur ce support."
215                               :"Writing unavailable on this device."), wControl);
216             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
217             teo.cass.write_protect = TRUE;
218         }
219     }
220     set_counter_cass ();
221     update_params ();
222     (void)data;
223 }
224 
225 
226 
227 /* change_counter_cass:
228  *  Change le compteur de la cassette (callback).
229  */
change_counter_cass(GtkWidget * widget,gpointer data)230 static void change_counter_cass (GtkWidget *widget, gpointer data)
231 {
232    cass_SetCounter(gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinner_cass)));
233    (void)widget;
234    (void)data;
235 }
236 
237 
238 
239 /* add_combo_entry
240  *  Ajoute une entr�e dans le combobox si inexistante.
241  */
add_combo_entry(const char * path)242 static void add_combo_entry (const char *path)
243 {
244     GList *path_node = g_list_find_custom (path_list, (gconstpointer)path, (GCompareFunc)g_strcmp0);
245 
246     if (path_node != NULL)
247     {
248         gtk_combo_box_set_active (GTK_COMBO_BOX(combo), g_list_position (path_list, path_node));
249     }
250     else
251     {
252         path_list = g_list_append (path_list, (gpointer)(g_strdup_printf (path,"%s")));
253         gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT(combo), NULL, (gchar *)basename((char *)path));
254         gtk_combo_box_set_active (GTK_COMBO_BOX(combo), entry_max);
255         entry_max++;
256     }
257     update_params ();
258 }
259 
260 
261 
262 /* init_combo:
263  *  Remplit un combo vide.
264  */
init_combo(void)265 static void init_combo (void)
266 {
267     add_combo_entry (is_fr?"(Aucun)":"(None)");
268 }
269 
270 
271 
272 /* reset_combo:
273  *  Vide le combobox (callback).
274  */
reset_combo(GtkButton * button,gpointer data)275 static void reset_combo (GtkButton *button, gpointer data)
276 {
277     block_all ();
278 
279     ucass_Free ();
280     gtk_combo_box_text_remove_all (GTK_COMBO_BOX_TEXT(combo));
281     eject_cass ();
282     init_combo ();
283 
284     unblock_all ();
285 
286     update_params ();
287 
288     (void)button;
289     (void)data;
290 }
291 
292 
293 
294 /* combo_changed:
295  *  Changement de s�lection du combobox (callback).
296  */
combo_changed(GtkComboBox * combo_box,gpointer data)297 static void combo_changed (GtkComboBox *combo_box, gpointer data)
298 {
299     int entry_selected = gtk_combo_box_get_active (combo_box);
300 
301     if (entry_selected == 0)
302     {
303         eject_cass ();
304     }
305     else
306     {
307         (void)load_cass((char *)g_list_nth_data (path_list, (guint)entry_selected));
308     }
309     update_params ();
310 
311     (void)data;
312 }
313 
314 
315 
316 /* open_file:
317  *  Charge une nouvelle cassette (callback).
318  */
open_file(GtkButton * button,gpointer data)319 static void open_file (GtkButton *button, gpointer data)
320 {
321     static int first=1;
322     GtkFileFilter *filter;
323     static GtkWidget *dialog;
324     gchar *folder_name;
325     gchar *file_name;
326 
327     if (first) {
328         dialog = gtk_file_chooser_dialog_new (
329                  is_fr?"Sélectionner une cassette":"Select a tape",
330                  (GtkWindow *) wControl, GTK_FILE_CHOOSER_ACTION_OPEN,
331                  is_fr?"_Annuler":"_Cancel", GTK_RESPONSE_CANCEL,
332                  is_fr?"_Ouvrir":"_Open", GTK_RESPONSE_ACCEPT, NULL);
333         filter = gtk_file_filter_new ();
334         gtk_file_filter_set_name (filter, is_fr?"Fichiers cassette (.k7)":"Tape files (.k7)");
335         gtk_file_filter_add_pattern (filter, "*.k7");
336         gtk_file_filter_add_pattern (filter, "*.K7");
337         gtk_file_chooser_add_filter ((GtkFileChooser *)dialog, filter);
338 
339         /* Attend que le dialog ait tout assimil� */
340         while (gtk_events_pending ())
341             gtk_main_iteration ();
342 
343         first=0;
344     }
345 
346     if (teo.cass.file != NULL)
347     {
348         folder_name = std_strdup_printf ("%s", teo.cass.file);
349         std_CleanPath (folder_name);
350         (void)gtk_file_chooser_set_current_folder((GtkFileChooser *)dialog, folder_name);
351         folder_name = std_free (folder_name);
352     }
353     else
354     if (teo.default_folder != NULL)
355         (void)gtk_file_chooser_set_current_folder((GtkFileChooser *)dialog, teo.default_folder);
356     else
357     if (access("./disks/", F_OK) == 0)
358         (void)gtk_file_chooser_set_current_folder((GtkFileChooser *)dialog, "./k7/");
359 
360     if (gtk_dialog_run ((GtkDialog *)dialog) == GTK_RESPONSE_ACCEPT)
361     {
362         file_name = gtk_file_chooser_get_filename((GtkFileChooser *)dialog);
363         if (load_cass (file_name) >= 0)
364         {
365             block_all ();
366 
367             add_combo_entry (teo.cass.file);
368             folder_name = gtk_file_chooser_get_current_folder ((GtkFileChooser *)dialog);
369             teo.default_folder = std_free(teo.default_folder);
370             if (folder_name != NULL)
371                 teo.default_folder = std_strdup_printf ("%s",folder_name);
372             g_free (folder_name);
373 
374             unblock_all ();
375             update_params ();
376         }
377         g_free (file_name);
378     }
379     gtk_widget_hide(dialog);
380     (void)button;
381     (void)data;
382 }
383 
384 
385 /* --------------------------- Partie publique ----------------------------- */
386 
387 
388 /* ucass_Free
389  *  Lib�re la m�moire utilis�e par la liste des cassettes.
390  */
ucass_Free(void)391 void ucass_Free (void)
392 {
393     g_list_foreach (path_list, (GFunc)g_free, (gpointer) NULL);
394     g_list_free (path_list);
395     path_list=NULL;
396     entry_max = 0;
397 }
398 
399 
400 
401 /* ucass_UpdateCounter:
402  *  Positionne le compteur de cassette.
403  */
ucass_UpdateCounter(void)404 void ucass_UpdateCounter (void)
405 {
406     set_counter_cass ();
407 }
408 
409 
410 
411 /* ucass_Init:
412  *  Initialise la frame du notebook pour la cassette.
413  */
ucass_Init(GtkWidget * notebook)414 void ucass_Init (GtkWidget *notebook)
415 {
416     GtkWidget *vbox;
417     GtkWidget *hbox;
418     GtkWidget *widget;
419     GtkWidget *image;
420     GtkWidget *frame;
421     GtkAdjustment *adjustment;
422 
423     /* frame de la cartouche */
424     frame=gtk_frame_new("");
425     gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_NONE);
426     gtk_frame_set_label_align( GTK_FRAME(frame), 0.985, 0.0);
427     widget=gtk_label_new((is_fr?"Cassette":"Tape"));
428     gtk_notebook_append_page( GTK_NOTEBOOK(notebook), frame, widget);
429 
430     /* bo�te verticale associ�e � la frame */
431     vbox=gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
432     gtk_container_set_border_width( GTK_CONTAINER(vbox), 5);
433     gtk_container_add( GTK_CONTAINER(frame), vbox);
434 
435     /* bo�te horizontale */
436     hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
437     gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
438 
439     /* bouton de vidange */
440     emptying_button = gtk_button_new ();
441     image = gtk_image_new_from_icon_name ("edit-clear", GTK_ICON_SIZE_BUTTON);
442     gtk_button_set_image(GTK_BUTTON(emptying_button), image);
443     gtk_widget_set_tooltip_text (emptying_button,
444                                  is_fr?"Vide la liste des fichiers"
445                                        :"Empty the file list");
446     gtk_box_pack_start( GTK_BOX(hbox), emptying_button, FALSE, FALSE, 0);
447     emptying_button_id = g_signal_connect(G_OBJECT(emptying_button),
448                                           "clicked",
449                                           G_CALLBACK(reset_combo),
450                                           (gpointer) NULL);
451 
452     /* combobox pour le rappel de cassette */
453     combo=gtk_combo_box_text_new();
454     gtk_box_pack_start( GTK_BOX(hbox), combo, TRUE, TRUE,0);
455     init_combo ();
456     if (teo.cass.file != NULL)
457         add_combo_entry (teo.cass.file);
458     combo_id = g_signal_connect (G_OBJECT(combo),
459                                          "changed",
460                                          G_CALLBACK(combo_changed),
461                                          (gpointer) NULL);
462 
463     /* bouton protection de la cassette */
464     check_prot=gtk_check_button_new_with_label("prot.");
465     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_prot),
466                                  teo.cass.write_protect);
467     check_prot_id = g_signal_connect(G_OBJECT(check_prot),
468                                      "toggled",
469                                      G_CALLBACK(toggle_check_cass),
470                                      (gpointer)NULL);
471     gtk_box_pack_end( GTK_BOX(hbox), check_prot, FALSE, TRUE, 0);
472 
473     /* bouton d'ouverture de fichier */
474     widget = gtk_button_new ();
475     image = gtk_image_new_from_icon_name ("document-open", GTK_ICON_SIZE_BUTTON);
476     gtk_button_set_image(GTK_BUTTON(widget), image);
477     gtk_widget_set_tooltip_text (widget, is_fr?"Ouvrir un fichier cassette"
478                                               :"Open a tape file");
479     gtk_box_pack_start( GTK_BOX(hbox), widget, FALSE, FALSE, 0);
480     (void)g_signal_connect(G_OBJECT(widget),
481                            "clicked",
482                            G_CALLBACK(open_file),
483                            (gpointer) check_prot);
484 
485     /* molette du compteur de cassette */
486     adjustment = gtk_adjustment_new (0, 0, COUNTER_MAX, 1, 10, 0);
487     spinner_cass = gtk_spin_button_new (adjustment, 0.5, 0);
488 
489     /* seconde bo�te horizontale de la cassette */
490     counter_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
491     gtk_box_pack_start( GTK_BOX(vbox), counter_box, FALSE, FALSE, 0);
492 
493     /* molette du compteur de cassette */
494     widget=gtk_label_new((is_fr?"Compteur:":"Counter:"));
495     gtk_box_pack_start (GTK_BOX (counter_box), widget, FALSE, FALSE, 0);
496 
497     /* Connecter le signal directement au spin button ne marche pas. */
498     g_signal_connect(G_OBJECT(adjustment),
499                      "value_changed",
500                      G_CALLBACK(change_counter_cass),
501                      (gpointer)NULL);
502     gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner_cass), FALSE);
503     gtk_box_pack_start (GTK_BOX (counter_box), spinner_cass, FALSE, FALSE, 0);
504 
505     /* bouton rembobinage */
506     widget=gtk_button_new_with_label((is_fr?"Rembobiner la cassette":"Rewind tape"));
507     g_signal_connect(G_OBJECT(widget), "clicked",
508                      G_CALLBACK(click_rewind_cass), (gpointer)NULL);
509     gtk_box_pack_start( GTK_BOX(counter_box), widget, TRUE, FALSE, 0);
510 
511     update_params ();
512 }
513 
514