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: export.c 1292 2014-05-02 12:29:01Z tszilagyi $
19 */
20 
21 #include <config.h>
22 
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <fnmatch.h>
30 #include <sys/stat.h>
31 #include <glib.h>
32 #include <glib/gstdio.h>
33 #include <glib-object.h>
34 #include <gdk/gdk.h>
35 #include <gtk/gtk.h>
36 
37 #include "athread.h"
38 #include "common.h"
39 #include "i18n.h"
40 #include "utils.h"
41 #include "utils_gui.h"
42 #include "decoder/file_decoder.h"
43 #include "encoder/file_encoder.h"
44 #include "encoder/enc_lame.h"
45 #include "metadata.h"
46 #include "options.h"
47 #include "export.h"
48 
49 
50 #define BUFSIZE 10240
51 
52 extern GtkWidget * main_window;
53 extern options_t options;
54 
55 GtkWidget * export_window;
56 int export_slot_count;
57 
58 typedef struct {
59 
60 	char * infile;
61 
62 	char * artist;
63 	char * album;
64 	char * title;
65 	char * original;
66 	int year;
67 	int no;
68 
69 } export_item_t;
70 
71 
72 char *
export_compress_str(char * buf,int limit)73 export_compress_str(char * buf, int limit) {
74 
75 	char * str;
76 	char * valid = "abcdefghijklmnopqrstuvwxyz0123456789";
77 	int i = 0;
78 	int j = 0;
79 
80 	str = g_ascii_strdown(buf, -1);
81 	g_strcanon(str, valid, '_');
82 
83 	for (i = 0; str[i]; i++) {
84 		if (str[i] == '_') {
85 			continue;
86 		}
87 		str[j] = str[i];
88 		j++;
89 	}
90 
91 	if (limit < j) {
92 		str[limit] = '\0';
93 	} else if (j > 0) {
94 		str[j] = '\0';
95 	} else {
96 		str[0] = 'x';
97 		str[1] = '\0';
98 	}
99 
100 	return str;
101 }
102 
103 int
export_map_has(export_map_t * map,char * val)104 export_map_has(export_map_t * map, char * val) {
105 
106 	export_map_t * pmap;
107 
108 	for (pmap = map; pmap; pmap = pmap->next) {
109 		if (!strcmp(pmap->val, val)) {
110 			return 1;
111 		}
112 	}
113 
114 	return 0;
115 }
116 
117 export_map_t *
export_map_new(export_map_t * _map,char * key,int limit)118 export_map_new(export_map_t * _map, char * key, int limit) {
119 
120 	export_map_t * map;
121 	char * tmp;
122 	int i = '2';
123 
124 	if ((map = (export_map_t *)malloc(sizeof(export_map_t))) == NULL) {
125 		fprintf(stderr, "export_map_new(): malloc error\n");
126 		return NULL;
127 	}
128 
129 	map->next = NULL;
130 	map->key = g_utf8_casefold(key, -1);
131 
132 	tmp = export_compress_str(key, limit);
133 
134 	while (export_map_has(_map, tmp) && i <= 'z') {
135 
136 		tmp[strlen(tmp)-1] = i;
137 
138 		if ((i >= '2' && i < '9') || (i >= 'a' && i <= 'z')) {
139 			++i;
140 		} else {
141 			i = 'a';
142 		}
143 	}
144 
145 	map->val = strdup(tmp);
146 
147 	g_free(tmp);
148 	return map;
149 }
150 
151 char *
export_map_put(export_map_t ** map,char * key,int limit)152 export_map_put(export_map_t ** map, char * key, int limit) {
153 
154 	export_map_t * pmap;
155 	export_map_t * _pmap;
156 
157 	if (key == NULL || key[0] == '\0') {
158 		return NULL;
159 	}
160 
161 	if (*map == NULL) {
162 		*map = export_map_new(NULL, key, limit);
163 		return (*map)->val;
164 	} else {
165 		char * key1 = g_utf8_casefold(key, -1);
166 
167 		for (_pmap = pmap = *map; pmap; _pmap = pmap, pmap = pmap->next) {
168 
169 			if (!g_utf8_collate(key1, pmap->key)) {
170 				g_free(key1);
171 				return pmap->val;
172 			}
173 		}
174 
175 		g_free(key1);
176 
177 		_pmap->next = export_map_new(*map, key, limit);
178 		return _pmap->next->val;
179 	}
180 }
181 
182 void
export_map_free(export_map_t * map)183 export_map_free(export_map_t * map) {
184 
185 	export_map_t * pmap;
186 
187 	for (pmap = map; pmap; map = pmap) {
188 		pmap = map->next;
189 		g_free(map->key);
190 		free(map->val);
191 		free(map);
192 	}
193 }
194 
195 
196 export_t *
export_new(void)197 export_new(void) {
198 
199 	export_t * export;
200 
201 	if ((export = (export_t *)calloc(1, sizeof(export_t))) == NULL) {
202 		fprintf(stderr, "export_new: calloc error\n");
203 		return NULL;
204 	}
205 
206 #ifndef HAVE_LIBPTHREAD
207         export->mutex = g_mutex_new();
208 #endif /* !HAVE_LIBPTHREAD */
209 
210 	return export;
211 }
212 
213 void
export_item_free(export_item_t * item)214 export_item_free(export_item_t * item) {
215 
216 	free(item->infile);
217 	free(item->artist);
218 	free(item->album);
219 	free(item->title);
220 	free(item->original);
221 	free(item);
222 }
223 
224 void
export_free(export_t * export)225 export_free(export_t * export) {
226 
227 	GSList * node;
228 
229 #ifndef HAVE_LIBPTHREAD
230 	g_mutex_free(export->mutex);
231 #endif /* !HAVE_LIBPTHREAD */
232 
233 	for (node = export->slist; node; node = node->next) {
234 		export_item_free((export_item_t *)node->data);
235 	}
236 
237 	g_slist_free(export->slist);
238 	g_strfreev(export->excl_patternv);
239 
240 	export_map_free(export->artist_map);
241 	export_map_free(export->record_map);
242 	free(export);
243 }
244 
245 void
export_append_item(export_t * export,char * infile,char * artist,char * album,char * title,int year,int no)246 export_append_item(export_t * export, char * infile,
247 		   char * artist, char * album, char * title, int year, int no) {
248 
249 	export_item_t * item;
250 	char * basename;
251 	char * ext;
252 
253 	if ((item = (export_item_t *)calloc(1, sizeof(export_item_t))) == NULL) {
254 		fprintf(stderr, "export_append_item: calloc error\n");
255 		return;
256 	}
257 
258 	item->infile = strdup(infile);
259 	basename = g_path_get_basename(infile);
260 	if ((ext = g_strrstr(basename, ".")) != NULL) {
261 		*ext = '\0';
262 	}
263 
264 	item->artist = (artist && artist[0] != '\0') ? strdup(artist) : strdup(_("Unknown Artist"));
265 	item->album = (album && album[0] != '\0') ? strdup(album) : strdup(_("Unknown Album"));
266 	item->title = (title && title[0] != '\0') ? strdup(title) : strdup(_("Unknown Track"));
267 	item->original = basename;
268 	item->year = year;
269 	item->no = no;
270 
271 	export->slist = g_slist_append(export->slist, item);
272 }
273 
274 gboolean
export_finish(gpointer user_data)275 export_finish(gpointer user_data) {
276 
277 	export_t * export = (export_t *)user_data;
278 
279 	gtk_window_resize(GTK_WINDOW(export_window),
280 			  export_window->allocation.width,
281 			  export_window->allocation.height - export->slot->allocation.height);
282 
283 	gtk_widget_destroy(export->slot);
284 	export->slot = NULL;
285 
286 	g_source_remove(export->progbar_tag);
287 
288         export_free(export);
289 
290 	--export_slot_count;
291 
292 	if (export_slot_count == 0) {
293 		unregister_toplevel_window(export_window);
294 		gtk_widget_destroy(export_window);
295 		export_window = NULL;
296 	}
297 
298 	return FALSE;
299 }
300 
301 int
export_item_set_path(export_t * export,export_item_t * item,char * path,char * ext,int index)302 export_item_set_path(export_t * export, export_item_t * item, char * path, char * ext, int index) {
303 
304 	char track[MAXLEN];
305 	char buf[3*MAXLEN];
306 	char str_no[16];
307 	char str_index[16];
308 
309 	track[0] = '\0';
310 	buf[0] = '\0';
311 
312 	strcat(buf, export->outdir);
313 
314 	if (export->dir_for_artist) {
315 		strcat(buf, "/");
316 		strcat(buf, export_map_put(&export->artist_map, item->artist, export->dir_len_limit));
317 		if (!is_dir(buf) && mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
318 			fprintf(stderr, "mkdir: %s: %s\n", buf, strerror(errno));
319 			return -1;
320 		}
321 	}
322 
323 	if (export->dir_for_album) {
324 		strcat(buf, "/");
325 		strcat(buf, export_map_put(&export->record_map, item->album, export->dir_len_limit));
326 		if (!is_dir(buf) && mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
327 			fprintf(stderr, "mkdir: %s: %s\n", buf, strerror(errno));
328 			return -1;
329 		}
330 	}
331 
332 	snprintf(str_no, 15, "%02d", item->no);
333 	snprintf(str_index, 15, "%04d", index);
334 
335 	make_string_va(track, export->template, 'o', item->original, 'a', item->artist,
336 		       'r', item->album, 't', item->title, 'n', str_no, 'x', ext, 'i', str_index, 0);
337 
338 	snprintf(path, MAXLEN-1, "%s/%s", buf, track);
339 	return 0;
340 }
341 
342 gboolean
update_progbar_ratio(gpointer user_data)343 update_progbar_ratio(gpointer user_data) {
344 
345 	export_t * export = (export_t *)user_data;
346 
347 	if (export->slot) {
348 
349 		char tmp[16];
350 
351 		AQUALUNG_MUTEX_LOCK(export->mutex);
352 		snprintf(tmp, 15, "%d%%", (int)(export->ratio * 100));
353 		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(export->progbar), export->ratio);
354 		AQUALUNG_MUTEX_UNLOCK(export->mutex);
355 		gtk_progress_bar_set_text(GTK_PROGRESS_BAR(export->progbar), tmp);
356 	}
357 
358 	return TRUE;
359 }
360 
361 gboolean
set_prog_src_file_entry_idle(gpointer user_data)362 set_prog_src_file_entry_idle(gpointer user_data) {
363 
364 	export_t * export = (export_t *)user_data;
365 
366 	if (export->slot) {
367 
368 		char * utf8 = NULL;
369 
370                 AQUALUNG_MUTEX_LOCK(export->mutex);
371                 utf8 = g_filename_display_name(export->file1);
372                 AQUALUNG_MUTEX_UNLOCK(export->mutex);
373 
374 		gtk_entry_set_text(GTK_ENTRY(export->prog_file_entry1), utf8);
375 		gtk_widget_grab_focus(export->prog_cancel_button);
376 		g_free(utf8);
377 	}
378 
379 	return FALSE;
380 }
381 
382 gboolean
set_prog_trg_file_entry_idle(gpointer user_data)383 set_prog_trg_file_entry_idle(gpointer user_data) {
384 
385 	export_t * export = (export_t *)user_data;
386 
387 	if (export->slot) {
388 
389 		char * utf8 = NULL;
390 
391                 AQUALUNG_MUTEX_LOCK(export->mutex);
392                 utf8 = g_filename_display_name(export->file2);
393                 AQUALUNG_MUTEX_UNLOCK(export->mutex);
394 
395 		gtk_entry_set_text(GTK_ENTRY(export->prog_file_entry2), utf8);
396 		gtk_widget_grab_focus(export->prog_cancel_button);
397 		g_free(utf8);
398 	}
399 
400 	return FALSE;
401 }
402 
403 void
set_prog_src_file_entry(export_t * export,char * file)404 set_prog_src_file_entry(export_t * export, char * file) {
405 
406 	AQUALUNG_MUTEX_LOCK(export->mutex);
407 	strncpy(export->file1, file, MAXLEN-1);
408         AQUALUNG_MUTEX_UNLOCK(export->mutex);
409 
410 	aqualung_idle_add(set_prog_src_file_entry_idle, export);
411 }
412 
413 void
set_prog_trg_file_entry(export_t * export,char * file)414 set_prog_trg_file_entry(export_t * export, char * file) {
415 
416 	AQUALUNG_MUTEX_LOCK(export->mutex);
417 	strncpy(export->file2, file, MAXLEN-1);
418         AQUALUNG_MUTEX_UNLOCK(export->mutex);
419 
420 	aqualung_idle_add(set_prog_trg_file_entry_idle, export);
421 }
422 
423 
424 void
export_meta_amend_frame(metadata_t * meta,int tag,int type,export_item_t * item)425 export_meta_amend_frame(metadata_t * meta, int tag, int type, export_item_t * item) {
426 
427 	char * str;
428 	meta_frame_t * frame;
429 
430 	/* see whether particular frame type is available in this tag */
431 	if (!meta_get_fieldname_embedded(tag, type, &str)) {
432 		return;
433 	}
434 
435 	/* if yes, check for existence */
436 	frame = metadata_get_frame_by_tag_and_type(meta, tag, type, NULL);
437 	if (frame != NULL) {
438 		return;
439 	}
440 
441 	/* not found, add it with content from export item */
442 	frame = meta_frame_new();
443 	frame->tag = tag;
444 	frame->type = type;
445 	switch (type) {
446 	case META_FIELD_TITLE:
447 		frame->field_val = strdup(item->title);
448 		break;
449 	case META_FIELD_ARTIST:
450 		frame->field_val = strdup(item->artist);
451 		break;
452 	case META_FIELD_ALBUM:
453 		frame->field_val = strdup(item->album);
454 		break;
455 	case META_FIELD_DATE:
456 		{
457 			char str_year[6];
458 			snprintf(str_year, 5, "%d", item->year);
459 			frame->field_val = strdup(str_year);
460 		}
461 		break;
462 	case META_FIELD_TRACKNO:
463 		frame->int_val = item->no;
464 		break;
465 	}
466 
467 	metadata_add_frame(meta, frame);
468 }
469 
470 
471 /* Add metadata fields stored in Music Store / Playlist
472  * if they were not transferred from source file metadata.
473  */
474 void
export_meta_amend_stored_fields(metadata_t * meta,int tags,export_item_t * item)475 export_meta_amend_stored_fields(metadata_t * meta, int tags, export_item_t * item) {
476 
477 	int tag = META_TAG_MAX;
478 
479 	/* iterate on possible output tags */
480 	while (tag) {
481 
482 		if ((tags & tag) == 0) {
483 			tag >>= 1;
484 			continue;
485 		}
486 
487 		if (strcmp(item->title, _("Unknown Track")) != 0) {
488 			export_meta_amend_frame(meta, tag, META_FIELD_TITLE, item);
489 		}
490 		if (strcmp(item->artist, _("Unknown Artist")) != 0) {
491 			export_meta_amend_frame(meta, tag, META_FIELD_ARTIST, item);
492 		}
493 		if (strcmp(item->album, _("Unknown Album")) != 0) {
494 			export_meta_amend_frame(meta, tag, META_FIELD_ALBUM, item);
495 		}
496 		if (item->year != 0) {
497 			export_meta_amend_frame(meta, tag, META_FIELD_DATE, item);
498 		}
499 		if (item->no != 0) {
500 			export_meta_amend_frame(meta, tag, META_FIELD_TRACKNO, item);
501 		}
502 
503 		tag >>= 1;
504 	}
505 }
506 
507 
508 void
export_item(export_t * export,export_item_t * item,int index)509 export_item(export_t * export, export_item_t * item, int index) {
510 
511 	file_decoder_t * fdec;
512 	file_encoder_t * fenc;
513 	encoder_mode_t mode;
514 	char * ext = "raw";
515 	char filename[MAXLEN];
516 	int tags = 0;
517 	int force_copy = 0;
518 
519 	float buf[2*BUFSIZE];
520 	int n_read;
521 	long long samples_read = 0;
522 
523 	memset(&mode, 0, sizeof(encoder_mode_t));
524 
525 	switch (export->format) {
526 	case ENC_SNDFILE_LIB:
527 		ext = "wav";
528 		tags = 0;
529 		break;
530 	case ENC_FLAC_LIB:
531 		ext = "flac";
532 		tags = META_TAG_OXC | META_TAG_FLAC_APIC;
533 		break;
534 	case ENC_VORBIS_LIB:
535 		ext = "ogg";
536 		tags = META_TAG_OXC;
537 		break;
538 	case ENC_LAME_LIB:
539 		ext = "mp3";
540 		tags = META_TAG_ID3v1 | META_TAG_ID3v2 | META_TAG_APE;
541 		break;
542 	}
543 
544 
545 	fdec = file_decoder_new();
546 
547 	if (file_decoder_open(fdec, item->infile)) {
548 		return;
549 	}
550 
551 	if (export->filter_same) {
552 		if ((fdec->file_lib == FLAC_LIB && export->format == ENC_FLAC_LIB) ||
553 		    (fdec->file_lib == VORBIS_LIB && export->format == ENC_VORBIS_LIB) ||
554 		    (fdec->file_lib == MAD_LIB && export->format == ENC_LAME_LIB)) {
555 			force_copy = 1;
556 		}
557 	}
558 
559 	if (export->excl_enabled) {
560 		char * utf8 = g_filename_display_name(item->infile);
561 		int i;
562 
563 		for (i = 0; export->excl_patternv[i]; i++) {
564 
565 			if (*(export->excl_patternv[i]) == '\0') {
566 				continue;
567 			}
568 
569 			if (fnmatch(export->excl_patternv[i], utf8, FNM_CASEFOLD) == 0) {
570 				force_copy = 1;
571 				break;
572 			}
573 		}
574 
575 		g_free(utf8);
576 	}
577 
578 	if (force_copy || export->format == ENC_COPY) {
579 		if ((ext = strrchr(item->infile, '.')) == NULL) {
580 			ext = "";
581 		} else {
582 			++ext;
583 		}
584 	}
585 
586 	if (export_item_set_path(export, item, filename, ext, index) < 0) {
587 		file_decoder_close(fdec);
588 		file_decoder_delete(fdec);
589 		return;
590 	}
591 
592 	set_prog_src_file_entry(export, item->infile);
593 	set_prog_trg_file_entry(export, filename);
594 
595 	if (force_copy || export->format == ENC_COPY) {
596 
597 		char buf[BUFSIZE];
598 		size_t n_read;
599 		struct stat statbuf;
600 		unsigned long pos = 0;
601 		int n = 0;
602 		FILE * fi;
603 		FILE * fo;
604 
605 		file_decoder_close(fdec);
606 		file_decoder_delete(fdec);
607 
608 		if (g_stat(item->infile, &statbuf) != -1) {
609 			if (statbuf.st_size == 0) {
610 				return;
611 			}
612 		}
613 
614 		if ((fi = fopen(item->infile, "rb")) == NULL) {
615 			fprintf(stdout, "export_item: unable to open file %s\n", item->infile);
616 			return;
617 		}
618 
619 		if ((fo = fopen(filename, "wb")) == NULL) {
620 			fclose(fi);
621 			fprintf(stdout, "export_item: unable to open file %s\n", filename);
622 			return;
623 		}
624 
625 		while ((n_read = fread(buf, sizeof(char), sizeof(buf), fi)) > 0 && !export->cancelled) {
626 			fwrite(buf, sizeof(char), n_read, fo);
627 			pos += n_read;
628 
629 			if (n-- == 0) {
630 				n = 100;
631 				AQUALUNG_MUTEX_LOCK(export->mutex);
632 				export->ratio = (double)pos / statbuf.st_size;
633 				AQUALUNG_MUTEX_UNLOCK(export->mutex);
634 			}
635 		}
636 
637 		fclose(fi);
638 		fclose(fo);
639 		return;
640 	}
641 
642 
643 	strncpy(mode.filename, filename, MAXLEN-1);
644 	mode.file_lib = export->format;
645 	mode.sample_rate = fdec->fileinfo.sample_rate;
646 	mode.channels = fdec->fileinfo.channels;
647 
648 	if (mode.file_lib == ENC_FLAC_LIB) {
649 		mode.clevel = export->bitrate;
650 	} else if (mode.file_lib == ENC_VORBIS_LIB) {
651 		mode.bps = export->bitrate * 1000;
652 	} else if (mode.file_lib == ENC_LAME_LIB) {
653 		mode.bps = export->bitrate * 1000;
654 		mode.vbr = export->vbr;
655 	}
656 
657 	mode.write_meta = export->write_meta;
658 	if (mode.write_meta) {
659 		if (fdec->meta == NULL) {
660 			mode.meta = metadata_new();
661 		} else {
662 			mode.meta = metadata_clone(fdec->meta, tags);
663 		}
664 		export_meta_amend_stored_fields(mode.meta, tags, item);
665 	}
666 
667 	fenc = file_encoder_new();
668 
669 	if (file_encoder_open(fenc, &mode)) {
670 		return;
671 	}
672 
673 	while (!export->cancelled) {
674 
675 		n_read = file_decoder_read(fdec, buf, BUFSIZE);
676 		file_encoder_write(fenc, buf, n_read);
677 
678 		samples_read += n_read;
679 
680 		AQUALUNG_MUTEX_LOCK(export->mutex);
681 		export->ratio = (double)samples_read / fdec->fileinfo.total_samples;
682 		AQUALUNG_MUTEX_UNLOCK(export->mutex);
683 
684 		if (n_read < BUFSIZE) {
685 			break;
686 		}
687 	}
688 
689 	file_decoder_close(fdec);
690 	file_encoder_close(fenc);
691 	file_decoder_delete(fdec);
692 	file_encoder_delete(fenc);
693 	if (mode.meta != NULL) {
694 		metadata_free(mode.meta);
695 	}
696 }
697 
698 void *
export_thread(void * arg)699 export_thread(void * arg) {
700 
701 	export_t * export = (export_t *)arg;
702 	GSList * node;
703 	int index = 0;
704 
705 	AQUALUNG_THREAD_DETACH();
706 
707 	for (node = export->slist; node; node = node->next) {
708 
709 		if (export->cancelled) {
710 			break;
711 		}
712 
713 		++index;
714 		export_item(export, (export_item_t *)node->data, index);
715 	}
716 
717 	aqualung_idle_add(export_finish, export);
718 
719 	return NULL;
720 }
721 
722 void
export_browse_cb(GtkButton * button,gpointer data)723 export_browse_cb(GtkButton * button, gpointer data) {
724 
725 	file_chooser_with_entry(_("Please select the directory for exported files."),
726 				main_window,
727 				GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
728 				FILE_CHOOSER_FILTER_NONE,
729 				(GtkWidget *)data,
730 				options.exportdir);
731 }
732 
733 GtkWidget *
export_create_format_combo(void)734 export_create_format_combo(void) {
735 
736 	GtkWidget * combo = gtk_combo_box_new_text();
737 	int n = -1;
738 
739 #ifdef HAVE_SNDFILE_ENC
740 	gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "WAV");
741 	++n;
742 #endif /* HAVE_SNDFILE_ENC */
743 #ifdef HAVE_FLAC_ENC
744 	gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "FLAC");
745 	++n;
746 #endif /* HAVE_FLAC_ENC */
747 #ifdef HAVE_VORBISENC
748 	gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Ogg Vorbis");
749 	++n;
750 #endif /* HAVE_VORBISENC */
751 #ifdef HAVE_LAME
752 	gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "MP3");
753 	++n;
754 #endif /* HAVE_LAME */
755 	gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Copy"));
756 	++n;
757 
758 	if (n >= options.export_file_format) {
759 		gtk_combo_box_set_active(GTK_COMBO_BOX(combo), options.export_file_format);
760 	} else {
761 		gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
762 	}
763 
764 	return combo;
765 }
766 
767 
768 /* returns file_lib value */
769 int
export_get_format_from_combo(GtkWidget * combo)770 export_get_format_from_combo(GtkWidget * combo) {
771 
772 	int file_lib = -1;
773 	gchar * text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
774 	if (strcmp(text, "WAV") == 0) {
775 		file_lib = ENC_SNDFILE_LIB;
776 	}
777 	if (strcmp(text, "FLAC") == 0) {
778 		file_lib = ENC_FLAC_LIB;
779 	}
780 	if (strcmp(text, "Ogg Vorbis") == 0) {
781 		file_lib = ENC_VORBIS_LIB;
782 	}
783 	if (strcmp(text, "MP3") == 0) {
784 		file_lib = ENC_LAME_LIB;
785 	}
786 	if (strcmp(text, _("Copy")) == 0) {
787 		file_lib = ENC_COPY;
788 	}
789 	g_free(text);
790 	return file_lib;
791 }
792 
793 void
export_format_combo_changed(GtkWidget * widget,gpointer data)794 export_format_combo_changed(GtkWidget * widget, gpointer data) {
795 
796 	export_t * export = (export_t *)data;
797 	gchar * text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget));
798 
799 	if (strcmp(text, "WAV") == 0 || strcmp(text, _("Copy")) == 0) {
800 		gtk_widget_hide(export->bitrate_scale);
801 		gtk_widget_hide(export->bitrate_label);
802 		gtk_widget_hide(export->bitrate_value_label);
803 		gtk_widget_hide(export->vbr_check);
804 		gtk_widget_hide(export->meta_check);
805 	}
806 	if (strcmp(text, "FLAC") == 0) {
807 		gtk_widget_show(export->bitrate_scale);
808 		gtk_widget_show(export->bitrate_label);
809 		gtk_label_set_text(GTK_LABEL(export->bitrate_label), _("Compression level:"));
810 		gtk_widget_show(export->bitrate_value_label);
811 		gtk_widget_hide(export->vbr_check);
812 		gtk_widget_show(export->meta_check);
813 
814 		gtk_range_set_range(GTK_RANGE(export->bitrate_scale), 0, 8);
815 		gtk_range_set_value(GTK_RANGE(export->bitrate_scale), options.export_bitrate);
816 	}
817 	if (strcmp(text, "Ogg Vorbis") == 0) {
818 		gtk_widget_show(export->bitrate_scale);
819 		gtk_widget_show(export->bitrate_label);
820 		gtk_label_set_text(GTK_LABEL(export->bitrate_label), _("Bitrate [kbps]:"));
821 		gtk_widget_show(export->bitrate_value_label);
822 		gtk_widget_hide(export->vbr_check);
823 		gtk_widget_show(export->meta_check);
824 
825 		gtk_range_set_range(GTK_RANGE(export->bitrate_scale), 32, 320);
826 		gtk_range_set_value(GTK_RANGE(export->bitrate_scale), options.export_bitrate);
827 	}
828 	if (strcmp(text, "MP3") == 0) {
829 		gtk_widget_show(export->bitrate_scale);
830 		gtk_widget_show(export->bitrate_label);
831 		gtk_label_set_text(GTK_LABEL(export->bitrate_label), _("Bitrate [kbps]:"));
832 		gtk_widget_show(export->bitrate_value_label);
833 		gtk_widget_show(export->vbr_check);
834 		gtk_widget_show(export->meta_check);
835 
836 		gtk_range_set_range(GTK_RANGE(export->bitrate_scale), 32, 320);
837 		gtk_range_set_value(GTK_RANGE(export->bitrate_scale), options.export_bitrate);
838 	}
839 
840         options.export_file_format = export_get_format_from_combo(widget);
841 
842 	g_free(text);
843 }
844 
845 void
export_bitrate_changed(GtkRange * range,gpointer data)846 export_bitrate_changed(GtkRange * range, gpointer data) {
847 
848 	export_t * export = (export_t *)data;
849 	float val = gtk_range_get_value(range);
850 	gchar * text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(export->format_combo));
851 
852 	if (strcmp(text, "FLAC") == 0) {
853 		int i = (int)val;
854 		char str[256];
855 		switch (i) {
856 		case 0:
857 		case 8:
858 			snprintf(str, 255, "%d (%s)", i, (i == 0) ? _("fast") : _("best"));
859 			gtk_label_set_text(GTK_LABEL(export->bitrate_value_label), str);
860 			break;
861 		default:
862 			snprintf(str, 255, "%d", i);
863 			gtk_label_set_text(GTK_LABEL(export->bitrate_value_label), str);
864 			break;
865 		}
866 	}
867 	if (strcmp(text, "Ogg Vorbis") == 0) {
868 		int i = (int)val;
869 		char str[256];
870 		snprintf(str, 255, "%d", i);
871 		gtk_label_set_text(GTK_LABEL(export->bitrate_value_label), str);
872 	}
873 	if (strcmp(text, "MP3") == 0) {
874 		int i = (int)val;
875 		char str[256];
876 #ifdef HAVE_LAME
877 		i = lame_encoder_validate_bitrate(i, 0);
878 #endif /* HAVE_LAME */
879 		snprintf(str, 255, "%d", i);
880 		gtk_label_set_text(GTK_LABEL(export->bitrate_value_label), str);
881 	}
882 	g_free(text);
883 }
884 
885 void
check_dir_limit_toggled(GtkToggleButton * toggle,gpointer data)886 check_dir_limit_toggled(GtkToggleButton * toggle, gpointer data) {
887 
888 	export_t * export = (export_t *)data;
889 
890 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(export->check_dir_artist)) ||
891 	    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(export->check_dir_album))) {
892 		gtk_widget_set_sensitive(export->dirlen_spin, TRUE);
893 	} else {
894 		gtk_widget_set_sensitive(export->dirlen_spin, FALSE);
895 	}
896 }
897 
898 void
export_format_help_cb(GtkButton * button,gpointer user_data)899 export_format_help_cb(GtkButton * button, gpointer user_data) {
900 
901 	message_dialog(_("Help"),
902 		       ((export_t *)user_data)->dialog,
903 		       GTK_MESSAGE_INFO,
904 		       GTK_BUTTONS_OK,
905 		       NULL,
906 		       _("\nThe template string you enter here will be used to "
907 			 "construct the filename of the exported files. The Original "
908 			 "filename, Artist, Record and Track names are denoted by %%o, "
909 			 "%%a, %%r and %%t. The track number and format-dependent "
910 			 "file extension are denoted by %%n and %%x, respectively. "
911 			 "%%i gives an identifier which is unique within an export "
912 			 "session."));
913 }
914 
915 void
export_check_excl_toggled(GtkWidget * widget,gpointer data)916 export_check_excl_toggled(GtkWidget * widget, gpointer data) {
917 
918 	gboolean state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
919 	gtk_widget_set_sensitive((GtkWidget *)data, state);
920 }
921 
922 gboolean
export_window_event(GtkWidget * widget,GdkEvent * event,gpointer * data)923 export_window_event(GtkWidget * widget, GdkEvent * event, gpointer * data) {
924 
925 	if (event->type == GDK_DELETE) {
926 		gtk_window_iconify(GTK_WINDOW(export_window));
927 		return TRUE;
928 	}
929 
930 	return FALSE;
931 }
932 
933 void
export_cancel_event(GtkButton * button,gpointer data)934 export_cancel_event(GtkButton * button, gpointer data) {
935 
936 	export_t * export = (export_t *)data;
937 
938         export->cancelled = 1;
939 }
940 
941 void
create_export_window()942 create_export_window() {
943 
944 	GtkWidget * vbox;
945 
946 	export_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
947 	register_toplevel_window(export_window, TOP_WIN_SKIN | TOP_WIN_TRAY);
948         gtk_window_set_title(GTK_WINDOW(export_window), _("Exporting files"));
949         gtk_window_set_position(GTK_WINDOW(export_window), GTK_WIN_POS_CENTER);
950 	gtk_window_set_transient_for(GTK_WINDOW(export_window), GTK_WINDOW(main_window));
951 	gtk_window_set_type_hint(GTK_WINDOW(export_window), GDK_WINDOW_TYPE_HINT_DIALOG);
952 
953         gtk_window_resize(GTK_WINDOW(export_window), 480, 110);
954         g_signal_connect(G_OBJECT(export_window), "event",
955                          G_CALLBACK(export_window_event), NULL);
956 
957         gtk_container_set_border_width(GTK_CONTAINER(export_window), 5);
958 
959         vbox = gtk_vbox_new(FALSE, 0);
960         gtk_container_add(GTK_CONTAINER(export_window), vbox);
961 
962         gtk_widget_show_all(export_window);
963 }
964 
965 void
export_progress_window(export_t * export)966 export_progress_window(export_t * export) {
967 
968 	GtkWidget * vbox;
969 
970 	++export_slot_count;
971 
972 	if (export_window == NULL) {
973 		create_export_window();
974 	}
975 
976 	vbox = gtk_bin_get_child(GTK_BIN(export_window));
977 
978 	export->slot = gtk_table_new(5, 2, FALSE);
979         gtk_box_pack_start(GTK_BOX(vbox), export->slot, FALSE, FALSE, 0);
980 
981 	gtk_table_attach(GTK_TABLE(export->slot), gtk_hseparator_new(), 0, 2, 0, 1,
982 			 GTK_FILL, GTK_FILL, 5, 5);
983 
984 	insert_label_entry(export->slot, _("Source file:"), &export->prog_file_entry1, NULL, 1, 2, FALSE);
985 	insert_label_entry(export->slot, _("Target file:"), &export->prog_file_entry2, NULL, 2, 3, FALSE);
986 
987         export->prog_cancel_button = gui_stock_label_button(_("Abort"), GTK_STOCK_CANCEL);
988         g_signal_connect(export->prog_cancel_button, "clicked", G_CALLBACK(export_cancel_event), export);
989 	insert_label_progbar_button(export->slot, _("Progress:"), &export->progbar, export->prog_cancel_button, 3, 4);
990 
991 	gtk_table_attach(GTK_TABLE(export->slot), gtk_hseparator_new(), 0, 2, 4, 5,
992 			 GTK_FILL, GTK_FILL, 5, 5);
993 
994         gtk_widget_grab_focus(export->prog_cancel_button);
995 
996         gtk_widget_show_all(export->slot);
997 }
998 
999 gboolean
export_dialog_close(GtkWidget * widget,GdkEvent * event,gpointer ex)1000 export_dialog_close(GtkWidget * widget, GdkEvent * event, gpointer ex) {
1001 	export_t * export = ex;
1002 	gtk_widget_destroy(export->dialog);
1003 	return TRUE;
1004 }
1005 
1006 gboolean
export_dialog_response(GtkDialog * dialog,gint response_id,gpointer ex)1007 export_dialog_response(GtkDialog * dialog, gint response_id, gpointer ex) {
1008 	export_t * export = ex;
1009 	char * poutdir = g_filename_from_utf8(gtk_entry_get_text(GTK_ENTRY(export->outdir_entry)), -1, NULL, NULL, NULL);
1010 
1011 	if (response_id != GTK_RESPONSE_ACCEPT) {
1012 		gtk_widget_destroy(export->dialog);
1013 		return TRUE;
1014 	}
1015 
1016 	if ((poutdir == NULL) || (poutdir[0] == '\0')) {
1017 		gtk_widget_grab_focus(export->outdir_entry);
1018 		g_free(poutdir);
1019 		return FALSE;
1020 	}
1021 
1022 	normalize_filename(poutdir, export->outdir);
1023 	g_free(poutdir);
1024 
1025 	if (strlen(gtk_entry_get_text(GTK_ENTRY(export->templ_entry))) == 0) {
1026 		gtk_widget_grab_focus(export->templ_entry);
1027 		return FALSE;
1028 	} else {
1029 		int ret;
1030 		char buf[MAXLEN];
1031 		char * format = (char *)gtk_entry_get_text(GTK_ENTRY(export->templ_entry));
1032 		if ((ret = make_string_va(buf, format,
1033 					  'o', "o", 'a', "a", 'r', "r", 't', "t", 'n', "n", 'x', "x", 'i', "i", 0)) != 0) {
1034 			make_string_strerror(ret, buf);
1035 			message_dialog(_("Error in format string"),
1036 				       export->dialog,
1037 				       GTK_MESSAGE_ERROR,
1038 				       GTK_BUTTONS_OK,
1039 				       NULL,
1040 				       buf);
1041 			return FALSE;
1042 		}
1043 	}
1044 
1045 	if (access(export->outdir, R_OK | W_OK) != 0) {
1046 		message_dialog(_("Error"),
1047 			       export->dialog,
1048 			       GTK_MESSAGE_ERROR,
1049 			       GTK_BUTTONS_OK,
1050 			       NULL,
1051 			       _("\nDestination directory is not read-write accessible!"));
1052 
1053 		gtk_widget_grab_focus(export->outdir_entry);
1054 		return FALSE;
1055 	}
1056 
1057 	strncpy(options.exportdir, export->outdir, MAXLEN-1);
1058 	set_option_from_entry(export->templ_entry, export->template, MAXLEN);
1059 	set_option_from_entry(export->templ_entry, options.export_template, MAXLEN);
1060 	options.export_file_format = export->format = export_get_format_from_combo(export->format_combo);
1061 	options.export_bitrate = export->bitrate = gtk_range_get_value(GTK_RANGE(export->bitrate_scale));
1062 	set_option_from_toggle(export->check_dir_artist, &options.export_subdir_artist);
1063 	set_option_from_toggle(export->check_dir_album, &options.export_subdir_album);
1064 	set_option_from_spin(export->dirlen_spin, &options.export_subdir_limit);
1065 	set_option_from_toggle(export->vbr_check, &export->vbr);
1066 	options.export_vbr = export->vbr;
1067 	set_option_from_toggle(export->meta_check, &export->write_meta);
1068 	options.export_metadata = export->write_meta;
1069 	set_option_from_toggle(export->check_dir_artist, &export->dir_for_artist);
1070 	set_option_from_toggle(export->check_dir_album, &export->dir_for_album);
1071 
1072 	set_option_from_toggle(export->check_filter_same, &export->filter_same);
1073 	options.export_filter_same = export->filter_same;
1074 	set_option_from_toggle(export->check_excl_enabled, &export->excl_enabled);
1075 	options.export_excl_enabled = export->excl_enabled;
1076 
1077 	if (export->excl_enabled) {
1078 		set_option_from_entry(export->excl_entry, options.export_excl_pattern, MAXLEN);
1079 		export->excl_patternv =
1080 			g_strsplit(gtk_entry_get_text(GTK_ENTRY(export->excl_entry)), ",", 0);
1081 	}
1082 
1083 	if (export->dir_for_artist || export->dir_for_album) {
1084 		set_option_from_spin(export->dirlen_spin, &export->dir_len_limit);
1085 	} else {
1086 		export->dir_len_limit = MAXLEN-1;
1087 	}
1088 
1089 	gtk_widget_destroy(export->dialog);
1090 
1091 	export_progress_window(export);
1092 	export->progbar_tag = aqualung_timeout_add(250, update_progbar_ratio, export);
1093 	AQUALUNG_THREAD_CREATE(export->thread_id, NULL, export_thread, export);
1094 
1095 	return TRUE;
1096 }
1097 
1098 void
export_start(export_t * export)1099 export_start(export_t * export) {
1100 
1101 	GtkWidget * content_area;
1102 
1103 	GtkWidget * help_button;
1104 
1105         GtkWidget * table;
1106         GtkWidget * hbox;
1107         GtkWidget * frame;
1108 
1109         export->dialog = gtk_dialog_new_with_buttons(_("Export files"),
1110 						     GTK_WINDOW(main_window),
1111 						     GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
1112 						     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1113 						     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1114 						     NULL);
1115 
1116         gtk_window_set_position(GTK_WINDOW(export->dialog), GTK_WIN_POS_CENTER);
1117         gtk_window_set_default_size(GTK_WINDOW(export->dialog), 400, -1);
1118         gtk_dialog_set_default_response(GTK_DIALOG(export->dialog), GTK_RESPONSE_REJECT);
1119 
1120 	content_area = gtk_dialog_get_content_area(GTK_DIALOG(export->dialog));
1121 
1122 	g_signal_connect(G_OBJECT(export->dialog), "response",
1123 			 G_CALLBACK(export_dialog_response), (gpointer)export);
1124 	g_signal_connect(G_OBJECT(export->dialog), "delete_event",
1125 			 G_CALLBACK(export_dialog_close), (gpointer)export);
1126 
1127 	/* Location and filename */
1128 	frame = gtk_frame_new(_("Location and filename"));
1129 	gtk_box_pack_start(GTK_BOX(content_area), frame, FALSE, FALSE, 2);
1130         gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
1131 
1132 	table = gtk_table_new(5, 2, FALSE);
1133         gtk_container_add(GTK_CONTAINER(frame), table);
1134 
1135 	insert_label_entry_browse(table, _("Target directory:"), &export->outdir_entry, options.exportdir, 0, 1, export_browse_cb);
1136 
1137         export->check_dir_artist = gtk_check_button_new_with_label(_("Create subdirectories for artists"));
1138         gtk_widget_set_name(export->check_dir_artist, "check_on_notebook");
1139 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(export->check_dir_artist), options.export_subdir_artist);
1140         gtk_table_attach(GTK_TABLE(table), export->check_dir_artist, 0, 3, 1, 2, GTK_FILL, GTK_FILL, 5, 5);
1141         g_signal_connect(G_OBJECT(export->check_dir_artist), "toggled",
1142 			 G_CALLBACK(check_dir_limit_toggled), export);
1143 
1144         export->check_dir_album = gtk_check_button_new_with_label(_("Create subdirectories for albums"));
1145         gtk_widget_set_name(export->check_dir_album, "check_on_notebook");
1146 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(export->check_dir_album), options.export_subdir_album);
1147         gtk_table_attach(GTK_TABLE(table), export->check_dir_album, 0, 3, 2, 3, GTK_FILL, GTK_FILL, 5, 5);
1148         g_signal_connect(G_OBJECT(export->check_dir_album), "toggled",
1149 			 G_CALLBACK(check_dir_limit_toggled), export);
1150 
1151 	insert_label_spin_with_limits(table, _("Subdirectory name\nlength limit:"), &export->dirlen_spin, options.export_subdir_limit, 4, 64, 3, 4);
1152 	gtk_widget_set_sensitive(export->dirlen_spin, options.export_subdir_artist || options.export_subdir_album);
1153 
1154         help_button = gtk_button_new_from_stock(GTK_STOCK_HELP);
1155 	g_signal_connect(help_button, "clicked", G_CALLBACK(export_format_help_cb), export);
1156 	insert_label_entry_button(table, _("Filename template:"), &export->templ_entry, options.export_template, help_button, 4, 5);
1157 
1158 	/* Format */
1159 	frame = gtk_frame_new(_("Format"));
1160 	gtk_box_pack_start(GTK_BOX(content_area), frame, FALSE, FALSE, 2);
1161         gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
1162 
1163         table = gtk_table_new(4, 2, FALSE);
1164         gtk_container_add(GTK_CONTAINER(frame), table);
1165 
1166         hbox = gtk_hbox_new(FALSE, 0);
1167         gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("File format:")), FALSE, FALSE, 0);
1168         gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 5, 5);
1169 
1170 	export->format_combo = export_create_format_combo();
1171         g_signal_connect(G_OBJECT(export->format_combo), "changed",
1172 			 G_CALLBACK(export_format_combo_changed), export);
1173         gtk_table_attach(GTK_TABLE(table), export->format_combo, 1, 2, 0, 1,
1174 			 GTK_EXPAND | GTK_FILL, GTK_FILL, 5, 5);
1175 
1176         hbox = gtk_hbox_new(FALSE, 0);
1177 	export->bitrate_label = gtk_label_new(_("Compression level:"));
1178         gtk_box_pack_start(GTK_BOX(hbox), export->bitrate_label, FALSE, FALSE, 0);
1179         gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 5, 5);
1180 
1181 	export->bitrate_scale = gtk_hscale_new_with_range(0, 8, 1);
1182         g_signal_connect(G_OBJECT(export->bitrate_scale), "value-changed",
1183 			 G_CALLBACK(export_bitrate_changed), export);
1184 	gtk_scale_set_draw_value(GTK_SCALE(export->bitrate_scale), FALSE);
1185 	gtk_scale_set_digits(GTK_SCALE(export->bitrate_scale), 0);
1186         gtk_widget_set_size_request(export->bitrate_scale, 180, -1);
1187         gtk_table_attach(GTK_TABLE(table), export->bitrate_scale, 1, 2, 1, 2,
1188 			 GTK_EXPAND | GTK_FILL, GTK_FILL, 5, 5);
1189 
1190 	export->bitrate_value_label = gtk_label_new("0 (fast)");
1191         gtk_table_attach(GTK_TABLE(table), export->bitrate_value_label, 1, 2, 2, 3,
1192 			 GTK_EXPAND | GTK_FILL, GTK_FILL, 5, 5);
1193 
1194         export->vbr_check = gtk_check_button_new_with_label(_("VBR encoding"));
1195         gtk_widget_set_name(export->vbr_check, "check_on_notebook");
1196         gtk_table_attach(GTK_TABLE(table), export->vbr_check, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 5, 5);
1197 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(export->vbr_check), options.export_vbr);
1198 
1199         export->meta_check = gtk_check_button_new_with_label(_("Tag files with metadata"));
1200         gtk_widget_set_name(export->meta_check, "check_on_notebook");
1201         gtk_table_attach(GTK_TABLE(table), export->meta_check, 0, 2, 3, 4,
1202 			 GTK_FILL, GTK_FILL, 5, 5);
1203 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(export->meta_check), options.export_metadata);
1204 
1205 
1206 	/* Filter */
1207 	frame = gtk_frame_new(_("Filter"));
1208 	gtk_box_pack_start(GTK_BOX(content_area), frame, FALSE, FALSE, 2);
1209         gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
1210 
1211 	table = gtk_table_new(1, 2, FALSE);
1212         gtk_container_add(GTK_CONTAINER(frame), table);
1213 
1214         export->check_filter_same = gtk_check_button_new_with_label(_("Do not reencode files already being in the target format"));
1215         gtk_widget_set_name(export->check_filter_same, "check_on_notebook");
1216 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(export->check_filter_same), options.export_filter_same);
1217         gtk_table_attach(GTK_TABLE(table), export->check_filter_same, 0, 2, 0, 1, GTK_FILL, GTK_FILL, 5, 5);
1218 
1219         export->check_excl_enabled = gtk_check_button_new_with_label(_("Do not reencode files\nmatching wildcard:"));
1220         gtk_widget_set_name(export->check_excl_enabled, "check_on_notebook");
1221 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(export->check_excl_enabled), options.export_excl_enabled);
1222         gtk_table_attach(GTK_TABLE(table), export->check_excl_enabled, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 5, 5);
1223 
1224         export->excl_entry = gtk_entry_new();
1225         gtk_entry_set_max_length(GTK_ENTRY(export->excl_entry), MAXLEN-1);
1226 	gtk_entry_set_text(GTK_ENTRY(export->excl_entry), options.export_excl_pattern);
1227         gtk_table_attach(GTK_TABLE(table), export->excl_entry, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 5, 5);
1228 	gtk_widget_set_sensitive(export->excl_entry, options.export_excl_enabled);
1229 
1230 	g_signal_connect(G_OBJECT(export->check_excl_enabled), "toggled",
1231 			 G_CALLBACK(export_check_excl_toggled), export->excl_entry);
1232 
1233 
1234 	gtk_widget_show_all(export->dialog);
1235 	export_format_combo_changed(export->format_combo, export);
1236 
1237 	export->outdir[0] = '\0';
1238 	return;
1239 }
1240 
1241 // vim: shiftwidth=8:tabstop=8:softtabstop=8 :
1242 
1243