1 #include <config.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <glib.h>
6 #include <glib/gi18n.h>
7 #include <gtk/gtk.h>
8 #include <glade/glade.h>
9 #include <gdk/gdkkeysyms.h>
10
11 #include "elist.h"
12 #include "gtk_util.h"
13 #include "mru.h"
14 #include "prefs.h"
15 #include "file_list.h"
16 #include "message_box.h"
17 #include "cursor.h"
18 #include "str_util.h"
19 #include "vorbis_file.h"
20 #include "vorbis_edit_field.h"
21
22 #include "vorbis_edit.h"
23
24
25 /* widgets */
26 static GtkWindow *w_main = NULL;
27 static GtkNotebook *nb_edit = NULL;
28 static GtkNotebook *nb_vorbis = NULL;
29 static GtkMenuItem *m_vorbis = NULL;
30 static GtkCheckMenuItem *m_vor_view_simple = NULL;
31 static GtkCheckMenuItem *m_vor_view_advanced = NULL;
32
33 static GtkEntry *ent_title = NULL;
34 static GtkEntry *ent_artist = NULL;
35 static GtkEntry *ent_album = NULL;
36 static GtkEntry *ent_year = NULL;
37 static GtkEntry *ent_comment = NULL;
38 static GtkEntry *ent_track = NULL;
39 static GtkCombo *combo_genre = NULL;
40 static GtkLabel *lab_title = NULL;
41 static GtkLabel *lab_artist = NULL;
42 static GtkLabel *lab_album = NULL;
43 static GtkLabel *lab_year = NULL;
44 static GtkLabel *lab_comment = NULL;
45 static GtkLabel *lab_track = NULL;
46 static GtkLabel *lab_genre = NULL;
47 static GtkLabel *lab_advanced = NULL;
48 static GtkButton *b_advanced = NULL;
49 static GtkButton *b_clear = NULL;
50 static GtkButton *b_write = NULL;
51
52 static GtkTreeView *tv_comments = NULL;
53 static GtkListStore *store_comments = NULL;
54 static GtkButton *b_add = NULL;
55 static GtkButton *b_edit = NULL;
56 static GtkButton *b_remove = NULL;
57 static GtkButton *b_simple = NULL;
58
59 /* the various notebook tabs */
60 #define TAB_SIMPLE 0
61 #define TAB_ADVANCED 1
62
63 /* private data */
64 static int tab_edit_vorbis;
65 static gboolean changed_flag = TRUE;
66 static gboolean editable_flag = TRUE;
67 static gboolean ignore_changed_signals = FALSE;
68 static int extra_fields = 0;
69 static vorbis_file *file = NULL;
70
71 /* preferences */
72 static MRUList *genre_mru;
73 static int *current_tab;
74
75
76 /*** private functions ******************************************************/
77
tree_view_setup()78 static void tree_view_setup()
79 {
80 GtkTreeViewColumn *col;
81 GtkCellRenderer *renderer;
82
83 /* model */
84 store_comments = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
85
86 /* columns and renderers */
87 col = gtk_tree_view_column_new();
88 gtk_tree_view_column_set_title(col, _("Field"));
89 gtk_tree_view_append_column(tv_comments, col);
90
91 renderer = gtk_cell_renderer_text_new();
92 g_object_set(renderer, "ypad", 0, NULL);
93 gtk_tree_view_column_pack_start(col, renderer, FALSE);
94 gtk_tree_view_column_add_attribute(col, renderer, "text", 0);
95
96 col = gtk_tree_view_column_new();
97 gtk_tree_view_column_set_title(col, _("Text"));
98 gtk_tree_view_append_column(tv_comments, col);
99
100 renderer = gtk_cell_renderer_text_new();
101 g_object_set(renderer, "ypad", 0, NULL);
102 gtk_tree_view_column_pack_start(col, renderer, FALSE);
103 gtk_tree_view_column_add_attribute(col, renderer, "text", 1);
104
105 /* selection mode */
106 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(tv_comments),
107 GTK_SELECTION_SINGLE);
108 }
109
set_changed_flag(gboolean value)110 static void set_changed_flag(gboolean value)
111 {
112 if (changed_flag == value)
113 return;
114
115 changed_flag = value;
116 gtk_widget_set_sensitive(GTK_WIDGET(b_write), value);
117 }
118
set_editable_flag(gboolean value)119 static void set_editable_flag(gboolean value)
120 {
121 if (editable_flag == value)
122 return;
123
124 editable_flag = value;
125
126 /* simple edit controls */
127 gtk_widget_set_sensitive(GTK_WIDGET(ent_title), value);
128 gtk_widget_set_sensitive(GTK_WIDGET(lab_title), value);
129 gtk_widget_set_sensitive(GTK_WIDGET(ent_artist), value);
130 gtk_widget_set_sensitive(GTK_WIDGET(lab_artist), value);
131 gtk_widget_set_sensitive(GTK_WIDGET(ent_album), value);
132 gtk_widget_set_sensitive(GTK_WIDGET(lab_album), value);
133 gtk_widget_set_sensitive(GTK_WIDGET(ent_year), value);
134 gtk_widget_set_sensitive(GTK_WIDGET(lab_year), value);
135 gtk_widget_set_sensitive(GTK_WIDGET(ent_comment), value);
136 gtk_widget_set_sensitive(GTK_WIDGET(lab_comment), value);
137 gtk_widget_set_sensitive(GTK_WIDGET(combo_genre), value);
138 gtk_widget_set_sensitive(GTK_WIDGET(lab_genre), value);
139 gtk_widget_set_sensitive(GTK_WIDGET(ent_track), value);
140 gtk_widget_set_sensitive(GTK_WIDGET(lab_track), value);
141 gtk_widget_set_sensitive(GTK_WIDGET(b_clear), value);
142
143 /* advanced edit controls */
144 gtk_widget_set_sensitive(GTK_WIDGET(tv_comments), value);
145 gtk_widget_set_sensitive(GTK_WIDGET(b_add), value);
146 gtk_widget_set_sensitive(GTK_WIDGET(b_edit), value);
147 gtk_widget_set_sensitive(GTK_WIDGET(b_remove), value);
148 }
149
150
is_simple_field(char * name)151 static gboolean is_simple_field(char *name)
152 {
153 static char *fields[] = {
154 "title",
155 "artist",
156 "album",
157 "date",
158 "genre",
159 "comment",
160 "tracknumber",
161 NULL
162 };
163 int i;
164
165 for (i = 0; fields[i] != NULL; i++)
166 if (strcmp(name, fields[i]) == 0)
167 return TRUE;
168
169 return FALSE;
170 }
171
172
count_extra_fields_callback(gchar * name,gchar * text,gpointer data)173 static void count_extra_fields_callback(gchar *name, gchar *text, gpointer data)
174 {
175 if (!is_simple_field(name))
176 extra_fields++;
177 }
178
update_form_simple()179 static void update_form_simple()
180 {
181 const gchar *str;
182
183 ignore_changed_signals = TRUE;
184 if (g_elist_length(genre_mru->list) > 0)
185 gtk_combo_set_popdown_strings(combo_genre, GLIST(genre_mru->list));
186 vorbis_file_get_field(file, AF_TITLE, &str); gtk_entry_set_text(ent_title, str);
187 vorbis_file_get_field(file, AF_ARTIST, &str); gtk_entry_set_text(ent_artist, str);
188 vorbis_file_get_field(file, AF_ALBUM, &str); gtk_entry_set_text(ent_album, str);
189 vorbis_file_get_field(file, AF_YEAR, &str); gtk_entry_set_text(ent_year, str);
190 vorbis_file_get_field(file, AF_COMMENT, &str); gtk_entry_set_text(ent_comment, str);
191 vorbis_file_get_field(file, AF_TRACK, &str); gtk_entry_set_text(ent_track, str);
192 vorbis_file_get_field(file, AF_GENRE, &str); gtk_entry_set_text(GTK_ENTRY(combo_genre->entry), str);
193 ignore_changed_signals = FALSE;
194
195 extra_fields = 0;
196 g_hash_table_foreach(file->comments, (GHFunc)count_extra_fields_callback, NULL);
197
198 if (extra_fields > 0) {
199 char str[16];
200 snprintf(str, 16, _("Advanced (%i)"), extra_fields);
201 gtk_label_set_text(lab_advanced, str);
202 }
203 else {
204 gtk_label_set_text(lab_advanced, _("Advanced"));
205 }
206 }
207
append_row_callback(gchar * name,gchar * text,GtkListStore * store)208 static void append_row_callback(gchar *name, gchar *text, GtkListStore *store)
209 {
210 GtkTreeIter iter;
211
212 gtk_list_store_append(store, &iter);
213 gtk_list_store_set(store, &iter, 0, name, 1, text, -1);
214 }
215
update_form_advanced()216 static void update_form_advanced()
217 {
218 /* fill in the list */
219 gtk_tree_view_set_model(tv_comments, NULL);
220 gtk_list_store_clear(store_comments);
221
222 g_hash_table_foreach(file->comments, (GHFunc)append_row_callback, store_comments);
223 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store_comments), 0, GTK_SORT_ASCENDING);
224
225 gtk_tree_view_set_model(tv_comments, GTK_TREE_MODEL(store_comments));
226
227 gtk_widget_set_sensitive(GTK_WIDGET(b_edit), FALSE);
228 gtk_widget_set_sensitive(GTK_WIDGET(b_remove), FALSE);
229 }
230
update_form()231 static void update_form()
232 {
233 if (!file)
234 return;
235
236 if (*current_tab == TAB_SIMPLE)
237 update_form_simple();
238 else
239 update_form_advanced();
240 }
241
242
update_tag()243 static void update_tag()
244 {
245 if (!file || !changed_flag)
246 return;
247
248 if (*current_tab == TAB_SIMPLE) {
249 vorbis_file_set_field(file, AF_TITLE, gtk_entry_get_text(ent_title));
250 vorbis_file_set_field(file, AF_ARTIST, gtk_entry_get_text(ent_artist));
251 vorbis_file_set_field(file, AF_ALBUM, gtk_entry_get_text(ent_album));
252 vorbis_file_set_field(file, AF_YEAR, gtk_entry_get_text(ent_year));
253 vorbis_file_set_field(file, AF_COMMENT, gtk_entry_get_text(ent_comment));
254 vorbis_file_set_field(file, AF_TRACK, gtk_entry_get_text(ent_track));
255 vorbis_file_set_field(file, AF_GENRE, gtk_entry_get_text(GTK_ENTRY(combo_genre->entry)));
256 }
257 else {
258 /* Nothing to do here, advanced mode alters tag directly */
259 }
260 }
261
262
write_to_file()263 static void write_to_file()
264 {
265 int res;
266 const char *genre;
267
268 cursor_set_wait();
269
270 genre = gtk_entry_get_text(GTK_ENTRY(combo_genre->entry));
271 if (strcmp(genre, "") != 0)
272 mru_add(genre_mru, genre);
273
274 res = vorbis_file_write_changes(file);
275 if (res != AF_OK) {
276 char msg[512];
277 if (errno == EACCES || errno == EPERM) {
278 snprintf(msg, sizeof(msg),
279 _("Error saving file \"%s\":\n%s (%d)\n\n"
280 "Note:\n"
281 "In order to save changes to an Ogg Vorbis file, you must have\n"
282 "write permission for the directory where it is located."),
283 file->name, strerror(errno), errno);
284 }
285 else {
286 snprintf(msg, sizeof(msg),
287 _("Error saving file \"%s\":\n%s (%d)"),
288 file->name, strerror(errno), errno);
289 }
290
291 message_box(w_main, _("Error Saving File"), msg, 0, GTK_STOCK_OK, NULL);
292 }
293 else {
294 set_changed_flag(FALSE);
295 }
296
297 cursor_set_normal();
298 }
299
300
301
302 /* UI callbacks */
cb_vor_clear(GtkButton * button,gpointer user_data)303 void cb_vor_clear(GtkButton *button, gpointer user_data)
304 {
305 vorbis_file_remove_tag(file);
306 update_form();
307
308 set_changed_flag(TRUE);
309 }
310
cb_vor_write(GtkButton * button,gpointer user_data)311 void cb_vor_write(GtkButton *button, gpointer user_data)
312 {
313 if (!file) {
314 g_warning("file is not open!");
315 return;
316 }
317
318 update_tag();
319 write_to_file();
320 }
321
cb_vor_view_simple(GtkWidget * widget,GdkEvent * event)322 void cb_vor_view_simple(GtkWidget *widget, GdkEvent *event)
323 {
324 if (*current_tab != TAB_SIMPLE) {
325 update_tag();
326 *current_tab = TAB_SIMPLE;
327 update_form();
328 gtk_notebook_set_page(nb_vorbis, *current_tab);
329 }
330 }
331
cb_vor_view_advanced(GtkWidget * widget,GdkEvent * event)332 void cb_vor_view_advanced(GtkWidget *widget, GdkEvent *event)
333 {
334 if (*current_tab != TAB_ADVANCED) {
335 update_tag();
336 *current_tab = TAB_ADVANCED;
337 update_form();
338 gtk_notebook_set_page(nb_vorbis, *current_tab);
339 }
340 }
341
cb_vor_view_button(GtkButton * button,gpointer user_data)342 void cb_vor_view_button(GtkButton *button, gpointer user_data)
343 {
344 if (*current_tab == TAB_SIMPLE)
345 gtk_check_menu_item_set_active(m_vor_view_advanced, TRUE);
346 else if (*current_tab == TAB_ADVANCED)
347 gtk_check_menu_item_set_active(m_vor_view_simple, TRUE);
348 }
349
350
351 /* callbacks for the simple editor */
cb_vor_tag_changed(GObject * obj,gpointer user_data)352 void cb_vor_tag_changed(GObject *obj, gpointer user_data)
353 {
354 if (!ignore_changed_signals)
355 set_changed_flag(TRUE);
356 }
357
cb_vor_keypress(GtkWidget * widget,GdkEventKey * event,gpointer user_data)358 gboolean cb_vor_keypress(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
359 {
360 if (event->keyval == GDK_Return) {
361 if (file && changed_flag) {
362 update_tag();
363 write_to_file();
364 }
365 fl_select_next_file();
366 return TRUE;
367 }
368
369 return FALSE;
370 }
371
372 /* callbacks for the advanced editor */
cb_vor_comment_selection_changed(GtkTreeSelection * selection,gpointer data)373 void cb_vor_comment_selection_changed(GtkTreeSelection *selection, gpointer data)
374 {
375 if (gtk_tree_selection_count_selected_rows(selection) == 0) {
376 gtk_widget_set_sensitive(GTK_WIDGET(b_edit), FALSE);
377 gtk_widget_set_sensitive(GTK_WIDGET(b_remove), FALSE);
378 }
379 else {
380 gtk_widget_set_sensitive(GTK_WIDGET(b_edit), TRUE);
381 gtk_widget_set_sensitive(GTK_WIDGET(b_remove), TRUE);
382 }
383 }
384
cb_vor_add(GtkButton * button,gpointer user_data)385 void cb_vor_add(GtkButton *button, gpointer user_data)
386 {
387 if ( vorbis_editfld_create_comment(file) ) {
388 set_changed_flag(TRUE);
389 update_form();
390 }
391 }
392
cb_vor_edit(GtkButton * button,gpointer user_data)393 void cb_vor_edit(GtkButton *button, gpointer user_data)
394 {
395 GtkTreeModel *model;
396 GtkTreeIter iter;
397
398 if (gtk_tree_view_get_first_selected(tv_comments, &model, &iter)) {
399 char *name;
400 gtk_tree_model_get(model, &iter, 0, &name, -1);
401
402 if ( vorbis_editfld_edit_comment(file, name) ) {
403 set_changed_flag(TRUE);
404 update_form();
405 }
406 }
407 }
408
cb_vor_comment_row_activated(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * col,gpointer user_data)409 void cb_vor_comment_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data)
410 {
411 /* same as clicking the edit button */
412 cb_vor_edit(NULL, NULL);
413 }
414
cb_vor_remove(GtkButton * button,gpointer user_data)415 void cb_vor_remove(GtkButton *button, gpointer user_data)
416 {
417 GtkTreeModel *model;
418 GtkTreeIter iter;
419
420 if (gtk_tree_view_get_first_selected(tv_comments, &model, &iter)) {
421 char *name;
422 gtk_tree_model_get(model, &iter, 0, &name, -1);
423
424 /* Setting a comment to empty removes it */
425 vorbis_file_set_field_by_name(file, name, "");
426 set_changed_flag(TRUE);
427 update_form();
428 }
429 }
430
431
432 /*** public functions *******************************************************/
433
vorbis_edit_load(vorbis_file * f)434 void vorbis_edit_load(vorbis_file *f)
435 {
436 gtk_widget_show(GTK_WIDGET(m_vorbis));
437
438 file = f;
439 update_form();
440
441 set_editable_flag(audio_file_is_editable((audio_file *)file));
442 set_changed_flag(FALSE);
443
444 gtk_notebook_set_page(nb_edit, tab_edit_vorbis);
445 gtk_notebook_set_page(nb_vorbis, *current_tab);
446 }
447
448
vorbis_edit_unload()449 void vorbis_edit_unload()
450 {
451 if (changed_flag) {
452 int button;
453
454 button = message_box(w_main, _("Save Changes"),
455 _("Vorbis Tag has been modified. Save changes?"), 1,
456 _("Discard"), GTK_STOCK_SAVE, NULL);
457 if (button == 1) {
458 /* save button was pressed */
459 update_tag();
460 write_to_file();
461 }
462 }
463
464 file = NULL;
465
466 if (GTK_IS_WIDGET(m_vorbis))
467 gtk_widget_hide(GTK_WIDGET(m_vorbis));
468 }
469
470
vorbis_edit_init(GladeXML * xml)471 void vorbis_edit_init(GladeXML *xml)
472 {
473 GEList *temp_list;
474 GEList *genre_list;
475 int default_tab;
476
477 /*
478 * get the widgets from glade
479 */
480
481 w_main = GTK_WINDOW(glade_xml_get_widget(xml, "w_main"));
482 nb_edit = GTK_NOTEBOOK(glade_xml_get_widget(xml, "nb_edit"));
483 nb_vorbis = GTK_NOTEBOOK(glade_xml_get_widget(xml, "nb_vorbis"));
484 m_vorbis = GTK_MENU_ITEM(glade_xml_get_widget(xml, "m_vorbis"));
485 m_vor_view_simple = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(xml, "m_vor_view_simple"));
486 m_vor_view_advanced = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(xml, "m_vor_view_advanced"));
487
488 ent_title = GTK_ENTRY(glade_xml_get_widget(xml, "ent_vor_title"));
489 ent_artist = GTK_ENTRY(glade_xml_get_widget(xml, "ent_vor_artist"));
490 ent_album = GTK_ENTRY(glade_xml_get_widget(xml, "ent_vor_album"));
491 ent_year = GTK_ENTRY(glade_xml_get_widget(xml, "ent_vor_year"));
492 ent_comment = GTK_ENTRY(glade_xml_get_widget(xml, "ent_vor_comment"));
493 ent_track = GTK_ENTRY(glade_xml_get_widget(xml, "ent_vor_track"));
494 combo_genre = GTK_COMBO(glade_xml_get_widget(xml, "combo_vor_genre"));
495 lab_title = GTK_LABEL(glade_xml_get_widget(xml, "lab_vor_title"));
496 lab_artist = GTK_LABEL(glade_xml_get_widget(xml, "lab_vor_artist"));
497 lab_album = GTK_LABEL(glade_xml_get_widget(xml, "lab_vor_album"));
498 lab_year = GTK_LABEL(glade_xml_get_widget(xml, "lab_vor_year"));
499 lab_comment = GTK_LABEL(glade_xml_get_widget(xml, "lab_vor_comment"));
500 lab_track = GTK_LABEL(glade_xml_get_widget(xml, "lab_vor_track"));
501 lab_genre = GTK_LABEL(glade_xml_get_widget(xml, "lab_vor_genre"));
502 lab_advanced = GTK_LABEL(glade_xml_get_widget(xml, "lab_vor_advanced"));
503 b_advanced = GTK_BUTTON(glade_xml_get_widget(xml, "b_vor_advanced"));
504 b_clear = GTK_BUTTON(glade_xml_get_widget(xml, "b_vor_clear"));
505 b_write = GTK_BUTTON(glade_xml_get_widget(xml, "b_vor_write"));
506
507 tv_comments = GTK_TREE_VIEW(glade_xml_get_widget(xml, "tv_vor_comments"));
508 b_add = GTK_BUTTON(glade_xml_get_widget(xml, "b_vor_add"));
509 b_edit = GTK_BUTTON(glade_xml_get_widget(xml, "b_vor_edit"));
510 b_remove = GTK_BUTTON(glade_xml_get_widget(xml, "b_vor_remove"));
511 b_simple = GTK_BUTTON(glade_xml_get_widget(xml, "b_vor_simple"));
512
513 /* set up the tree view */
514 tree_view_setup();
515 g_signal_connect(gtk_tree_view_get_selection(tv_comments), "changed",
516 G_CALLBACK(cb_vor_comment_selection_changed), NULL);
517
518 /* find out which tab has the Vorbis interface */
519 tab_edit_vorbis = gtk_notebook_page_num(nb_edit, glade_xml_get_widget(xml, "cont_vorbis_edit"));
520
521
522 /*
523 * get the preference values, or set them to defaults
524 */
525
526 /* genre_mru */
527 temp_list = g_elist_new();
528 genre_list = pref_get_or_set("common_edit:genre_mru", PREF_STRING | PREF_LIST, temp_list);
529 g_elist_free_data(temp_list);
530 genre_mru = mru_new_from_list(50, genre_list);
531
532 /* current_tab */
533 default_tab = TAB_SIMPLE;
534 current_tab = pref_get_or_set("vorbis_edit:current_tab", PREF_INT, &default_tab);
535 if (*current_tab != TAB_SIMPLE && *current_tab != TAB_ADVANCED)
536 *current_tab = default_tab;
537
538 if (*current_tab == TAB_SIMPLE)
539 gtk_check_menu_item_set_active(m_vor_view_simple, TRUE);
540 else
541 gtk_check_menu_item_set_active(m_vor_view_advanced, TRUE);
542 }
543
544