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