1 /*                                                     -*- linux-c -*-
2     Copyright (C) 2004 Tom Szilagyi
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18     $Id: file_info.c 1298 2014-05-19 12:53:11Z tszilagyi $
19 */
20 
21 #include <config.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <glib.h>
28 #include <glib-object.h>
29 #include <gdk/gdk.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <gdk-pixbuf/gdk-pixbuf.h>
32 #include <gtk/gtk.h>
33 
34 #ifdef HAVE_MOD
35 #include <libmodplug/modplug.h>
36 #include "decoder/dec_mod.h"
37 #endif /* HAVE_MOD */
38 
39 #ifdef HAVE_WAVPACK
40 #include <wavpack/wavpack.h>
41 #include "decoder/dec_wavpack.h"
42 #endif /* HAVE_WAVPACK */
43 
44 #ifdef HAVE_CDDA
45 #include "cdda.h"
46 #endif /* HAVE_CDDA */
47 
48 #include "common.h"
49 #include "utils.h"
50 #include "utils_gui.h"
51 #include "cover.h"
52 #include "decoder/file_decoder.h"
53 #include "music_browser.h"
54 #include "store_file.h"
55 #include "gui_main.h"
56 #include "options.h"
57 #include "trashlist.h"
58 #include "i18n.h"
59 #include "metadata.h"
60 #include "metadata_api.h"
61 #include "metadata_id3v1.h"
62 #include "metadata_id3v2.h"
63 #include "file_info.h"
64 
65 
66 /* requested width of value display/edit widgets */
67 #define VAL_WIDGET_WIDTH 250
68 
69 /* dimensions of cover thumbnail */
70 #define THUMB_SIZE 48
71 
72 /* import destination codes */
73 enum {
74 	IMPORT_DEST_ARTIST,
75 	IMPORT_DEST_RECORD,
76 	IMPORT_DEST_TITLE,
77 	IMPORT_DEST_YEAR,
78 	IMPORT_DEST_NUMBER,
79 	IMPORT_DEST_COMMENT,
80 	IMPORT_DEST_RVA
81 };
82 
83 
84 extern options_t options;
85 
86 #define FI_MAXPAGES 256
87 
88 typedef struct {
89 	int tag;
90 	GtkWidget * table;
91 	int n_rows;
92 	int n_cols;
93 	GtkWidget * combo;
94 	GSList * slist; /* list of insertable frames */
95 } pageidx_t;
96 
97 typedef struct {
98 	/* arguments to show_file_info */
99 	GtkTreeModel * model;
100 	GtkTreeIter iter_track;
101 	fileinfo_model_func_t mfun;
102 	int mindepth;
103 	gboolean allow_ms_import;
104 	gboolean display_cover;
105 
106 	/* entries containing displayed data */
107 	GtkWidget * entry_name;
108 	GtkWidget * entry_path;
109 	GtkWidget * cover_image_area;
110 	GtkWidget * entry_format;
111 	GtkWidget * entry_length;
112 	GtkWidget * entry_sr;
113 	GtkWidget * entry_ch;
114 	GtkWidget * entry_bw;
115 	GtkWidget * entry_nsamples;
116 	GtkWidget * entry_mode;
117 
118 	/* work data */
119 	gboolean media_ok;
120 	char * name;
121 	char * filename;
122 	decoder_t * dec;
123 	int is_cdda;
124 	fileinfo_t fileinfo;
125 
126 	int bail_out;
127 	file_decoder_t * fdec;
128 	metadata_t * meta;
129 	trashlist_t * trash;
130 	GtkWidget * info_window;
131 	GtkWidget * event_box;
132 	GtkWidget * cover_align;
133 	GtkWidget * hbox;
134 	GtkWidget * add_tag_table;
135 	GtkWidget * button_table;
136 	GtkWidget * prev_button;
137 	GtkWidget * next_button;
138 	GtkWidget * save_button;
139 	GtkWidget * nb;
140 	GtkWidget * combo;
141 	int addable_tags; /* META_TAG_*, bitwise or-ed */
142 	gint n_pages; /* number of notebook pages */
143 	pageidx_t pageidx[FI_MAXPAGES];
144 	int selected_tag;
145 	int dirty; /* whether the dialog has unsaved changes */
146 	gboolean cover_set_from_apic;
147 
148 	/* for MOD info */
149 	GtkWidget * smp_instr_list;
150 	GtkListStore * smp_instr_list_store;
151 } fi_t;
152 
153 typedef struct {
154 	fi_t * fi;
155 	meta_frame_t * frame;
156         int dest_type; /* one of IMPORT_DEST_* */
157 } import_data_t;
158 
159 
160 
161 typedef struct {
162 	fi_t * fi;
163 	char savefile[MAXLEN];
164 	unsigned int image_size;
165 	void * image_data;
166 	GtkWidget * save_button;
167 } save_pic_t;
168 
169 typedef struct {
170 	fi_t * fi;
171 	meta_frame_t * frame;
172 	save_pic_t * save_pic;
173 } change_pic_t;
174 
175 
176 /* 'source widget' for APIC frames */
177 typedef struct {
178 	GtkWidget * descr_entry;
179 	GtkWidget * type_combo;
180 	GtkWidget * mime_label;
181 	GtkWidget * image;
182 } apic_source_t;
183 
184 
185 extern GtkWidget * main_window;
186 extern GtkTreeStore * music_store;
187 extern GtkWidget * music_tree;
188 
189 
190 #ifdef HAVE_MOD
191 void module_info_fill_page(fi_t * fi, meta_frame_t * frame, GtkWidget * vbox);
192 #endif /* HAVE_MOD */
193 
194 
195 gint fi_save(GtkWidget * widget, gpointer data);
196 void fi_procframe_ins(fi_t * fi, meta_frame_t * frame);
197 void fi_procframe_add_tag_page(fi_t * fi, meta_frame_t * frame);
198 
199 
200 fi_t *
fi_new(void)201 fi_new(void) {
202 
203 	fi_t * fi;
204 	int i;
205 
206 	if ((fi = (fi_t *)calloc(1, sizeof(fi_t))) == NULL) {
207 		fprintf(stderr, "error: fileinfo_new(): calloc error\n");
208 		return NULL;
209 	}
210 
211 	for (i = 0; i < FI_MAXPAGES; i++) {
212 		fi->pageidx[i].tag = -1;
213 	}
214 	fi->selected_tag = -1;
215 
216 	return fi;
217 }
218 
219 void
fi_unload(fi_t * fi)220 fi_unload(fi_t * fi) {
221 	fi->media_ok = FALSE;
222 	if (fi->save_button) {
223 		if (GTK_IS_WIDGET(fi->save_button)) gtk_widget_destroy(fi->save_button);
224 		fi->save_button = NULL;
225 	}
226 	if (fi->add_tag_table) {
227 		if (GTK_IS_WIDGET(fi->add_tag_table)) gtk_widget_destroy(fi->add_tag_table);
228 		fi->add_tag_table = NULL;
229 	}
230 
231 	if (fi->fdec != NULL) {
232 		file_decoder_delete(fi->fdec);
233 		fi->fdec = NULL;
234 		fi->meta = NULL;
235 	}
236 	free(fi->name);
237 	free(fi->filename);
238 
239 	trashlist_free(fi->trash);
240 	fi->trash = NULL;
241 	fi->cover_set_from_apic = FALSE;
242 }
243 
244 void
fi_delete(fi_t * fi)245 fi_delete(fi_t * fi) {
246 
247 	int i;
248 
249 	if (fi == NULL)	return;
250 
251 	fi_unload(fi);
252 
253 	for (i = 0; i < FI_MAXPAGES; i++) {
254 		g_slist_free(fi->pageidx[i].slist);
255 		fi->pageidx[i].slist = NULL;
256 	}
257 	free(fi);
258 }
259 
260 
261 void
fi_mark_changed(fi_t * fi)262 fi_mark_changed(fi_t * fi) {
263 
264 	if (fi->dirty != 0)
265 		return;
266 
267 	gtk_window_set_title(GTK_WINDOW(fi->info_window), _("*File info"));
268 	fi->dirty = 1;
269 }
270 
271 
272 void
fi_mark_unchanged(fi_t * fi)273 fi_mark_unchanged(fi_t * fi) {
274 
275 	if (fi->dirty == 0)
276 		return;
277 
278 	gtk_window_set_title(GTK_WINDOW(fi->info_window), _("File info"));
279 	fi->dirty = 0;
280 }
281 
282 
283 import_data_t *
import_data_new(void)284 import_data_new(void) {
285 
286 	import_data_t * data;
287 
288 	if ((data = (import_data_t *)calloc(1, sizeof(import_data_t))) == NULL) {
289 		fprintf(stderr, "error: import_data_new(): calloc error\n");
290 		return NULL;
291 	}
292 	return data;
293 }
294 
295 
296 int
fi_close_dialog(fi_t * fi)297 fi_close_dialog(fi_t * fi) {
298 
299 	int ret;
300 	GtkWidget * dialog = gtk_message_dialog_new(GTK_WINDOW(fi->info_window),
301 						    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
302 						    GTK_MESSAGE_WARNING,
303 						    GTK_BUTTONS_NONE,
304 						    _("There are unsaved changes to the file metadata."));
305 
306 	gtk_window_set_title(GTK_WINDOW(dialog), _("Warning"));
307 	gtk_dialog_add_buttons(GTK_DIALOG(dialog),
308 			       _("Save and close"), 1,
309 			       _("Discard changes"), 2,
310 			       _("Do not close"), 0,
311 			       NULL);
312 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0);
313 
314 	ret = aqualung_dialog_run(GTK_DIALOG(dialog));
315 	gtk_widget_destroy(dialog);
316 	return ret;
317 }
318 
319 
320 int
fi_can_close(fi_t * fi)321 fi_can_close(fi_t * fi) {
322 
323 	if (fi->dirty != 0) {
324 		switch (fi_close_dialog(fi)) {
325 		case 1: /* Save and close */
326 			if (fi_save(NULL, fi)) {
327 				fi_mark_unchanged(fi);
328 				return TRUE;
329 			} else {
330 				return FALSE;
331 			}
332 		case 2: /* Discard changes */
333 			fi_mark_unchanged(fi);
334 			return TRUE;
335 		default: /* Do not close */
336 			return FALSE;
337 		}
338 	}
339 	return TRUE;
340 }
341 
342 
343 gint
dismiss(GtkWidget * widget,gpointer data)344 dismiss(GtkWidget * widget, gpointer data) {
345 
346 	fi_t * fi = (fi_t *)data;
347 
348 	if (fi_can_close(fi) != TRUE) {
349 		return TRUE;
350 	}
351 
352 	unregister_toplevel_window(fi->info_window);
353 	gtk_widget_destroy(fi->info_window);
354 
355 	fi_delete(fi);
356 	return TRUE;
357 }
358 
359 
360 gint
info_window_close(GtkWidget * widget,GdkEventAny * event,gpointer data)361 info_window_close(GtkWidget * widget, GdkEventAny * event, gpointer data) {
362 
363 	fi_t * fi = (fi_t *)data;
364 
365 	if (fi_can_close(fi) != TRUE) {
366 		return TRUE;
367 	}
368 
369 	unregister_toplevel_window(fi->info_window);
370 	gtk_widget_destroy(fi->info_window);
371 	trashlist_free(fi->trash);
372 
373 	fi_delete(fi);
374 	return TRUE;
375 }
376 
377 
378 gint
info_window_key_pressed(GtkWidget * widget,GdkEventKey * kevent,gpointer data)379 info_window_key_pressed(GtkWidget * widget, GdkEventKey * kevent, gpointer data) {
380 
381 	fi_t * fi = (fi_t *)data;
382 	int page;
383 
384 	switch (kevent->keyval) {
385 	case GDK_Escape:
386 		dismiss(NULL, data);
387 		return TRUE;
388 		break;
389 	case GDK_Return:
390 		page = (gtk_notebook_get_current_page(GTK_NOTEBOOK(fi->nb)) + 1) % fi->n_pages;
391 		gtk_notebook_set_current_page(GTK_NOTEBOOK(fi->nb), page);
392 		break;
393 	}
394 	return FALSE;
395 }
396 
397 
398 int
fi_set_frame_from_source(fi_t * fi,meta_frame_t * frame)399 fi_set_frame_from_source(fi_t * fi, meta_frame_t * frame) {
400 
401 	char * parsefmt = meta_get_field_parsefmt(frame->type);
402 	char parsefmt_esc[MAXLEN];
403 
404 	escape_percents(parsefmt, parsefmt_esc);
405 	if (META_FIELD_TEXT(frame->type)) {
406 		if (frame->field_val != NULL) {
407 			free(frame->field_val);
408 		}
409 		if (frame->type == META_FIELD_GENRE) {
410 			frame->field_val = gtk_combo_box_get_active_text(GTK_COMBO_BOX(frame->source));
411 		} else {
412 			frame->field_val = strdup(gtk_entry_get_text(GTK_ENTRY(frame->source)));
413 		}
414 	} else if (META_FIELD_INT(frame->type)) {
415 		int val = 0;
416 		const char * str = gtk_entry_get_text(GTK_ENTRY(frame->source));
417 		if (sscanf(str, parsefmt, &val) < 1) {
418 			char msg[MAXLEN];
419 			snprintf(msg, MAXLEN-1,
420 				 _("Conversion error in field %s:\n"
421 				   "'%s' does not conform to format '%s'!"),
422 				 frame->field_name, str, "%s");
423 			message_dialog(_("Error"),
424 				       fi->info_window, GTK_MESSAGE_ERROR,
425 				       GTK_BUTTONS_OK, NULL, msg, parsefmt_esc);
426 			return -1;
427 		} else {
428 			frame->int_val = val;
429 		}
430 	} else if (META_FIELD_FLOAT(frame->type)) {
431 		float val = 0;
432 		const char * str = gtk_entry_get_text(GTK_ENTRY(frame->source));
433 		if (sscanf(str, parsefmt, &val) < 1) {
434 			char msg[MAXLEN];
435 			snprintf(msg, MAXLEN-1,
436 				 _("Conversion error in field %s:\n"
437 				   "'%s' does not conform to format '%s'!"),
438 				 frame->field_name, str, "%s");
439 			message_dialog(_("Error"),
440 				       fi->info_window, GTK_MESSAGE_ERROR,
441 				       GTK_BUTTONS_OK, NULL, msg, parsefmt_esc);
442 			return -1;
443 		} else {
444 			frame->float_val = val;
445 		}
446 	} else if (META_FIELD_BIN(frame->type)) {
447 		if (frame->type == META_FIELD_APIC) {
448 			apic_source_t * source = (apic_source_t *)frame->source;
449 			if (frame->field_val != NULL) {
450 				free(frame->field_val);
451 			}
452 			frame->field_val = strdup(gtk_entry_get_text(GTK_ENTRY(source->descr_entry)));
453 			frame->int_val = gtk_combo_box_get_active(GTK_COMBO_BOX(source->type_combo));
454 			if (frame->length == 0) {
455 				message_dialog(_("Error"),
456 					       fi->info_window, GTK_MESSAGE_ERROR,
457 					       GTK_BUTTONS_OK, NULL,
458 					       _("Attached Picture frame with no image set!\n"
459 						 "Please set an image or remove the frame."));
460 				return -1;
461 			}
462 		}
463 	}
464 	return 0;
465 }
466 
467 
468 gint
fi_save(GtkWidget * widget,gpointer data)469 fi_save(GtkWidget * widget, gpointer data) {
470 
471 	fi_t * fi = (fi_t *)data;
472 	metadata_t * meta = fi->meta;
473 	meta_frame_t * frame = meta->root;
474 	int status = 0;
475 
476 	while (frame != NULL) {
477 		if (fi_set_frame_from_source(fi, frame) < 0) {
478 			status = -1;
479 			break;
480 		}
481 		frame = frame->next;
482 	}
483 
484 	if (status != 0) {
485 		return FALSE;
486 	}
487 
488 	if (fi->fdec->meta_write != NULL) {
489 		int ret;
490 		ret = fi->fdec->meta_write(fi->fdec, fi->meta);
491 		if (ret == META_ERROR_NONE) {
492 			fi_mark_unchanged(fi);
493 			return TRUE;
494 		} else {
495 			message_dialog(_("Error"),
496 				       fi->info_window, GTK_MESSAGE_ERROR,
497 				       GTK_BUTTONS_OK, NULL,
498 				       _("Failed to write metadata to file.\n"
499 					 "Reason: %s"),
500 				       metadata_strerror(ret));
501 			return FALSE;
502 		}
503 	} else {
504 		fprintf(stderr, "programmer error: fdec->meta_write == NULL\n");
505 	}
506 
507 	return FALSE;
508 }
509 
510 
511 void
import_button_pressed(GtkWidget * widget,gpointer gptr_data)512 import_button_pressed(GtkWidget * widget, gpointer gptr_data) {
513 
514 	import_data_t * data = (import_data_t *)gptr_data;
515 	fi_t * fi = data->fi;
516 	meta_frame_t * frame = data->frame;
517 	GtkTreeModel * model = fi->model;
518 	GtkTreeIter iter_track = fi->iter_track;
519 	GtkTreeIter record_iter;
520 	GtkTreeIter artist_iter;
521 	GtkTreePath * path;
522 	char tmp[MAXLEN];
523 
524 	record_data_t * record_data;
525 	track_data_t * track_data;
526 
527 	if (fi_set_frame_from_source(fi, frame) < 0) {
528 		return;
529 	}
530 
531 	switch (data->dest_type) {
532 	case IMPORT_DEST_TITLE:
533 		gtk_tree_store_set(music_store, &iter_track, MS_COL_NAME, frame->field_val, -1);
534 		music_store_mark_changed(&iter_track);
535 		break;
536 	case IMPORT_DEST_RECORD:
537 		gtk_tree_model_iter_parent(model, &record_iter, &iter_track);
538 		gtk_tree_store_set(music_store, &record_iter, MS_COL_NAME, frame->field_val, -1);
539 		music_store_mark_changed(&record_iter);
540 		break;
541 	case IMPORT_DEST_ARTIST:
542 		gtk_tree_model_iter_parent(model, &record_iter, &iter_track);
543 		gtk_tree_model_iter_parent(model, &artist_iter, &record_iter);
544 		gtk_tree_store_set(music_store, &artist_iter, MS_COL_NAME, frame->field_val, -1);
545 		gtk_tree_store_set(music_store, &artist_iter, MS_COL_SORT, frame->field_val, -1);
546 		path = gtk_tree_model_get_path(model, &iter_track);
547 		gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(music_tree), path, NULL, TRUE, 0.5f, 0.0f);
548 		music_store_mark_changed(&artist_iter);
549 		break;
550 	case IMPORT_DEST_YEAR:
551 		gtk_tree_model_iter_parent(model, &record_iter, &iter_track);
552 		gtk_tree_model_get(GTK_TREE_MODEL(music_store), &record_iter, MS_COL_DATA, &record_data, -1);
553 		if (sscanf(frame->field_val, "%d", &record_data->year) < 1) {
554 			char msg[MAXLEN];
555 			snprintf(msg, MAXLEN-1,
556 				 _("Error converting field %s to Year:\n"
557 				   "'%s' is not an integer number!"),
558 				 frame->field_name, frame->field_val);
559 			message_dialog(_("Error"),
560 				       fi->info_window, GTK_MESSAGE_ERROR,
561 				       GTK_BUTTONS_OK, NULL, msg);
562 			return;
563 		} else {
564 			music_store_mark_changed(&record_iter);
565 		}
566 		break;
567 	case IMPORT_DEST_NUMBER:
568 		snprintf(tmp, MAXLEN-1, "%02d", frame->int_val);
569 		gtk_tree_store_set(music_store, &iter_track, MS_COL_SORT, tmp, -1);
570 		music_store_mark_changed(&iter_track);
571 		break;
572 	case IMPORT_DEST_COMMENT:
573 		gtk_tree_model_get(model, &iter_track, MS_COL_DATA, &track_data, -1);
574 		tmp[0] = '\0';
575 		if (track_data->comment != NULL) {
576 			strncat(tmp, track_data->comment, MAXLEN-1);
577 		}
578 		if ((tmp[strlen(tmp)-1] != '\n') && (tmp[0] != '\0')) {
579 			strncat(tmp, "\n", MAXLEN-1);
580 		}
581 		strncat(tmp, frame->field_val, MAXLEN-1);
582 		free_strdup(&track_data->comment, tmp);
583 		music_store_mark_changed(&iter_track);
584 		break;
585 	case IMPORT_DEST_RVA:
586 		gtk_tree_model_get(model, &iter_track, MS_COL_DATA, &track_data, -1);
587 		track_data->rva = frame->float_val;
588 		track_data->use_rva = 1;
589 		music_store_mark_changed(&iter_track);
590 		break;
591 	}
592 }
593 
594 int
lookup_page(fi_t * fi,int tag)595 lookup_page(fi_t * fi, int tag) {
596 
597 	int i;
598 	for (i = 0; i < FI_MAXPAGES; i++) {
599 		if (fi->pageidx[i].tag == tag) {
600 			return i;
601 		}
602 	}
603 	return -1;
604 }
605 
606 void
fi_set_page(fi_t * fi)607 fi_set_page(fi_t * fi) {
608 	if (fi->selected_tag > -1) { /* try to select first page with the same tag */
609 		int idx = lookup_page(fi, fi->selected_tag);
610 		if (idx > -1) {
611 			gtk_notebook_set_current_page(GTK_NOTEBOOK(fi->nb), idx);
612 			return;
613 		}
614 	}
615 	/* fallback to default behaviour */
616 	fi->n_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(fi->nb));
617 	if (fi->n_pages > 1) {
618 		gtk_notebook_set_current_page(GTK_NOTEBOOK(fi->nb), options.tags_tab_first ? 1 : 0);
619 	}
620 }
621 
622 int
fi_tabwidth(fi_t * fi,metadata_t * meta)623 fi_tabwidth(fi_t * fi, metadata_t * meta) {
624 
625 	/* tabwidth is 2, +1 if called from Music Store, +1 if editable */
626 	int tabwidth = 2;
627 	tabwidth += fi->allow_ms_import? 1 : 0;
628 	tabwidth += meta->writable ? 1 : 0;
629 	return tabwidth;
630 }
631 
632 
633 void
save_pic_button_pressed(GtkWidget * widget,gpointer data)634 save_pic_button_pressed(GtkWidget * widget, gpointer data) {
635 
636 	save_pic_t * save_pic = (save_pic_t *)data;
637 	fi_t * fi = save_pic->fi;
638 
639 	GSList * lfiles = NULL;
640 	char filename[MAXLEN];
641 	char * dirname;
642 
643 	dirname = g_path_get_dirname(options.currdir);
644 	snprintf(filename, MAXLEN-1, "%s/%s", dirname, save_pic->savefile);
645 	g_free(dirname);
646 
647 	lfiles = file_chooser(_("Please specify the file to save the image to."),
648 			      fi->info_window,
649 			      GTK_FILE_CHOOSER_ACTION_SAVE,
650 			      FILE_CHOOSER_FILTER_NONE,
651 			      FALSE,
652 			      filename);
653 
654 	if (lfiles != NULL) {
655 
656 		FILE * f = fopen(filename, "wb");
657 
658 		g_slist_free(lfiles);
659 
660 		if (f == NULL) {
661 			fprintf(stderr, "error: fopen() failed\n");
662 			return;
663 		}
664 		if (fwrite(save_pic->image_data, 1, save_pic->image_size, f) != save_pic->image_size) {
665 			fprintf(stderr, "fwrite() error\n");
666 			return;
667 		}
668 		fclose(f);
669 
670 		strncpy(options.currdir, filename, MAXLEN-1);
671 	}
672 }
673 
674 
675 void
save_pic_update(save_pic_t * save_pic,fi_t * fi,meta_frame_t * frame)676 save_pic_update(save_pic_t * save_pic, fi_t * fi, meta_frame_t * frame) {
677 
678 	int i, r;
679 	char mtype[20];
680 	char savefilename[256];
681 
682 	mtype[0] = '\0';
683 	strcpy(savefilename, "picture.");
684 	r = sscanf(frame->field_name, "image/%s", mtype);
685 	if (r == 0) {
686 		strncpy(mtype, frame->field_name, 19);
687 	}
688 	for (i = 0; mtype[i] != '\0'; i++) {
689 		mtype[i] = tolower(mtype[i]);
690 	}
691 	if (mtype[0] == '\0') {
692 		strcpy(mtype, "dat");
693 	}
694 	strncat(savefilename, mtype, 255);
695 
696 	save_pic->fi = fi;
697 	strncpy(save_pic->savefile, savefilename, MAXLEN-1);
698 	save_pic->image_size = frame->length;
699 	save_pic->image_data = frame->data;
700 
701 	if (save_pic->image_size > 0) {
702 		gtk_widget_set_sensitive(save_pic->save_button, TRUE);
703 	} else {
704 		gtk_widget_set_sensitive(save_pic->save_button, FALSE);
705 	}
706 }
707 
708 
709 GtkWidget *
make_image_from_binary(meta_frame_t * frame)710 make_image_from_binary(meta_frame_t * frame) {
711 
712 	GdkPixbuf * pixbuf;
713 	GdkPixbuf * pixbuf_scaled;
714 	GtkWidget * image;
715 	int width, height;
716 	int new_width, new_height;
717 
718 	void * data = frame->data;
719 	int length = frame->length;
720 
721 	GdkPixbufLoader * loader;
722 
723 	if (length == 0) {
724 		return gtk_label_new(_("(no image)"));
725 	}
726 
727 	if (strcmp(frame->field_name, "-->") == 0) {
728 		/* display URL */
729 		char * str = meta_id3v2_to_utf8(0x0/*ascii*/, data, length);
730 		GtkWidget * label = gtk_label_new(str);
731 		g_free(str);
732 		return label;
733 	}
734 
735 	loader = gdk_pixbuf_loader_new();
736 	if (gdk_pixbuf_loader_write(loader, frame->data, frame->length, NULL) != TRUE) {
737 		fprintf(stderr, "make_image_from_binary: failed to load image #1\n");
738 		g_object_unref(loader);
739 		return gtk_label_new(_("(error loading image)"));
740 	}
741 
742 	if (gdk_pixbuf_loader_close(loader, NULL) != TRUE) {
743 		fprintf(stderr, "make_image_from_binary: failed to load image #2\n");
744 		g_object_unref(loader);
745 		return gtk_label_new(_("(error loading image)"));
746 	}
747 
748 	pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
749 	width = gdk_pixbuf_get_width(pixbuf);
750 	height = gdk_pixbuf_get_height(pixbuf);
751 	new_width = (width > VAL_WIDGET_WIDTH) ? VAL_WIDGET_WIDTH : width;
752 	new_height = ((double)new_width / (double)width) * height;
753 	pixbuf_scaled = gdk_pixbuf_scale_simple(pixbuf, new_width, new_height, GDK_INTERP_BILINEAR);
754 	image = gtk_image_new_from_pixbuf(pixbuf_scaled);
755 	g_object_unref(pixbuf_scaled);
756 	g_object_unref(loader);
757 	return image;
758 }
759 
760 
761 void
change_pic_button_pressed(GtkWidget * widget,gpointer data)762 change_pic_button_pressed(GtkWidget * widget, gpointer data) {
763 
764 	change_pic_t * change_pic = (change_pic_t *)data;
765 	save_pic_t * save_pic = change_pic->save_pic;
766 	fi_t * fi = change_pic->fi;
767 	meta_frame_t * frame = change_pic->frame;
768 	apic_source_t * source = ((apic_source_t *)(frame->source));
769 	GSList * lfiles = NULL;
770 
771 	char str[MAXLEN];
772 	GdkPixbufFormat * pformat;
773 	gchar ** mime_types;
774 
775 	GtkWidget * vbox; /* parent of image */
776 
777 	lfiles = file_chooser(_("Please specify the file to load the image from."),
778 			      fi->info_window,
779 			      GTK_FILE_CHOOSER_ACTION_OPEN,
780 			      FILE_CHOOSER_FILTER_NONE,
781 			      FALSE,
782 			      options.currdir);
783 
784 	if (lfiles != NULL) {
785 		g_slist_free(lfiles);
786 	} else {
787 		return;
788 	}
789 
790 	pformat = gdk_pixbuf_get_file_info(options.currdir, NULL, NULL);
791 	if (pformat == NULL) {
792 		char msg[MAXLEN];
793 		snprintf(msg, MAXLEN-1, _("Could not load image from:\n%s"), options.currdir);
794 		message_dialog(_("Error"), fi->info_window, GTK_MESSAGE_ERROR,
795 			       GTK_BUTTONS_OK, NULL, msg);
796 		return;
797 	}
798 
799 	if (frame->data != NULL) {
800 		free(frame->data);
801 	}
802 	if (!g_file_get_contents(options.currdir, ((gchar **)(&frame->data)),
803 				 ((gsize *)(&frame->length)), NULL)) {
804 		fprintf(stderr, "g_file_get_contents failed on %s\n", options.currdir);
805 		return;
806 	}
807 
808 	mime_types = gdk_pixbuf_format_get_mime_types(pformat);
809 	if (mime_types[0] != NULL) {
810 		if (frame->field_name != NULL) {
811 			free(frame->field_name);
812 		}
813 		frame->field_name = strdup(mime_types[0]);
814 		g_strfreev(mime_types);
815 	} else {
816 		fprintf(stderr, "error: no mime type for image %s\n", options.currdir);
817 		g_strfreev(mime_types);
818 		return;
819 	}
820 
821 	vbox = gtk_widget_get_parent(source->image);
822 	gtk_widget_destroy(source->image);
823 	source->image = make_image_from_binary(frame);
824 	gtk_widget_show(source->image);
825 	gtk_container_add(GTK_CONTAINER(vbox), source->image);
826 	snprintf(str, MAXLEN-1, _("MIME type: %s"), frame->field_name);
827 	gtk_label_set_text(GTK_LABEL(source->mime_label), str);
828 	/* update callback data for 'Save picture' */
829 	save_pic_update(save_pic, fi, frame);
830 
831 	fi_mark_changed(fi);
832 }
833 
834 
835 GtkWidget *
fi_procframe_label_apic(fi_t * fi,meta_frame_t * frame)836 fi_procframe_label_apic(fi_t * fi, meta_frame_t * frame) {
837 
838 	metadata_t * meta = fi->meta;
839 	apic_source_t * source;
840 
841 	char * pic_caption;
842 	char str[MAXLEN];
843 	GtkWidget * label_frame;
844 	GtkWidget * vbox = gtk_vbox_new(FALSE, 0);
845 	GtkWidget * hbox;
846 	GtkWidget * label;
847 	GtkWidget * button;
848 
849 	change_pic_t * change_pic;
850 
851 	save_pic_t * save_pic = (save_pic_t *)malloc(sizeof(save_pic_t));
852 	if (save_pic == NULL)
853 		return NULL;
854 
855 	trashlist_add(fi->trash, save_pic);
856 
857 	change_pic = (change_pic_t *)malloc(sizeof(change_pic_t));
858 	if (change_pic == NULL)
859 		return NULL;
860 
861 	trashlist_add(fi->trash, change_pic);
862 	change_pic->fi = fi;
863 	change_pic->frame = frame;
864 	change_pic->save_pic = save_pic;
865 
866 	frame->source = calloc(1, sizeof(apic_source_t));
867 	if (frame->source == NULL)
868 		return NULL;
869 	trashlist_add(fi->trash, frame->source);
870 	source = ((apic_source_t *)(frame->source));
871 
872 	meta_get_fieldname(META_FIELD_APIC, &pic_caption);
873 	snprintf(str, MAXLEN-1, "%s:", pic_caption);
874 
875 	label_frame = gtk_frame_new(pic_caption);
876 	gtk_container_add(GTK_CONTAINER(label_frame), vbox);
877 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
878 
879 
880 	hbox = gtk_hbox_new(FALSE, 0);
881 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
882 	snprintf(str, MAXLEN-1, _("MIME type: %s"), frame->field_name);
883 	source->mime_label = gtk_label_new(str);
884 	gtk_box_pack_start(GTK_BOX(hbox), source->mime_label, FALSE, FALSE, 0);
885 
886 	hbox = gtk_hbox_new(FALSE, 0);
887 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
888 	label = gtk_label_new(_("Picture type:"));
889 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
890 
891 	if (meta->writable) {
892 		int i = 0;
893 		char * type_str;
894 		hbox = gtk_hbox_new(FALSE, 0);
895 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
896 		source->type_combo = gtk_combo_box_new_text();
897 		while (1) {
898 			type_str = meta_id3v2_apic_type_to_string(i);
899 			if (type_str == NULL) {
900 				break;
901 			}
902 			gtk_combo_box_append_text(GTK_COMBO_BOX(source->type_combo), type_str);
903 			++i;
904 		}
905 		gtk_combo_box_set_active(GTK_COMBO_BOX(source->type_combo), frame->int_val);
906 		gtk_box_pack_start(GTK_BOX(hbox), source->type_combo, TRUE, TRUE, 0);
907 	} else {
908 		hbox = gtk_hbox_new(FALSE, 0);
909 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
910 		snprintf(str, MAXLEN-1, "%s",
911 			 meta_id3v2_apic_type_to_string(frame->int_val));
912 		label = gtk_label_new(str);
913 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
914 	}
915 
916 	hbox = gtk_hbox_new(FALSE, 0);
917 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
918 	label = gtk_label_new(_("Description:"));
919 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
920 
921 	if (meta->writable) {
922 		hbox = gtk_hbox_new(FALSE, 0);
923 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
924 		source->descr_entry = gtk_entry_new();
925 		gtk_entry_set_text(GTK_ENTRY(source->descr_entry), frame->field_val);
926 		gtk_box_pack_start(GTK_BOX(hbox), source->descr_entry, TRUE, TRUE, 0);
927 	} else {
928 		hbox = gtk_hbox_new(FALSE, 0);
929 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
930 		label = gtk_label_new((frame->field_val[0] == '\0') ?
931 				      _("(no description)") : frame->field_val);
932 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
933 	}
934 
935 
936 	hbox = gtk_hbox_new(TRUE, 0);
937 	gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
938 
939 	button = gui_stock_label_button(_("Change"), GTK_STOCK_OPEN);
940 	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 3);
941 	if (meta->writable) {
942 		g_signal_connect(G_OBJECT(button), "clicked",
943 				 G_CALLBACK(change_pic_button_pressed),
944 				 (gpointer)change_pic);
945 	} else {
946 		gtk_widget_set_sensitive(button, FALSE);
947 	}
948 
949 	save_pic->save_button = gui_stock_label_button(_("Save"), GTK_STOCK_SAVE);
950 	gtk_box_pack_start(GTK_BOX(hbox), save_pic->save_button, TRUE, TRUE, 3);
951 	g_signal_connect(G_OBJECT(save_pic->save_button), "clicked",
952 			 G_CALLBACK(save_pic_button_pressed),
953 			 (gpointer)save_pic);
954 
955 	save_pic_update(save_pic, fi, frame);
956 
957 	return label_frame;
958 }
959 
960 
961 GtkWidget *
fi_procframe_label(fi_t * fi,meta_frame_t * frame)962 fi_procframe_label(fi_t * fi, meta_frame_t * frame) {
963 
964 	if (frame->type == META_FIELD_APIC) {
965 		return fi_procframe_label_apic(fi, frame);
966 	} else {
967 		char str[MAXLEN];
968 		GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
969 		GtkWidget * label;
970 		snprintf(str, MAXLEN-1, "%s:", frame->field_name);
971 		label = gtk_label_new(str);
972 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
973 		return hbox;
974 	}
975 }
976 
977 
978 void
fi_entry_changed_cb(GtkEntry * entry,gpointer data)979 fi_entry_changed_cb(GtkEntry * entry, gpointer data) {
980 
981 	fi_t * fi = (fi_t *)data;
982 	fi_mark_changed(fi);
983 }
984 
985 
986 gint
genre_cmp(gconstpointer a,gconstpointer b)987 genre_cmp(gconstpointer a, gconstpointer b) {
988 
989 	return strcmp(id3v1_genre_str_from_code(GPOINTER_TO_INT(a)),
990 		      id3v1_genre_str_from_code(GPOINTER_TO_INT(b)));
991 }
992 
993 
994 void
make_genre_combo(meta_frame_t * frame,GtkWidget ** widget,GtkWidget ** entry)995 make_genre_combo(meta_frame_t * frame, GtkWidget ** widget, GtkWidget ** entry) {
996 
997 	int i;
998 	GtkWidget * combo = gtk_combo_box_entry_new_text();
999 	GSList * list = NULL;
1000 	GSList * _list;
1001 
1002 	for (i = 0; i < 256; i++) {
1003 		char * genre = id3v1_genre_str_from_code(i);
1004 		if (genre == NULL) {
1005 			break;
1006 		}
1007 		list = g_slist_append(list, GINT_TO_POINTER(i));
1008 	}
1009 
1010 	list = g_slist_sort(list, genre_cmp);
1011 	_list = list;
1012 	while (_list != NULL) {
1013 		char * genre = id3v1_genre_str_from_code(GPOINTER_TO_INT(_list->data));
1014 		gtk_combo_box_append_text(GTK_COMBO_BOX(combo), genre);
1015 		_list = g_slist_next(_list);
1016 	}
1017 	g_slist_free(list);
1018 
1019 	gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(combo), 4);
1020 
1021 	*widget = combo;
1022 	*entry = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(combo)));
1023 
1024 	if (frame->field_val[0] == '\0') { /* set default genre if none present */
1025 		gtk_entry_set_text(GTK_ENTRY(*entry), id3v1_genre_str_from_code(0));
1026 	} else {
1027 		gtk_entry_set_text(GTK_ENTRY(*entry), frame->field_val);
1028 	}
1029 
1030 	/* for ID3v1, only predefined genres can be selected, no editing allowed */
1031 	if (frame->tag == META_TAG_ID3v1) {
1032 		gtk_widget_set_can_focus(*entry, FALSE);
1033 		gtk_editable_set_editable(GTK_EDITABLE(*entry), FALSE);
1034 	}
1035 }
1036 
1037 
1038 void
make_apic_widget(meta_frame_t * frame,GtkWidget ** widget,GtkWidget ** entry)1039 make_apic_widget(meta_frame_t * frame, GtkWidget ** widget, GtkWidget ** entry) {
1040 
1041 	GtkWidget * apic_frame = gtk_frame_new(NULL);
1042 	GtkWidget * image = make_image_from_binary(frame);
1043 	GtkWidget * vbox = gtk_vbox_new(FALSE, 0);
1044 
1045 	gtk_frame_set_shadow_type(GTK_FRAME(apic_frame), GTK_SHADOW_NONE);
1046 	gtk_container_add(GTK_CONTAINER(apic_frame), vbox);
1047 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1048 	gtk_box_pack_start(GTK_BOX(vbox), image, TRUE, TRUE, 0);
1049 
1050 	*widget = apic_frame;
1051 	*entry = apic_frame;
1052 	((apic_source_t *)(frame->source))->image = image;
1053 }
1054 
1055 
1056 GtkWidget *
fi_procframe_entry(fi_t * fi,meta_frame_t * frame)1057 fi_procframe_entry(fi_t * fi, meta_frame_t * frame) {
1058 
1059 	metadata_t * meta = fi->meta;
1060 	GtkWidget * widget = NULL;
1061 	GtkWidget * entry = NULL;
1062 	if (META_FIELD_BIN(frame->type)) {
1063 		/* TODO create image/whatever widget, etc. */
1064 		if (frame->type == META_FIELD_APIC) {
1065 			make_apic_widget(frame, &widget, &entry);
1066 		}
1067 	} else if (META_FIELD_INT(frame->type)) {
1068 		char str[MAXLEN];
1069 		char * format = meta_get_field_renderfmt(frame->type);
1070 		widget = entry = gtk_entry_new();
1071 		snprintf(str, MAXLEN-1, format, frame->int_val);
1072 		gtk_entry_set_text(GTK_ENTRY(entry), str);
1073 	} else if (META_FIELD_FLOAT(frame->type)) {
1074 		char str[MAXLEN];
1075 		char * format = meta_get_field_renderfmt(frame->type);
1076 		widget = entry = gtk_entry_new();
1077 		snprintf(str, MAXLEN-1, format, frame->float_val);
1078 		gtk_entry_set_text(GTK_ENTRY(entry), str);
1079 	} else {
1080 		if (meta->writable && (frame->type == META_FIELD_GENRE)) {
1081 			make_genre_combo(frame, &widget, &entry);
1082 		} else {
1083 			widget = entry = gtk_entry_new();
1084 			gtk_entry_set_text(GTK_ENTRY(entry), frame->field_val);
1085 		}
1086 	}
1087 
1088 	if ((META_FIELD_TEXT(frame->type)) ||
1089 	    (META_FIELD_INT(frame->type)) ||
1090 	    (META_FIELD_FLOAT(frame->type))) {
1091 		if (!meta->writable) {
1092 			gtk_widget_set_can_focus(entry, FALSE);
1093 			gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1094 		} else {
1095 			g_signal_connect(G_OBJECT(widget), "changed",
1096 					 G_CALLBACK(fi_entry_changed_cb), (gpointer)fi);
1097 		}
1098 		gtk_widget_set_size_request(widget, VAL_WIDGET_WIDTH, -1);
1099 	}
1100 	return widget;
1101 }
1102 
1103 
1104 import_data_t *
make_import_data_from_frame(fi_t * fi,meta_frame_t * frame,char * label)1105 make_import_data_from_frame(fi_t * fi, meta_frame_t * frame, char * label) {
1106 
1107 	import_data_t * data = import_data_new();
1108 	trashlist_add(fi->trash, data);
1109 	data->fi = fi;
1110 	data->frame = frame;
1111 
1112 	switch (frame->type) {
1113 	case META_FIELD_TITLE:
1114 		data->dest_type = IMPORT_DEST_TITLE;
1115 		strcpy(label, _("Import as Title"));
1116 		break;
1117 	case META_FIELD_ARTIST:
1118 		data->dest_type = IMPORT_DEST_ARTIST;
1119 		strcpy(label, _("Import as Artist"));
1120 		break;
1121 	case META_FIELD_ALBUM:
1122 		data->dest_type = IMPORT_DEST_RECORD;
1123 		strcpy(label, _("Import as Record"));
1124 		break;
1125 	case META_FIELD_DATE:
1126 		data->dest_type = IMPORT_DEST_YEAR;
1127 		strcpy(label, _("Import as Year"));
1128 		break;
1129 	case META_FIELD_TRACKNO:
1130 		data->dest_type = IMPORT_DEST_NUMBER;
1131 		strcpy(label, _("Import as Track No."));
1132 		break;
1133 	case META_FIELD_RG_TRACK_GAIN:
1134 	case META_FIELD_RG_ALBUM_GAIN:
1135 	case META_FIELD_RVA2:
1136 		data->dest_type = IMPORT_DEST_RVA;
1137 		strcpy(label, _("Import as RVA"));
1138 		break;
1139 	default:
1140 		data->dest_type = IMPORT_DEST_COMMENT;
1141 		strcpy(label, _("Add to Comments"));
1142 		break;
1143 	}
1144 
1145 	return data;
1146 }
1147 
1148 
1149 GtkWidget *
fi_procframe_importbtn(fi_t * fi,meta_frame_t * frame)1150 fi_procframe_importbtn(fi_t * fi, meta_frame_t * frame) {
1151 
1152 	char label[MAXLEN];
1153 	GtkWidget * button = gtk_button_new();
1154 	g_signal_connect(G_OBJECT(button), "clicked",
1155 			 G_CALLBACK(import_button_pressed),
1156 			 (gpointer)make_import_data_from_frame(fi, frame, label));
1157 	gtk_button_set_label(GTK_BUTTON(button), label);
1158 	return button;
1159 }
1160 
1161 
1162 void
fi_fill_tagcombo(GtkComboBox * combo,int addable_tags)1163 fi_fill_tagcombo(GtkComboBox * combo, int addable_tags) {
1164 
1165 	int i;
1166 	int tag = 1;
1167 
1168 	if (!GTK_IS_COMBO_BOX(combo))
1169 		return;
1170 
1171 	for (i = 0; i < 100; i++) {
1172 		gtk_combo_box_remove_text(combo, 0);
1173 	}
1174 
1175 	if (addable_tags == 0) {
1176 		gtk_widget_set_sensitive(GTK_WIDGET(combo), FALSE);
1177 		return;
1178 	}
1179 
1180 	gtk_widget_set_sensitive(GTK_WIDGET(combo), TRUE);
1181 
1182 	while (tag <= META_TAG_MAX) {
1183 		if (addable_tags & tag) {
1184 			gtk_combo_box_append_text(combo, meta_get_tagname(tag));
1185 		}
1186 		tag <<= 1;
1187 	}
1188 
1189 	gtk_combo_box_set_active(combo, 0);
1190 }
1191 
1192 gint
fi_fill_combo_cmp(gconstpointer a,gconstpointer b)1193 fi_fill_combo_cmp(gconstpointer a, gconstpointer b) {
1194 
1195 	int field1 = GPOINTER_TO_INT(a);
1196 	int field2 = GPOINTER_TO_INT(b);
1197 	char * str1;
1198 	char * str2;
1199 
1200 	if (!meta_get_fieldname(field1, &str1)) {
1201 		fprintf(stderr, "fi_fill_combo_cmp: programmer error #1\n");
1202 	}
1203 	if (!meta_get_fieldname(field2, &str2)) {
1204 		fprintf(stderr, "fi_fill_combo_cmp: programmer error #2\n");
1205 	}
1206 
1207 	return strcmp(str1, str2);
1208 }
1209 
1210 
1211 void
fi_fill_combo_foreach(gpointer data,gpointer user_data)1212 fi_fill_combo_foreach(gpointer data, gpointer user_data) {
1213 
1214 	int field = GPOINTER_TO_INT(data);
1215 	char * str;
1216 	GtkComboBox * combo = (GtkComboBox *)user_data;
1217 	if (!meta_get_fieldname(field, &str)) {
1218 		fprintf(stderr, "fi_fill_combo_foreach: programmer error\n");
1219 	}
1220 	gtk_combo_box_append_text(combo, str);
1221 }
1222 
1223 void
fi_fill_combo(GtkComboBox * combo,GSList * slist)1224 fi_fill_combo(GtkComboBox * combo, GSList * slist) {
1225 
1226 	int i;
1227 	GSList * sorted_list = g_slist_copy(slist);
1228 	for (i = 0; i < 100; i++) {
1229 		gtk_combo_box_remove_text(combo, 0);
1230 	}
1231 	sorted_list = g_slist_sort(sorted_list, fi_fill_combo_cmp);
1232 	g_slist_foreach(sorted_list, fi_fill_combo_foreach, combo);
1233 	g_slist_free(sorted_list);
1234 	gtk_combo_box_set_active(combo, 0);
1235 }
1236 
1237 typedef struct {
1238 	fi_t * fi;
1239 	meta_frame_t * frame;
1240 	GtkWidget * label;
1241 	GtkWidget * entry;
1242 	GtkWidget * importbtn;
1243 	GtkWidget * delbtn;
1244 } fi_del_t;
1245 
1246 void
fi_del_button_pressed(GtkWidget * widget,gpointer data)1247 fi_del_button_pressed(GtkWidget * widget, gpointer data) {
1248 
1249 	fi_del_t * fi_del = (fi_del_t *)data;
1250 	fi_t * fi = fi_del->fi;
1251 	metadata_t * meta = fi_del->fi->meta;
1252 	meta_frame_t * frame = fi_del->frame;
1253 
1254 	if (frame->flags & META_FIELD_UNIQUE) {
1255 		int page = lookup_page(fi, frame->tag);
1256 		GtkWidget * combo = fi->pageidx[page].combo;
1257 		GSList * slist = fi->pageidx[page].slist;
1258 		slist = g_slist_append(slist, GINT_TO_POINTER(frame->type));
1259 		fi->pageidx[page].slist = slist;
1260 		fi_fill_combo(GTK_COMBO_BOX(combo), slist);
1261 	}
1262 
1263 	metadata_remove_frame(meta, frame);
1264 	meta_frame_free(frame);
1265 
1266 	gtk_widget_destroy(fi_del->label);
1267 	gtk_widget_destroy(fi_del->entry);
1268 	if (fi_del->importbtn != NULL) {
1269 		gtk_widget_destroy(fi_del->importbtn);
1270 	}
1271 	gtk_widget_destroy(fi_del->delbtn);
1272 	fi_mark_changed(fi);
1273 }
1274 
1275 GtkWidget *
fi_procframe_delbtn(fi_t * fi,meta_frame_t * frame,GtkWidget * label,GtkWidget * entry,GtkWidget * importbtn)1276 fi_procframe_delbtn(fi_t * fi, meta_frame_t * frame,
1277 		    GtkWidget * label, GtkWidget * entry, GtkWidget * importbtn) {
1278 
1279 	fi_del_t * fi_del;
1280 	GtkWidget * button = gtk_button_new();
1281 	gtk_button_set_image(GTK_BUTTON(button),
1282 			     gtk_image_new_from_stock(GTK_STOCK_DELETE,
1283 						      GTK_ICON_SIZE_MENU));
1284 
1285 	if (frame->flags & META_FIELD_MANDATORY) {
1286 		gtk_widget_set_sensitive(button, FALSE);
1287 		return button;
1288 	}
1289 
1290 	fi_del = calloc(1, sizeof(fi_del_t));
1291 	fi_del->fi = fi;
1292 	fi_del->frame = frame;
1293 	fi_del->label = label;
1294 	fi_del->entry = entry;
1295 	fi_del->importbtn = importbtn;
1296 	fi_del->delbtn = button;
1297 	trashlist_add(fi->trash, fi_del);
1298 	g_signal_connect(G_OBJECT(button), "clicked",
1299 			 G_CALLBACK(fi_del_button_pressed),
1300 			 (gpointer)fi_del);
1301 	return button;
1302 }
1303 
1304 
1305 typedef struct {
1306 	fi_t * fi;
1307 	int tag;
1308 } fi_add_t;
1309 
1310 
1311 void
fi_add_button_pressed(GtkWidget * widget,gpointer data)1312 fi_add_button_pressed(GtkWidget * widget, gpointer data) {
1313 
1314 	fi_add_t * fi_add = (fi_add_t *)data;
1315 	fi_t * fi = fi_add->fi;
1316 	metadata_t * meta = fi_add->fi->meta;
1317 	int page = lookup_page(fi, fi_add->tag);
1318 	GtkWidget * combo = fi->pageidx[page].combo;
1319 	GSList * slist = fi->pageidx[page].slist;
1320 
1321 	meta_frame_t * frame = meta_frame_new();
1322 	int type;
1323 	char * str;
1324 
1325 	/* Create new frame */
1326 	char * combo_entry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1327 	if (combo_entry == NULL) {
1328 		return;
1329 	}
1330 
1331 	type = meta_frame_type_from_name(combo_entry);
1332 	g_free(combo_entry);
1333 
1334 	frame->tag = fi_add->tag;
1335 	frame->type = type;
1336 	frame->flags = meta_get_default_flags(fi_add->tag, type);
1337 	if (meta_get_fieldname(type, &str)) {
1338 		frame->field_name = strdup(str);
1339 	} else {
1340 		fprintf(stderr, "fi_add_button_pressed: programmer error\n");
1341 	}
1342 	if (options.metaedit_auto_clone) {
1343 		metadata_clone_frame(meta, frame);
1344 	}
1345 	if (frame->field_val == NULL) {
1346 		frame->field_val = strdup("");
1347 	}
1348 	frame->next = NULL;
1349 
1350 	metadata_add_frame(meta, frame);
1351 
1352 	if (frame->flags & META_FIELD_UNIQUE) {
1353 		slist = g_slist_remove(slist, GINT_TO_POINTER(frame->type));
1354 		fi->pageidx[page].slist = slist;
1355 		fi_fill_combo(GTK_COMBO_BOX(combo), slist);
1356 	}
1357 
1358 	fi_procframe_ins(fi, frame);
1359 	fi_mark_changed(fi);
1360 }
1361 
1362 GtkWidget *
fi_procframe_addbtn(fi_t * fi,int tag)1363 fi_procframe_addbtn(fi_t * fi, int tag) {
1364 
1365 	fi_add_t * fi_add;
1366 	GtkWidget * button = gui_stock_label_button(_("Add"), GTK_STOCK_ADD);
1367 
1368 	fi_add = calloc(1, sizeof(fi_add_t));
1369 	fi_add->fi = fi;
1370 	fi_add->tag = tag;
1371 	trashlist_add(fi->trash, fi_add);
1372 	g_signal_connect(G_OBJECT(button), "clicked",
1373 			 G_CALLBACK(fi_add_button_pressed),
1374 			 (gpointer)fi_add);
1375 	return button;
1376 }
1377 
1378 void
fi_addtag_button_pressed(GtkWidget * widget,gpointer data)1379 fi_addtag_button_pressed(GtkWidget * widget, gpointer data) {
1380 
1381 	fi_t * fi = (fi_t *)data;
1382 	metadata_t * meta = fi->meta;
1383 	meta_frame_t * frame;
1384 	int tag;
1385 	char * combo_entry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(fi->combo));
1386 	if (combo_entry == NULL) {
1387 		return;
1388 	}
1389 	frame = meta_frame_new();
1390 	tag = frame->tag = meta_tag_from_name(combo_entry);
1391 	g_free(combo_entry);
1392 
1393 	metadata_add_mandatory_frames(meta, frame->tag);
1394 
1395 	fi_procframe_add_tag_page(fi, frame);
1396 	fi->addable_tags &= ~frame->tag;
1397 	fi_fill_tagcombo(GTK_COMBO_BOX(fi->combo), fi->addable_tags);
1398 	meta_frame_free(frame);
1399 
1400 	/* display fields, if any */
1401 	frame = metadata_get_frame_by_tag(meta, tag, NULL);
1402 	while (frame != NULL) {
1403 		fi_procframe_ins(fi, frame);
1404 		frame = metadata_get_frame_by_tag(meta, tag, frame);
1405 	}
1406 	fi_mark_changed(fi);
1407 }
1408 
1409 GtkWidget *
fi_procframe_addtagbtn(fi_t * fi)1410 fi_procframe_addtagbtn(fi_t * fi) {
1411 
1412 	GtkWidget * button = gui_stock_label_button(_("Add"), GTK_STOCK_ADD);
1413 	g_signal_connect(G_OBJECT(button), "clicked",
1414 			 G_CALLBACK(fi_addtag_button_pressed),
1415 			 (gpointer)fi);
1416 	return button;
1417 }
1418 
1419 
1420 #ifdef HAVE_MOD
1421 void
fi_procframe_ins_modinfo(fi_t * fi,meta_frame_t * frame)1422 fi_procframe_ins_modinfo(fi_t * fi, meta_frame_t * frame) {
1423 
1424 	int page = lookup_page(fi, frame->tag);
1425 	GtkWidget * table = fi->pageidx[page].table;
1426 	GtkWidget * vbox = gtk_vbox_new(FALSE, 0);
1427 
1428 	module_info_fill_page(fi, frame, vbox);
1429 	gtk_table_attach(GTK_TABLE(table), vbox, 0, 1, 0, 1,
1430 			 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1431 	gtk_widget_show_all(fi->nb);
1432 }
1433 #endif /* HAVE_MOD */
1434 
1435 
1436 void
fi_procframe_ins(fi_t * fi,meta_frame_t * frame)1437 fi_procframe_ins(fi_t * fi, meta_frame_t * frame) {
1438 
1439 	metadata_t * meta = fi->meta;
1440 	int page = lookup_page(fi, frame->tag);
1441 	GtkWidget * table = fi->pageidx[page].table;
1442 	int n_rows = fi->pageidx[page].n_rows;
1443 	int n_cols = fi->pageidx[page].n_cols;
1444 	int col = 0;
1445 
1446 	GtkWidget * label = NULL;
1447 	GtkWidget * entry = NULL;
1448 	GtkWidget * importbtn = NULL;
1449 	GtkWidget * delbtn = NULL;
1450 
1451 	if (frame->type == META_FIELD_HIDDEN) {
1452 		return;
1453 	}
1454 
1455 #ifdef HAVE_MOD
1456 	if (frame->type == META_FIELD_MODINFO) {
1457 		fi_procframe_ins_modinfo(fi, frame);
1458 		return;
1459 	}
1460 #endif /* HAVE_MOD */
1461 
1462 	if (frame->type == META_FIELD_APIC) {
1463 		/* only display first APIC found */
1464 		meta_frame_t * first_apic = metadata_get_frame_by_type(fi->meta, META_FIELD_APIC, NULL);
1465 		if (frame == first_apic &&
1466 		    (find_cover_filename(fi->filename) == NULL || !options.use_external_cover_first)) {
1467 			display_cover_from_binary(fi->cover_image_area, fi->event_box, fi->cover_align,
1468 						  THUMB_SIZE, THUMB_SIZE, frame->data, frame->length, FALSE, TRUE);
1469 			fi->cover_set_from_apic = TRUE;
1470 		} else {
1471 			display_cover(fi->cover_image_area, fi->event_box, fi->cover_align,
1472 				      THUMB_SIZE, THUMB_SIZE, fi->filename, FALSE, TRUE);
1473 		}
1474 	}
1475 
1476 	++n_rows;
1477 	fi->pageidx[page].n_rows = n_rows;
1478 	gtk_table_resize(GTK_TABLE(table), n_rows, n_cols);
1479 
1480 	/* Field label */
1481 	label = fi_procframe_label(fi, frame);
1482 	gtk_table_attach(GTK_TABLE(table), label, col, col+1, n_rows-1, n_rows,
1483 			 GTK_FILL, GTK_FILL, 5, 3);
1484 	++col;
1485 
1486 	/* Field entry */
1487 	entry = fi_procframe_entry(fi, frame);
1488 	gtk_table_attach(GTK_TABLE(table), entry, col, col+1, n_rows-1, n_rows,
1489 			 GTK_EXPAND | GTK_FILL, GTK_FILL, 5, 3);
1490 	if (frame->type != META_FIELD_APIC) {
1491 		frame->source = entry;
1492 	}
1493 	++col;
1494 
1495 	/* Import button */
1496 	if (fi->allow_ms_import) {
1497 		importbtn = fi_procframe_importbtn(fi, frame);
1498 		gtk_table_attach(GTK_TABLE(table), importbtn, col, col+1, n_rows-1, n_rows,
1499 				 GTK_FILL, GTK_FILL, 5, 3);
1500 		++col;
1501 	}
1502 
1503 	/* Delete button */
1504 	if (meta->writable) {
1505 		delbtn = fi_procframe_delbtn(fi, frame,
1506 					     label, entry, importbtn);
1507 		gtk_table_attach(GTK_TABLE(table), delbtn, col, col+1, n_rows-1, n_rows,
1508 				 GTK_FILL, GTK_FILL, 5, 3);
1509 		++col;
1510 	}
1511 
1512 	if (meta->writable && (frame->flags & META_FIELD_UNIQUE)) {
1513 		GtkWidget * combo = fi->pageidx[page].combo;
1514 		GSList * slist = fi->pageidx[page].slist;
1515 		slist = g_slist_remove(slist, GINT_TO_POINTER(frame->type));
1516 		fi->pageidx[page].slist = slist;
1517 		fi_fill_combo(GTK_COMBO_BOX(combo), slist);
1518 	}
1519 
1520 	gtk_widget_show_all(fi->nb);
1521 }
1522 
1523 typedef struct {
1524 	fi_t * fi;
1525 	int tag;
1526 } fi_deltag_t;
1527 
1528 void
fi_deltag_button_pressed(GtkWidget * widget,gpointer data)1529 fi_deltag_button_pressed(GtkWidget * widget, gpointer data) {
1530 
1531 	fi_deltag_t * fi_deltag = (fi_deltag_t *)data;
1532 	fi_t * fi = fi_deltag->fi;
1533 	int tag = fi_deltag->tag;
1534 	metadata_t * meta = fi->meta;
1535 	meta_frame_t * frame;
1536 	int i;
1537 
1538 	/* Remove all frames with frame->tag == tag */
1539 	frame = meta->root;
1540 	while (frame != NULL) {
1541 		if (frame->tag == tag) {
1542 			meta_frame_t * f = frame->next;
1543 			metadata_remove_frame(meta, frame);
1544 			meta_frame_free(frame);
1545 			frame = f;
1546 		} else {
1547 			frame = frame->next;
1548 		}
1549 	}
1550 
1551 	/* Remove notebook page and update fi->pageidx */
1552 	for (i = FI_MAXPAGES-1; i >= 0; i--) {
1553 		if (fi->pageidx[i].tag == tag) {
1554 			int k;
1555 			for (k = i; k < FI_MAXPAGES-1; k++) {
1556 				fi->pageidx[k] = fi->pageidx[k+1];
1557 			}
1558 			fi->pageidx[FI_MAXPAGES-1].tag = -1;
1559 			gtk_notebook_remove_page(GTK_NOTEBOOK(fi->nb), i);
1560 			fi->n_pages--;
1561 			break;
1562 		}
1563 	}
1564 
1565 	fi->addable_tags |= tag;
1566 	fi_fill_tagcombo(GTK_COMBO_BOX(fi->combo), fi->addable_tags);
1567 	fi_mark_changed(fi);
1568 }
1569 
1570 GtkWidget *
fi_procframe_deltagbtn(fi_t * fi,int tag)1571 fi_procframe_deltagbtn(fi_t * fi, int tag) {
1572 
1573 	fi_deltag_t * fi_deltag;
1574 	GtkWidget * button = gtk_button_new();
1575 	gtk_button_set_image(GTK_BUTTON(button),
1576 			     gtk_image_new_from_stock(GTK_STOCK_DELETE,
1577 						      GTK_ICON_SIZE_MENU));
1578 
1579 	fi_deltag = calloc(1, sizeof(fi_deltag_t));
1580 	fi_deltag->fi = fi;
1581 	fi_deltag->tag = tag;
1582 	trashlist_add(fi->trash, fi_deltag);
1583 	g_signal_connect(G_OBJECT(button), "clicked",
1584 			 G_CALLBACK(fi_deltag_button_pressed),
1585 			 (gpointer)fi_deltag);
1586 	return button;
1587 }
1588 
1589 void
fi_procframe_add_tag_page(fi_t * fi,meta_frame_t * frame)1590 fi_procframe_add_tag_page(fi_t * fi, meta_frame_t * frame) {
1591 
1592 	metadata_t * meta = fi->meta;
1593 	GtkWidget * vbox = gtk_vbox_new(FALSE, 4);
1594 	GtkWidget * vbox_padding = gtk_vbox_new(FALSE, 0);
1595 	GtkWidget * scrwin = gtk_scrolled_window_new(NULL, NULL);
1596 	GtkWidget * table = gtk_table_new(0, fi_tabwidth(fi, meta), FALSE);
1597 	GtkWidget * label = gtk_label_new(meta_get_tagname(frame->tag));
1598 	GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
1599 	GtkWidget * combo = gtk_combo_box_new_text();
1600 	GtkWidget * addbtn, * delbtn;
1601 	GSList * slist = meta_get_possible_fields(frame->tag);
1602 
1603 	gtk_box_pack_start(GTK_BOX(vbox_padding), table, TRUE, TRUE, 7);
1604 	gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0);
1605 	gtk_widget_set_size_request(scrwin, -1, 275);
1606 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
1607 				       GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1608 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin), GTK_SHADOW_NONE);
1609 	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrwin), vbox_padding);
1610 
1611 	gtk_notebook_append_page(GTK_NOTEBOOK(fi->nb), vbox, label);
1612 	fi->n_pages++;
1613 	fi->pageidx[fi->n_pages-1].tag = frame->tag;
1614 	fi->pageidx[fi->n_pages-1].table = table;
1615 	fi->pageidx[fi->n_pages-1].combo = combo;
1616 	fi->pageidx[fi->n_pages-1].slist = slist;
1617 	fi->pageidx[fi->n_pages-1].n_rows = 0;
1618 	fi->pageidx[fi->n_pages-1].n_cols = fi_tabwidth(fi, meta);
1619 
1620 	if (meta->writable) {
1621 		addbtn = fi_procframe_addbtn(fi, frame->tag);
1622 		gtk_box_pack_start(GTK_BOX(hbox), addbtn, FALSE, FALSE, 5);
1623 
1624 		label = gtk_label_new(_("field:"));
1625 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
1626 
1627 		fi_fill_combo(GTK_COMBO_BOX(combo), slist);
1628 		gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 5);
1629 
1630 		delbtn = fi_procframe_deltagbtn(fi, frame->tag);
1631 		gtk_box_pack_end(GTK_BOX(hbox), delbtn, FALSE, FALSE, 5);
1632 
1633 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1634 
1635 		fi->addable_tags &= ~frame->tag;
1636 		fi_fill_tagcombo(GTK_COMBO_BOX(fi->combo), fi->addable_tags);
1637 	}
1638 	gtk_widget_show_all(fi->nb);
1639 }
1640 
1641 void
fi_procframe(fi_t * fi,meta_frame_t * frame)1642 fi_procframe(fi_t * fi, meta_frame_t * frame) {
1643 
1644 	int page = lookup_page(fi, frame->tag);
1645 	if (page == -1) {
1646 		fi_procframe_add_tag_page(fi, frame);
1647 	}
1648 	fi_procframe_ins(fi, frame);
1649 }
1650 
1651 typedef struct {
1652 	metadata_t * meta;
1653 	void * data;
1654 } fi_procmeta_wait_data_t;
1655 
1656 void fi_procmeta(metadata_t * meta, void * data);
1657 
1658 gboolean
fi_procmeta_wait(gpointer cb_data)1659 fi_procmeta_wait(gpointer cb_data) {
1660 
1661 	fi_procmeta_wait_data_t * wd = (fi_procmeta_wait_data_t *)cb_data;
1662 	fi_procmeta(wd->meta, wd->data);
1663 	free(wd);
1664 	return FALSE;
1665 }
1666 
1667 void
fi_procmeta(metadata_t * meta,void * data)1668 fi_procmeta(metadata_t * meta, void * data) {
1669 
1670 	fi_t * fi = (fi_t *)data;
1671 	meta_frame_t * frame;
1672 	int i;
1673 
1674 	if (fi->bail_out) {
1675 		/* this condition signals that file_decoder_open in show_file_info has failed */
1676 		fi_delete(fi);
1677 		return;
1678 	}
1679 
1680 	if ((fi->nb == NULL) || !GTK_IS_NOTEBOOK(fi->nb)) {
1681 		fi_procmeta_wait_data_t * fi_procmeta_wait_data = calloc(1, sizeof(fi_procmeta_wait_data_t));
1682 		fi_procmeta_wait_data->meta = meta;
1683 		fi_procmeta_wait_data->data = data;
1684 		aqualung_timeout_add(100, fi_procmeta_wait, (gpointer)fi_procmeta_wait_data);
1685 		return;
1686 	}
1687 
1688 	if (fi->meta == meta) {
1689 		return;
1690 	}
1691 
1692 	/* remove possible previous metadata pages from notebook */
1693 	for (i = FI_MAXPAGES-1; i > 0; i--) {
1694 		if (fi->pageidx[i].tag != -1) {
1695 			gtk_notebook_remove_page(GTK_NOTEBOOK(fi->nb), i);
1696 			fi->pageidx[i].tag = -1;
1697 		}
1698 	}
1699 	fi->n_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(fi->nb));
1700 
1701 	if (fi->meta != NULL) {
1702 		metadata_free(fi->meta);
1703 	}
1704 	fi->meta = meta;
1705 	fi->addable_tags = meta->valid_tags;
1706 	frame = meta->root;
1707 
1708 	if (fi->meta->writable && (fi->save_button == NULL)) {
1709 		fi->save_button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
1710 		g_signal_connect(fi->save_button, "clicked",
1711 				 G_CALLBACK(fi_save), (gpointer)fi);
1712 		gtk_table_attach(GTK_TABLE(fi->button_table), fi->save_button,
1713 				 0, 1, 0, 1, GTK_FILL, GTK_FILL, 3, 0);
1714 		gtk_widget_show_all(fi->info_window);
1715 	}
1716 
1717 	if (fi->meta->writable && (fi->add_tag_table == NULL)) {
1718 		GtkWidget * addbtn;
1719 
1720 		fi->add_tag_table = gtk_table_new(1, 3, FALSE);
1721 		gtk_box_pack_start(GTK_BOX(fi->hbox), fi->add_tag_table, FALSE, TRUE, 2);
1722 
1723 		addbtn = fi_procframe_addtagbtn(fi);
1724 		gtk_table_attach(GTK_TABLE(fi->add_tag_table), addbtn,
1725 				 0, 1, 0, 1, GTK_FILL, GTK_FILL, 3, 0);
1726 
1727 		gtk_table_attach(GTK_TABLE(fi->add_tag_table), gtk_label_new(_("tag:")),
1728 				 1, 2, 0, 1, GTK_FILL, GTK_FILL, 3, 0);
1729 
1730 		fi->combo = gtk_combo_box_new_text();
1731 		fi_fill_tagcombo(GTK_COMBO_BOX(fi->combo), fi->addable_tags);
1732 		gtk_table_attach(GTK_TABLE(fi->add_tag_table), fi->combo,
1733 				 2, 3, 0, 1, GTK_FILL, GTK_FILL, 3, 0);
1734 
1735 		gtk_widget_show_all(fi->info_window);
1736 	}
1737 
1738 	while (frame != NULL) {
1739 		fi_procframe(fi, frame);
1740 		frame = frame->next;
1741 	}
1742 
1743 	fi_set_page(fi);
1744 }
1745 
1746 
1747 gboolean
fi_cover_press_button_cb(GtkWidget * widget,GdkEventButton * event,gpointer data)1748 fi_cover_press_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
1749 
1750 	fi_t * fi = (fi_t *)data;
1751 
1752 	if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
1753 		meta_frame_t * frame;
1754 		frame = metadata_get_frame_by_type(fi->meta, META_FIELD_APIC, NULL);
1755 		if (frame != NULL && (find_cover_filename(fi->filename) == NULL || !options.use_external_cover_first)) {
1756 			display_zoomed_cover_from_binary(fi->info_window, fi->event_box, frame->data, frame->length);
1757 		} else {
1758 			display_zoomed_cover(fi->info_window, fi->event_box, (gchar *)fi->filename);
1759 		}
1760         }
1761         return TRUE;
1762 }
1763 
1764 void
fi_add_file_table_row(char * caption,GtkWidget ** entry,GtkWidget * table,int row)1765 fi_add_file_table_row(char * caption, GtkWidget ** entry, GtkWidget * table, int row) {
1766 	GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
1767 	GtkWidget * label = gtk_label_new(caption);
1768 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1769 	gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, row, row+1, GTK_FILL, GTK_FILL, 5, 3);
1770 	*entry = gtk_entry_new();
1771 	gtk_widget_set_size_request(*entry, 350, -1);
1772 	gtk_widget_set_can_focus(*entry, FALSE);
1773 	gtk_editable_set_editable(GTK_EDITABLE(*entry), FALSE);
1774 	gtk_table_attach(GTK_TABLE(table), *entry, 1, 2, row, row+1,
1775 			 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), GTK_FILL, 5, 3);
1776 }
1777 
1778 void
fi_set_common_entries(fi_t * fi)1779 fi_set_common_entries(fi_t * fi) {
1780         char str[MAXLEN];
1781 	gchar * file_display;
1782 
1783 	if (!fi->media_ok) {
1784 		if (GTK_IS_ENTRY(fi->entry_name)) gtk_entry_set_text(GTK_ENTRY(fi->entry_name), fi->name);
1785 		if (GTK_IS_ENTRY(fi->entry_path)) gtk_entry_set_text(GTK_ENTRY(fi->entry_path), "(unable to open)");
1786 		if (GTK_IS_ENTRY(fi->entry_format)) gtk_entry_set_text(GTK_ENTRY(fi->entry_format), "");
1787 		if (GTK_IS_ENTRY(fi->entry_length)) gtk_entry_set_text(GTK_ENTRY(fi->entry_length), "");
1788 		if (GTK_IS_ENTRY(fi->entry_sr)) gtk_entry_set_text(GTK_ENTRY(fi->entry_sr), "");
1789 		if (GTK_IS_ENTRY(fi->entry_ch)) gtk_entry_set_text(GTK_ENTRY(fi->entry_ch), "");
1790 		if (GTK_IS_ENTRY(fi->entry_bw)) gtk_entry_set_text(GTK_ENTRY(fi->entry_bw), "");
1791 		if (GTK_IS_ENTRY(fi->entry_nsamples)) gtk_entry_set_text(GTK_ENTRY(fi->entry_nsamples), "");
1792 		if (GTK_IS_ENTRY(fi->entry_mode)) gtk_entry_set_text(GTK_ENTRY(fi->entry_mode), "");
1793 		return;
1794 	}
1795 
1796 	/* Heading */
1797 
1798 	gtk_entry_set_text(GTK_ENTRY(fi->entry_name), fi->name);
1799 
1800 	file_display = g_filename_display_name(fi->filename);
1801 	gtk_entry_set_text(GTK_ENTRY(fi->entry_path), file_display);
1802 	g_free(file_display);
1803 
1804 	if (!fi->cover_set_from_apic) {
1805 		if (options.show_cover_for_ms_tracks_only == TRUE) {
1806 			if (fi->display_cover == TRUE) {
1807 				display_cover(fi->cover_image_area, fi->event_box, fi->cover_align,
1808 					      THUMB_SIZE, THUMB_SIZE, fi->filename, TRUE, TRUE);
1809 			} else {
1810 				hide_cover_thumbnail();
1811 			}
1812 		} else {
1813 			display_cover(fi->cover_image_area, fi->event_box, fi->cover_align,
1814 				      THUMB_SIZE, THUMB_SIZE, fi->filename, TRUE, TRUE);
1815 		}
1816 	}
1817 
1818 	/* Audio data */
1819 
1820 	gtk_entry_set_text(GTK_ENTRY(fi->entry_format), fi->fileinfo.format_str);
1821 
1822 	if (fi->fileinfo.total_samples == 0) {
1823 		strcpy(str, "N/A");
1824 	} else {
1825 		sample2time(fi->fileinfo.sample_rate, fi->fileinfo.total_samples, str, 0);
1826 	}
1827 	gtk_entry_set_text(GTK_ENTRY(fi->entry_length), str);
1828 
1829 	sprintf(str, _("%ld Hz"), fi->fileinfo.sample_rate);
1830 	gtk_entry_set_text(GTK_ENTRY(fi->entry_sr), str);
1831 
1832 	if (fi->fileinfo.is_mono) {
1833 		strcpy(str, _("MONO"));
1834 	} else {
1835 		strcpy(str, _("STEREO"));
1836 	}
1837 	gtk_entry_set_text(GTK_ENTRY(fi->entry_ch), str);
1838 
1839 	if (fi->fileinfo.bps == 0) {
1840 		strcpy(str, "N/A kbit/s");
1841 	} else {
1842 		format_bps_label(fi->fileinfo.bps, fi->fileinfo.format_flags, str);
1843 	}
1844 	gtk_entry_set_text(GTK_ENTRY(fi->entry_bw), str);
1845 
1846 	if (fi->fileinfo.total_samples == 0) {
1847 		strcpy(str, "N/A");
1848 	} else {
1849 		sprintf(str, "%lld", fi->fileinfo.total_samples);
1850 	}
1851 	gtk_entry_set_text(GTK_ENTRY(fi->entry_nsamples), str);
1852 
1853 #ifdef HAVE_WAVPACK
1854 	if (!fi->is_cdda && fi->fdec && fi->fdec->file_lib == WAVPACK_LIB) {
1855 		wavpack_pdata_t * pd = (wavpack_pdata_t *)fi->dec->pdata;
1856 		int mode = WavpackGetMode(pd->wpc);
1857 
1858 		if ((mode & MODE_LOSSLESS) && (mode & MODE_WVC)) {
1859 			strncpy(str, "Hybrid Lossless", MAXLEN-1);
1860 		} else if (mode & MODE_LOSSLESS) {
1861 			strncpy(str, "Lossless", MAXLEN-1);
1862 		} else {
1863 			strncpy(str, "Hybrid Lossy", MAXLEN-1);
1864 		}
1865 		cut_trailing_whitespace(str);
1866 		gtk_entry_set_text(GTK_ENTRY(fi->entry_mode), str);
1867 	}
1868 #endif /* HAVE_WAVPACK */
1869 }
1870 
1871 /* Fill up internal data with values from referenced file.
1872  * Return TRUE if successful, FALSE on error.
1873  */
1874 gboolean
fi_media_init(fi_t * fi)1875 fi_media_init(fi_t * fi) {
1876 
1877 	fi->is_cdda = 0;
1878 	fi->bail_out = 0;
1879 	fi->trash = trashlist_new();
1880 
1881 #ifdef HAVE_CDDA
1882         if (g_str_has_prefix(fi->filename, "CDDA ")) {
1883 
1884                 char device_path[CDDA_MAXLEN];
1885                 long hash;
1886                 int track;
1887                 cdda_drive_t * drive;
1888 
1889                 if (sscanf(fi->filename, "CDDA %s %lX %u", device_path, &hash, &track) < 3) {
1890                         return FALSE;
1891                 }
1892 
1893                 drive = cdda_get_drive_by_device_path(device_path);
1894                 if ((drive == NULL) || (drive->disc.hash == 0L)) {
1895 			return FALSE;
1896 		}
1897 
1898 		fi->is_cdda = 1;
1899 
1900 		fi->fileinfo.format_str = _("Audio CD");
1901 		fi->fileinfo.sample_rate = 44100;
1902 		fi->fileinfo.is_mono = 0;
1903 		fi->fileinfo.format_flags = 0;
1904 		fi->fileinfo.bps = 2*16*44100;
1905 		fi->fileinfo.total_samples = (drive->disc.toc[track] - drive->disc.toc[track-1]) * 588;
1906         }
1907 #endif /* HAVE_CDDA */
1908 
1909 	if (!fi->is_cdda) {
1910 		fi->fdec = file_decoder_new();
1911 		if (fi->fdec == NULL) {
1912 			return FALSE;
1913 		}
1914 
1915 		file_decoder_set_meta_cb(fi->fdec, fi_procmeta, fi);
1916 		if (file_decoder_open(fi->fdec, fi->filename) != 0) {
1917 			fi->bail_out = 1;
1918 			return FALSE;
1919 		}
1920 
1921 		file_decoder_send_metadata(fi->fdec);
1922 		fi->dec = (decoder_t *)fi->fdec->pdec;
1923 
1924 		fi->fileinfo.format_str = fi->dec->format_str;
1925 		fi->fileinfo.sample_rate = fi->fdec->fileinfo.sample_rate;
1926 		fi->fileinfo.is_mono = fi->fdec->fileinfo.is_mono;
1927 		fi->fileinfo.format_flags = fi->fdec->fileinfo.format_flags;
1928 		fi->fileinfo.bps = fi->fdec->fileinfo.bps;
1929 		fi->fileinfo.total_samples = fi->fdec->fileinfo.total_samples;
1930 	}
1931 	return TRUE;
1932 }
1933 
1934 void
fi_reload(fi_t * fi,GtkTreeIter iter)1935 fi_reload(fi_t * fi, GtkTreeIter iter) {
1936 	int i;
1937 
1938 	fi->selected_tag = fi->pageidx[gtk_notebook_get_current_page(GTK_NOTEBOOK(fi->nb))].tag;
1939 
1940 	fi_unload(fi);
1941 	fi->iter_track = iter;
1942 	fi->mfun(fi->model, fi->iter_track, &fi->name, &fi->filename);
1943 
1944 	/* remove any metadata pages from notebook */
1945 	while (gtk_notebook_get_n_pages(GTK_NOTEBOOK(fi->nb)) > 1) {
1946 		gtk_notebook_remove_page(GTK_NOTEBOOK(fi->nb), 1);
1947 	}
1948 	fi->n_pages = 1;
1949 	for (i = 1; i < FI_MAXPAGES; i++) {
1950 		fi->pageidx[i].tag = FALSE;
1951 	}
1952 
1953 	fi->media_ok = fi_media_init(fi);
1954 	fi_set_common_entries(fi);
1955 }
1956 
1957 gint
fi_prev(GtkWidget * widget,gpointer data)1958 fi_prev(GtkWidget * widget, gpointer data) {
1959 	fi_t * fi = data;
1960 	GtkTreeModel * model = fi->model;
1961 	GtkTreeIter iter = fi->iter_track;
1962 	GtkTreeIter prev;
1963 
1964 	if (fi_can_close(fi) != TRUE) {
1965 		return TRUE;
1966 	}
1967 	if (!tree_model_prev_iter(model, &iter, &prev, fi->mindepth)) {
1968 		return TRUE;
1969 	}
1970 	fi_reload(fi, prev);
1971 	return TRUE;
1972 }
1973 
1974 gint
fi_next(GtkWidget * widget,gpointer data)1975 fi_next(GtkWidget * widget, gpointer data) {
1976 	fi_t * fi = data;
1977 	GtkTreeModel * model = fi->model;
1978 	GtkTreeIter iter = fi->iter_track;
1979 	GtkTreeIter next;
1980 
1981 	if (fi_can_close(fi) != TRUE) {
1982 		return TRUE;
1983 	}
1984 	if (!tree_model_next_iter(model, &iter, &next, fi->mindepth)) {
1985 		return TRUE;
1986 	}
1987 	fi_reload(fi, next);
1988 	return TRUE;
1989 }
1990 
1991 void
show_file_info(GtkTreeModel * model,GtkTreeIter iter_track,fileinfo_model_func_t mfun,int mindepth,gboolean allow_ms_import,gboolean display_cover)1992 show_file_info(GtkTreeModel * model, GtkTreeIter iter_track,
1993 	       fileinfo_model_func_t mfun, int mindepth,
1994 	       gboolean allow_ms_import, gboolean display_cover) {
1995 
1996 	GtkWidget * vbox;
1997 	GtkWidget * hbox_t;
1998 	GtkWidget * table;
1999 	GtkWidget * dismiss_btn;
2000 
2001 	GtkWidget * vbox_file;
2002 	GtkWidget * label_file;
2003 	GtkWidget * table_file;
2004 
2005 	fi_t * fi = fi_new();
2006 	if (fi == NULL) {
2007 		return;
2008 	}
2009 
2010 	/* save arguments */
2011 	fi->model = model;
2012 	fi->iter_track = iter_track;
2013 	fi->mfun = mfun;
2014 	fi->mindepth = mindepth;
2015 	fi->allow_ms_import = allow_ms_import;
2016 	fi->display_cover = display_cover;
2017 	mfun(fi->model, fi->iter_track, &fi->name, &fi->filename);
2018 
2019 	fi->info_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2020 	register_toplevel_window(fi->info_window, TOP_WIN_SKIN | TOP_WIN_TRAY);
2021         gtk_window_set_title(GTK_WINDOW(fi->info_window), _("File info"));
2022 	gtk_window_set_transient_for(GTK_WINDOW(fi->info_window), GTK_WINDOW(main_window));
2023 	gtk_window_set_position(GTK_WINDOW(fi->info_window), GTK_WIN_POS_CENTER);
2024         gtk_window_set_resizable(GTK_WINDOW(fi->info_window), TRUE);
2025 	g_signal_connect(G_OBJECT(fi->info_window), "delete_event",
2026 			 G_CALLBACK(info_window_close), (gpointer)fi);
2027         g_signal_connect(G_OBJECT(fi->info_window), "key_press_event",
2028 			 G_CALLBACK(info_window_key_pressed), (gpointer)fi);
2029 	gtk_container_set_border_width(GTK_CONTAINER(fi->info_window), 5);
2030 
2031 	vbox = gtk_vbox_new(FALSE, 0);
2032 	gtk_container_add(GTK_CONTAINER(fi->info_window), vbox);
2033 
2034 	hbox_t = gtk_hbox_new(FALSE, 0);
2035 	gtk_box_pack_start(GTK_BOX(vbox), hbox_t, FALSE, FALSE, 5);
2036 
2037 	table = gtk_table_new(2, 2, FALSE);
2038 	gtk_box_pack_start(GTK_BOX(hbox_t), table, TRUE, TRUE, 4);
2039 
2040 	fi_add_file_table_row(_("Track:"), &fi->entry_name, table, 0);
2041 	fi_add_file_table_row(_("File:"), &fi->entry_path, table, 1);
2042 
2043 	fi->cover_align = gtk_alignment_new(0.5f, 0.5f, 0.0f, 0.0f);
2044 	gtk_box_pack_start(GTK_BOX(hbox_t), fi->cover_align, FALSE, FALSE, 0);
2045         fi->cover_image_area = gtk_image_new();
2046         fi->event_box = gtk_event_box_new ();
2047 	gtk_container_add(GTK_CONTAINER(fi->cover_align), fi->event_box);
2048         gtk_container_add (GTK_CONTAINER (fi->event_box), fi->cover_image_area);
2049         g_signal_connect(G_OBJECT(fi->event_box), "button_press_event",
2050                          G_CALLBACK(fi_cover_press_button_cb), (gpointer)fi);
2051 	fi->hbox = gtk_hbox_new(FALSE, 0);
2052 	gtk_box_pack_end(GTK_BOX(vbox), fi->hbox, FALSE, FALSE, 5);
2053 
2054 	fi->nb = gtk_notebook_new();
2055 	gtk_box_pack_start(GTK_BOX(vbox), fi->nb, TRUE, TRUE, 5);
2056 
2057 	/* Audio data notebook page */
2058 
2059 	vbox_file = gtk_vbox_new(FALSE, 4);
2060 	table_file = gtk_table_new(6, 2, FALSE);
2061 	gtk_box_pack_start(GTK_BOX(vbox_file), table_file, TRUE, TRUE, 10);
2062 	label_file = gtk_label_new(_("Audio data"));
2063 	fi->pageidx[0].tag = -1;
2064 	gtk_notebook_append_page(GTK_NOTEBOOK(fi->nb), vbox_file, label_file);
2065 	fi->n_pages = 1;
2066 
2067 	fi_add_file_table_row(_("Format:"), &fi->entry_format, table_file, 0);
2068 	fi_add_file_table_row(_("Length:"), &fi->entry_length, table_file, 1);
2069 	fi_add_file_table_row(_("Samplerate:"), &fi->entry_sr, table_file, 2);
2070 	fi_add_file_table_row(_("Channel count:"), &fi->entry_ch, table_file, 3);
2071 	fi_add_file_table_row(_("Bandwidth:"), &fi->entry_bw, table_file, 4);
2072 	fi_add_file_table_row(_("Total samples:"), &fi->entry_nsamples, table_file, 5);
2073 #ifdef HAVE_WAVPACK
2074 	if (!fi->is_cdda && fi->fdec && fi->fdec->file_lib == WAVPACK_LIB) {
2075 		fi_add_file_table_row(_("Mode:"), &fi->entry_mode, table_file, 6);
2076 	}
2077 #endif /* HAVE_WAVPACK */
2078 
2079 	/* end of notebook stuff */
2080 	gtk_widget_grab_focus(fi->nb);
2081 
2082 	fi->button_table = gtk_table_new(1, 2, FALSE);
2083 	gtk_box_pack_end(GTK_BOX(fi->hbox), fi->button_table, FALSE, TRUE, 2);
2084 
2085 	fi->prev_button = gui_stock_label_button(NULL, GTK_STOCK_GO_BACK);
2086 	g_signal_connect(fi->prev_button, "clicked", G_CALLBACK(fi_prev), (gpointer)fi);
2087 	gtk_table_attach(GTK_TABLE(fi->button_table), fi->prev_button,
2088 			 1, 2, 0, 1, GTK_FILL, GTK_FILL, 3, 0);
2089 
2090 	fi->next_button = gui_stock_label_button(NULL, GTK_STOCK_GO_FORWARD);
2091 	g_signal_connect(fi->next_button, "clicked", G_CALLBACK(fi_next), (gpointer)fi);
2092 	gtk_table_attach(GTK_TABLE(fi->button_table), fi->next_button,
2093 			 2, 3, 0, 1, GTK_FILL, GTK_FILL, 3, 0);
2094 
2095         dismiss_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
2096 	g_signal_connect(dismiss_btn, "clicked", G_CALLBACK(dismiss), (gpointer)fi);
2097 	gtk_table_attach(GTK_TABLE(fi->button_table), dismiss_btn, 3, 4, 0, 1,
2098 			 GTK_FILL, GTK_FILL, 3, 0);
2099 
2100 	gtk_widget_show_all(fi->info_window);
2101 
2102 	fi->media_ok = fi_media_init(fi);
2103 	fi_set_common_entries(fi);
2104 	fi_set_page(fi);
2105 
2106 	/* fi_t object is freed in info_window destroy handlers */
2107 }
2108 
2109 
2110 #ifdef HAVE_MOD
2111 /*
2112  * type = 0 for sample list
2113  * type != 0 for instrument list
2114  */
2115 void
show_list(fi_t * fi,gint type)2116 show_list(fi_t * fi, gint type) {
2117 
2118 	GtkTreeIter iter;
2119 	gint i, len;
2120 	gchar temp[MAXLEN], number[MAXLEN];
2121 	decoder_t * md_dec;
2122 	mod_pdata_t * md_pd;
2123 
2124         md_dec = (decoder_t *)(fi->fdec->pdec);
2125         md_pd = (mod_pdata_t *)md_dec->pdata;
2126 
2127         if (type) {
2128                 len = ModPlug_NumInstruments(md_pd->mpf);
2129         } else {
2130                 len = ModPlug_NumSamples(md_pd->mpf);
2131         }
2132 
2133         if (len) {
2134                 gtk_list_store_clear(fi->smp_instr_list_store);
2135                 for(i = 0; i < len; i++) {
2136                         memset(temp, 0, MAXLEN-1);
2137 
2138                         if (type) {
2139                                 ModPlug_InstrumentName(md_pd->mpf, i, temp);
2140                         } else {
2141                                 ModPlug_SampleName(md_pd->mpf, i, temp);
2142                         }
2143 
2144                         sprintf(number, "%2d", i);
2145                         gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(fi->smp_instr_list_store), &iter, NULL, i);
2146                         gtk_list_store_append(fi->smp_instr_list_store, &iter);
2147                         gtk_list_store_set(fi->smp_instr_list_store, &iter, 0, number, 1, temp, -1);
2148                 }
2149         }
2150 }
2151 
2152 void
set_first_row(fi_t * fi)2153 set_first_row(fi_t * fi) {
2154 
2155 	GtkTreeIter iter;
2156 	GtkTreePath * visible_path;
2157 
2158         gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(fi->smp_instr_list_store), &iter, NULL, 0);
2159         visible_path = gtk_tree_model_get_path(GTK_TREE_MODEL(fi->smp_instr_list_store), &iter);
2160         gtk_tree_view_set_cursor(GTK_TREE_VIEW(fi->smp_instr_list), visible_path, NULL, TRUE);
2161         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fi->smp_instr_list), visible_path,
2162 				     NULL, TRUE, 1.0, 0.0);
2163         gtk_widget_grab_focus(GTK_WIDGET(fi->smp_instr_list));
2164 }
2165 
2166 void
radio_buttons_cb(GtkToggleButton * toggle_button,gpointer data)2167 radio_buttons_cb(GtkToggleButton * toggle_button, gpointer data) {
2168 
2169 	fi_t * fi = (fi_t *)data;
2170 
2171 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button))) {
2172                 show_list(fi, 0);
2173         } else {
2174                 show_list(fi, 1);
2175         }
2176         set_first_row(fi);
2177 }
2178 
2179 static void
module_info_add_row(char * caption,GtkWidget ** mod_label,GtkWidget * table,int row)2180 module_info_add_row(char * caption, GtkWidget ** mod_label, GtkWidget * table, int row) {
2181 	GtkWidget * label = gtk_label_new(caption);
2182 	gtk_widget_show(label);
2183 	gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row+1,
2184 			 (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0);
2185 	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
2186 
2187 	*mod_label = gtk_label_new("");
2188 	gtk_widget_show(*mod_label);
2189 	gtk_table_attach(GTK_TABLE(table), *mod_label, 1, 2, row, row+1,
2190 			 (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(0), 0, 0);
2191 }
2192 
2193 void
module_info_fill_page(fi_t * fi,meta_frame_t * frame,GtkWidget * vbox)2194 module_info_fill_page(fi_t * fi, meta_frame_t * frame, GtkWidget * vbox) {
2195 
2196 	mod_info * mdi = (mod_info *)frame->data;
2197 
2198 	gchar *a_type[] = {
2199 		"None", "MOD", "S3M", "XM", "MED", "MTM", "IT", "669",
2200 		"ULT", "STM", "FAR", "WAV", "AMF", "AMS", "DSM", "MDL",
2201 		"OKT", "MID", "DMF", "PTM", "DBM", "MT2", "AMF0", "PSM",
2202 		"J2B", "UMX"
2203 	};
2204 
2205 	gint i, n;
2206 	gchar temp[MAXLEN];
2207 	GtkWidget *table;
2208 	GtkWidget *label;
2209 	GtkWidget *mod_type_label;
2210 	GtkWidget *mod_channels_label;
2211 	GtkWidget *mod_patterns_label;
2212 	GtkWidget *mod_samples_label;
2213 	GtkWidget *mod_instruments_label;
2214 	GtkWidget *vseparator;
2215 	GtkWidget *hbox2;
2216 	GtkWidget *vbox2;
2217 	GtkWidget *vbox3;
2218 	GtkWidget *samples_radiobutton = NULL;
2219 	GtkWidget *instruments_radiobutton = NULL;
2220 	GtkWidget *scrolledwindow;
2221 
2222 	GtkCellRenderer *renderer;
2223 	GtkTreeViewColumn *column;
2224 
2225         hbox2 = gtk_hbox_new (FALSE, 0);
2226         gtk_widget_show (hbox2);
2227         gtk_box_pack_start (GTK_BOX (vbox), hbox2, TRUE, TRUE, 0);
2228 
2229         vbox2 = gtk_vbox_new (FALSE, 0);
2230         gtk_widget_show (vbox2);
2231         gtk_box_pack_start (GTK_BOX (hbox2), vbox2, FALSE, FALSE, 0);
2232 
2233         if (mdi->instruments) {
2234                 table = gtk_table_new (5, 2, FALSE);
2235                 gtk_widget_show (table);
2236                 gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
2237                 gtk_container_set_border_width (GTK_CONTAINER (table), 8);
2238                 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
2239                 gtk_table_set_col_spacings (GTK_TABLE (table), 4);
2240 
2241 		module_info_add_row(_("Type:"), &mod_type_label, table, 0);
2242 		module_info_add_row(_("Channels:"), &mod_channels_label, table, 1);
2243 		module_info_add_row(_("Patterns:"), &mod_patterns_label, table, 2);
2244 		module_info_add_row(_("Samples:"), &mod_samples_label, table, 3);
2245 		module_info_add_row(_("Instruments:"), &mod_instruments_label, table, 4);
2246 
2247                 sprintf(temp, "%d", mdi->instruments);
2248                 gtk_label_set_text (GTK_LABEL(mod_instruments_label), temp);
2249 
2250                 table = gtk_table_new (2, 1, FALSE);
2251                 gtk_widget_show (table);
2252                 gtk_box_pack_end (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
2253                 gtk_container_set_border_width (GTK_CONTAINER (table), 8);
2254                 gtk_table_set_row_spacings (GTK_TABLE (table), 4);
2255 
2256                 samples_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, _("Samples"));
2257                 gtk_widget_show (samples_radiobutton);
2258                 gtk_table_attach (GTK_TABLE (table), samples_radiobutton, 0, 1, 0, 1,
2259                                   (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
2260                                   (GtkAttachOptions) (0), 0, 0);
2261 		g_signal_connect(G_OBJECT(samples_radiobutton), "toggled",
2262 				 G_CALLBACK(radio_buttons_cb), (gpointer)fi);
2263 
2264                 instruments_radiobutton = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON(samples_radiobutton), _("Instruments"));
2265                 gtk_widget_show (instruments_radiobutton);
2266                 gtk_table_attach (GTK_TABLE (table), instruments_radiobutton, 0, 1, 1, 2,
2267                                   (GtkAttachOptions) (GTK_FILL),
2268                                   (GtkAttachOptions) (0), 0, 0);
2269         } else {
2270                 table = gtk_table_new (4, 2, FALSE);
2271                 gtk_widget_show (table);
2272                 gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
2273                 gtk_container_set_border_width (GTK_CONTAINER (table), 8);
2274                 gtk_table_set_row_spacings (GTK_TABLE (table), 4);
2275                 gtk_table_set_col_spacings (GTK_TABLE (table), 4);
2276 
2277 		module_info_add_row(_("Type:"), &mod_type_label, table, 0);
2278 		module_info_add_row(_("Channels:"), &mod_channels_label, table, 1);
2279 		module_info_add_row(_("Patterns:"), &mod_patterns_label, table, 2);
2280 		module_info_add_row(_("Samples:"), &mod_samples_label, table, 3);
2281 		module_info_add_row(_("Instruments:"), &mod_instruments_label, table, 4);
2282         }
2283 
2284         n = mdi->type;
2285         i = 0;
2286 
2287         while (n > 0) {         /* calculate module type index */
2288                 n >>= 1;
2289                 i++;
2290         }
2291 
2292         gtk_label_set_text (GTK_LABEL(mod_type_label), a_type[i]);
2293         sprintf(temp, "%d", mdi->channels);
2294         gtk_label_set_text (GTK_LABEL(mod_channels_label), temp);
2295         sprintf(temp, "%d", mdi->patterns);
2296         gtk_label_set_text (GTK_LABEL(mod_patterns_label), temp);
2297         sprintf(temp, "%d", mdi->samples);
2298         gtk_label_set_text (GTK_LABEL(mod_samples_label), temp);
2299 
2300         vseparator = gtk_vseparator_new ();
2301         gtk_widget_show (vseparator);
2302         gtk_box_pack_start (GTK_BOX (hbox2), vseparator, FALSE, FALSE, 4);
2303 
2304         vbox3 = gtk_vbox_new (FALSE, 0);
2305         gtk_widget_show (vbox3);
2306         gtk_box_pack_start (GTK_BOX (hbox2), vbox3, TRUE, TRUE, 0);
2307 
2308         scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
2309         gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow), 4);
2310         gtk_widget_show (scrolledwindow);
2311         gtk_widget_set_size_request (scrolledwindow, -1, 220);
2312         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
2313                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2314         gtk_box_pack_end (GTK_BOX (vbox3), scrolledwindow, TRUE, TRUE, 0);
2315         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN);
2316 
2317         fi->smp_instr_list_store = gtk_list_store_new(2,
2318 						      G_TYPE_STRING,
2319 						      G_TYPE_STRING);
2320         fi->smp_instr_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(fi->smp_instr_list_store));
2321 	gtk_widget_set_name(fi->smp_instr_list, "samples_instruments_list");
2322         gtk_widget_show (fi->smp_instr_list);
2323 	renderer = gtk_cell_renderer_text_new();
2324 	column = gtk_tree_view_column_new_with_attributes(_("No."), renderer, "text", 0, NULL);
2325         gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
2326                                         GTK_TREE_VIEW_COLUMN_FIXED);
2327         gtk_tree_view_column_set_spacing(GTK_TREE_VIEW_COLUMN(column), 3);
2328         gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), FALSE);
2329         gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column), 40);
2330 	gtk_tree_view_append_column(GTK_TREE_VIEW(fi->smp_instr_list), column);
2331 	column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", 1, NULL);
2332         gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
2333                                         GTK_TREE_VIEW_COLUMN_FIXED);
2334 	gtk_tree_view_append_column(GTK_TREE_VIEW(fi->smp_instr_list), column);
2335         gtk_tree_view_column_set_spacing(GTK_TREE_VIEW_COLUMN(column), 3);
2336         gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), FALSE);
2337         gtk_container_add (GTK_CONTAINER (scrolledwindow), fi->smp_instr_list);
2338 
2339         if (mdi->instruments && mdi->type == 0x4) { /* if XM module go to instrument page */
2340                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(instruments_radiobutton), TRUE);
2341         } else {
2342                 show_list(fi, 0);
2343                 set_first_row(fi);
2344         }
2345 }
2346 #endif /* HAVE_MOD */
2347 
2348 
2349 // vim: shiftwidth=8:tabstop=8:softtabstop=8 :
2350 
2351