1 /*
2 * Copyright (C) 2018-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "audio/engine.h"
21 #include "audio/exporter.h"
22 #include "audio/master_track.h"
23 #include "gui/widgets/dialogs/export_dialog.h"
24 #include "gui/widgets/dialogs/export_progress_dialog.h"
25 #include "gui/widgets/main_window.h"
26 #include "project.h"
27 #include "utils/arrays.h"
28 #include "utils/color.h"
29 #include "utils/datetime.h"
30 #include "utils/flags.h"
31 #include "utils/gtk.h"
32 #include "utils/io.h"
33 #include "utils/mem.h"
34 #include "utils/objects.h"
35 #include "utils/resources.h"
36 #include "utils/ui.h"
37 #include "settings/settings.h"
38 #include "zrythm_app.h"
39
40 #include <glib/gi18n.h>
41 #include <gtk/gtk.h>
42
43 G_DEFINE_TYPE (
44 ExportDialogWidget,
45 export_dialog_widget,
46 GTK_TYPE_DIALOG)
47
48 enum
49 {
50 COLUMN_AUDIO_FORMAT_LABEL,
51 COLUMN_AUDIO_FORMAT,
52 NUM_AUDIO_FORMAT_COLUMNS
53 };
54
55 enum
56 {
57 FILENAME_PATTERN_MIXDOWN_FORMAT,
58 FILENAME_PATTERN_DATE_MIXDOWN_FORMAT,
59 NUM_FILENAME_PATTERNS,
60 };
61
62 enum
63 {
64 TRACK_COLUMN_CHECKBOX,
65 TRACK_COLUMN_ICON,
66 TRACK_COLUMN_BG_RGBA,
67 TRACK_COLUMN_DUMMY_TEXT,
68 TRACK_COLUMN_NAME,
69 TRACK_COLUMN_TRACK,
70 NUM_TRACK_COLUMNS,
71 };
72
73 static const char * dummy_text = "";
74
75 static void
add_enabled_recursively(ExportDialogWidget * self,GtkTreeIter * iter,Track *** tracks,size_t * size,int * count)76 add_enabled_recursively (
77 ExportDialogWidget * self,
78 GtkTreeIter * iter,
79 Track *** tracks,
80 size_t * size,
81 int * count)
82 {
83 GtkTreeModel * model =
84 GTK_TREE_MODEL (self->tracks_store);
85
86 Track * track;
87 gboolean checked;
88 gtk_tree_model_get (
89 model, iter,
90 TRACK_COLUMN_CHECKBOX, &checked,
91 TRACK_COLUMN_TRACK, &track,
92 -1);
93 if (checked)
94 {
95 array_double_size_if_full (
96 *tracks, *count, *size, Track *);
97 array_append (*tracks, *count, track);
98 g_debug ("added %s", track->name);
99 }
100
101 /* if group track, also check children
102 * recursively */
103 bool has_children =
104 gtk_tree_model_iter_has_child (model, iter);
105 if (has_children)
106 {
107 int num_children =
108 gtk_tree_model_iter_n_children (
109 model, iter);
110
111 for (int i = 0; i < num_children; i++)
112 {
113 GtkTreeIter child_iter;
114 gtk_tree_model_iter_nth_child (
115 model, &child_iter, iter, i);
116
117 /* recurse */
118 add_enabled_recursively (
119 self, &child_iter, tracks, size, count);
120 }
121 }
122 }
123
124 /**
125 * Returns the currently checked tracks.
126 *
127 * Must be free'd with free().
128 *
129 * @param[out] num_tracks Number of tracks returned.
130 *
131 * @return Newly allocated track array, or NULL if
132 * no tracks selected.
133 */
134 static Track **
get_enabled_tracks(ExportDialogWidget * self,int * num_tracks)135 get_enabled_tracks (
136 ExportDialogWidget * self,
137 int * num_tracks)
138 {
139 size_t size = 1;
140 int count = 0;
141 Track ** tracks = object_new_n (size, Track *);
142
143 GtkTreeModel * model =
144 GTK_TREE_MODEL (self->tracks_store);
145 GtkTreeIter iter;
146 gtk_tree_model_get_iter_first (
147 model, &iter);
148
149 add_enabled_recursively (
150 self, &iter, &tracks, &size, &count);
151
152 if (count == 0)
153 {
154 *num_tracks = 0;
155 free (tracks);
156 return NULL;
157 }
158 else
159 {
160 *num_tracks = count;
161 return tracks;
162 }
163 }
164
165 /**
166 * Returns the currently selected tracks.
167 *
168 * Must be free'd with free().
169 *
170 * @param[out] num_tracks Number of tracks returned.
171 *
172 * @return Newly allocated track array, or NULL if
173 * no tracks selected.
174 */
175 static Track **
get_selected_tracks(ExportDialogWidget * self,int * num_tracks)176 get_selected_tracks (
177 ExportDialogWidget * self,
178 int * num_tracks)
179 {
180 GtkTreeSelection * selection =
181 gtk_tree_view_get_selection (
182 (self->tracks_treeview));
183
184 size_t size = 1;
185 int count = 0;
186 Track ** tracks =
187 calloc (size, sizeof (Track *));
188
189 GList * selected_rows =
190 gtk_tree_selection_get_selected_rows (
191 selection, NULL);
192 GList * list_iter =
193 g_list_first (selected_rows);
194 while (list_iter)
195 {
196 GtkTreePath * tp = list_iter->data;
197 gtk_tree_selection_select_path (
198 selection, tp);
199 GtkTreeIter iter;
200 gtk_tree_model_get_iter (
201 GTK_TREE_MODEL (self->tracks_store),
202 &iter, tp);
203 Track * track;
204 gtk_tree_model_get (
205 GTK_TREE_MODEL (self->tracks_store),
206 &iter, TRACK_COLUMN_TRACK, &track, -1);
207
208 array_double_size_if_full (
209 tracks, count, size, Track *);
210 array_append (tracks, size, track);
211 g_debug ("track %s selected", track->name);
212
213 list_iter = g_list_next (list_iter);
214 }
215
216 g_list_free_full (
217 selected_rows,
218 (GDestroyNotify) gtk_tree_path_free);
219
220 if (count == 0)
221 {
222 *num_tracks = 0;
223 free (tracks);
224 return NULL;
225 }
226 else
227 {
228 *num_tracks = count;
229 return tracks;
230 }
231 }
232
233 static char *
get_mixdown_export_filename(ExportDialogWidget * self)234 get_mixdown_export_filename (
235 ExportDialogWidget * self)
236 {
237 const char * mixdown_str = "mixdown";
238 const char * format =
239 exporter_stringize_audio_format (
240 gtk_combo_box_get_active (self->format),
241 true);
242 char * datetime_str =
243 datetime_get_for_filename ();
244 char * base = NULL;
245 switch (g_settings_get_enum (
246 S_EXPORT, "filename-pattern"))
247 {
248 case EFP_APPEND_FORMAT:
249 base =
250 g_strdup_printf (
251 "%s.%s", mixdown_str, format);
252 break;
253 case EFP_PREPEND_DATE_APPEND_FORMAT:
254 base =
255 g_strdup_printf (
256 "%s_%s.%s", datetime_str,
257 mixdown_str, format);
258 break;
259 default:
260 g_return_val_if_reached (NULL);
261 }
262 g_return_val_if_fail (base, NULL);
263
264 char * exports_dir =
265 project_get_path (
266 PROJECT, PROJECT_PATH_EXPORTS, false);
267 char * tmp =
268 g_build_filename (
269 exports_dir, base, NULL);
270 char * full_path =
271 io_get_next_available_filepath (tmp);
272 g_free (base);
273 g_free (tmp);
274 g_free (exports_dir);
275 g_free (datetime_str);
276
277 /* we now have the full path, get only the
278 * basename */
279 base = g_path_get_basename (full_path);
280 g_free (full_path);
281
282 return base;
283 }
284
285 /**
286 * Gets the filename string for stems.
287 *
288 * @param max_files Max files to show, then append
289 * "..." at the end.
290 * @param track If non-NULL, assumed to be the
291 * stem for this track.
292 */
293 static char *
get_stem_export_filenames(ExportDialogWidget * self,int max_files,Track * in_track)294 get_stem_export_filenames (
295 ExportDialogWidget * self,
296 int max_files,
297 Track * in_track)
298 {
299 int num_tracks;
300 Track ** tracks =
301 get_enabled_tracks (self, &num_tracks);
302
303 if (!tracks)
304 {
305 return g_strdup (_("none"));
306 }
307
308 const char * format =
309 exporter_stringize_audio_format (
310 gtk_combo_box_get_active (self->format),
311 true);
312 char * datetime_str =
313 datetime_get_for_filename ();
314
315 GString * gstr = g_string_new (NULL);
316
317 int new_max_files = MIN (num_tracks, max_files);
318
319 for (int i = 0; i < new_max_files; i++)
320 {
321 Track * track = tracks[i];
322
323 if (in_track)
324 {
325 track = in_track;
326 }
327
328 char * base = NULL;
329 switch (g_settings_get_enum (
330 S_EXPORT, "filename-pattern"))
331 {
332 case EFP_APPEND_FORMAT:
333 base =
334 g_strdup_printf (
335 "%s.%s", track->name, format);
336 break;
337 case EFP_PREPEND_DATE_APPEND_FORMAT:
338 base =
339 g_strdup_printf (
340 "%s_%s.%s", datetime_str,
341 track->name, format);
342 break;
343 default:
344 g_return_val_if_reached (NULL);
345 }
346 g_return_val_if_fail (base, NULL);
347
348 char * exports_dir =
349 project_get_path (
350 PROJECT, PROJECT_PATH_EXPORTS, false);
351 char * tmp =
352 g_build_filename (
353 exports_dir, base, NULL);
354 char * full_path =
355 io_get_next_available_filepath (tmp);
356 g_free (base);
357 g_free (tmp);
358 g_free (exports_dir);
359
360 /* we now have the full path, get only the
361 * basename */
362 base = g_path_get_basename (full_path);
363 g_free (full_path);
364
365 g_string_append (gstr, base);
366
367 if (in_track)
368 {
369 g_free (base);
370 goto return_result;
371 }
372
373 if (i < (new_max_files - 1))
374 {
375 g_string_append (gstr, "\n");
376 }
377 else if (i == (new_max_files - 1) &&
378 new_max_files < num_tracks)
379 {
380 if (num_tracks - new_max_files == 1)
381 {
382 g_string_append (
383 gstr, _("\n1 more file..."));
384 }
385 else
386 {
387 g_string_append_printf (
388 gstr, _("\n%d more files..."),
389 num_tracks - new_max_files);
390 }
391 }
392 g_free (base);
393 }
394 return_result:
395 g_free (datetime_str);
396
397 return g_string_free (gstr, false);
398 }
399
400 static char *
get_exports_dir(void)401 get_exports_dir (void)
402 {
403 bool export_stems =
404 g_settings_get_boolean (
405 S_EXPORT, "export-stems");
406 return
407 project_get_path (
408 PROJECT,
409 export_stems ?
410 PROJECT_PATH_EXPORTS_STEMS :
411 PROJECT_PATH_EXPORTS,
412 false);
413 }
414
415 /**
416 * Gets the export filename only, or absolute path
417 * if @ref absolute is true.
418 *
419 * @param track If non-NULL, assumed to be the
420 * stem for this track.
421 */
422 static char *
get_export_filename(ExportDialogWidget * self,bool absolute,Track * track)423 get_export_filename (
424 ExportDialogWidget * self,
425 bool absolute,
426 Track * track)
427 {
428 bool export_stems =
429 g_settings_get_boolean (
430 S_EXPORT, "export-stems");
431 char * filename = NULL;
432 if (export_stems)
433 {
434 filename =
435 get_stem_export_filenames (self, 4, track);
436 if (absolute)
437 {
438 char * exports_dir = get_exports_dir ();
439 char * abs_path =
440 g_build_filename (
441 exports_dir, filename, NULL);
442 g_free (exports_dir);
443 g_free (filename);
444 return abs_path;
445 }
446 else
447 {
448 return filename;
449 }
450 }
451 else
452 {
453 filename =
454 get_mixdown_export_filename (self);
455
456 if (absolute)
457 {
458 char * exports_dir = get_exports_dir ();
459 char * abs_path =
460 g_build_filename (
461 exports_dir, filename, NULL);
462 g_free (exports_dir);
463 g_free (filename);
464 return abs_path;
465 }
466 else
467 {
468 return filename;
469 }
470 }
471 }
472
473 static void
update_text(ExportDialogWidget * self)474 update_text (ExportDialogWidget * self)
475 {
476 char * filename =
477 get_export_filename (self, false, NULL);
478 g_return_if_fail (filename);
479
480 char matcha[10];
481 ui_gdk_rgba_to_hex (&UI_COLORS->matcha, matcha);
482
483 #define ORANGIZE(x) \
484 "<span " \
485 "foreground=\"" matcha "\">" x "</span>"
486
487 char * exports_dir = get_exports_dir ();
488 char * str =
489 g_strdup_printf (
490 "%s\n"
491 "<span foreground=\"%s\">%s</span>"
492 "\n\n"
493 "%s\n"
494 "<a href=\"%s\">%s</a>",
495 _("The following files will be created:"),
496 matcha,
497 filename,
498 _("in the directory:"),
499 exports_dir,
500 exports_dir);
501 gtk_label_set_markup (
502 self->output_label, str);
503 g_free (filename);
504 g_free (str);
505 g_free (exports_dir);
506
507 g_signal_connect (
508 G_OBJECT (self->output_label), "activate-link",
509 G_CALLBACK (z_gtk_activate_dir_link_func), self);
510
511 #undef ORANGIZE
512 }
513
514 static void
on_song_toggled(GtkToggleButton * toggle,ExportDialogWidget * self)515 on_song_toggled (GtkToggleButton * toggle,
516 ExportDialogWidget * self)
517 {
518 if (gtk_toggle_button_get_active (toggle))
519 {
520 gtk_toggle_button_set_active (
521 self->time_range_loop, 0);
522 gtk_toggle_button_set_active (
523 self->time_range_custom, 0);
524 }
525 }
526
527 static void
on_loop_toggled(GtkToggleButton * toggle,ExportDialogWidget * self)528 on_loop_toggled (GtkToggleButton * toggle,
529 ExportDialogWidget * self)
530 {
531 if (gtk_toggle_button_get_active (toggle))
532 {
533 gtk_toggle_button_set_active (
534 self->time_range_song, 0);
535 gtk_toggle_button_set_active (
536 self->time_range_custom, 0);
537 }
538 }
539
540 static void
on_custom_toggled(GtkToggleButton * toggle,ExportDialogWidget * self)541 on_custom_toggled (GtkToggleButton * toggle,
542 ExportDialogWidget * self)
543 {
544 if (gtk_toggle_button_get_active (toggle))
545 {
546 gtk_toggle_button_set_active (
547 self->time_range_song, 0);
548 gtk_toggle_button_set_active (
549 self->time_range_loop, 0);
550 }
551 }
552
553 static void
on_mixdown_toggled(GtkToggleButton * toggle,ExportDialogWidget * self)554 on_mixdown_toggled (
555 GtkToggleButton * toggle,
556 ExportDialogWidget * self)
557 {
558 bool export_stems =
559 !gtk_toggle_button_get_active (toggle);
560 g_settings_set_boolean (
561 S_EXPORT, "export-stems", export_stems);
562 gtk_toggle_button_set_active (
563 self->stems_toggle, export_stems);
564 update_text (self);
565 }
566
567 static void
on_stems_toggled(GtkToggleButton * toggle,ExportDialogWidget * self)568 on_stems_toggled (
569 GtkToggleButton * toggle,
570 ExportDialogWidget * self)
571 {
572 bool export_stems =
573 gtk_toggle_button_get_active (toggle);
574 g_settings_set_boolean (
575 S_EXPORT, "export-stems", export_stems);
576 gtk_toggle_button_set_active (
577 self->mixdown_toggle, !export_stems);
578 update_text (self);
579 }
580
581 /**
582 * Creates the combo box model for bit depth.
583 */
584 static GtkTreeModel *
create_bit_depth_store()585 create_bit_depth_store ()
586 {
587 GtkTreeIter iter;
588 GtkTreeStore *store;
589
590 store =
591 gtk_tree_store_new (1,
592 G_TYPE_STRING);
593
594 gtk_tree_store_append (store, &iter, NULL);
595 gtk_tree_store_set (
596 store, &iter,
597 0, "16 bit",
598 -1);
599 gtk_tree_store_append (store, &iter, NULL);
600 gtk_tree_store_set (
601 store, &iter,
602 0, "24 bit",
603 -1);
604 gtk_tree_store_append (store, &iter, NULL);
605 gtk_tree_store_set (
606 store, &iter,
607 0, "32 bit",
608 -1);
609
610 return GTK_TREE_MODEL (store);
611 }
612
613 static void
setup_bit_depth_combo_box(ExportDialogWidget * self)614 setup_bit_depth_combo_box (
615 ExportDialogWidget * self)
616 {
617 GtkTreeModel * model =
618 create_bit_depth_store ();
619 gtk_combo_box_set_model (
620 self->bit_depth,
621 model);
622 gtk_cell_layout_clear (
623 GTK_CELL_LAYOUT (self->bit_depth));
624 GtkCellRenderer* renderer =
625 gtk_cell_renderer_text_new ();
626 gtk_cell_layout_pack_start (
627 GTK_CELL_LAYOUT (self->bit_depth),
628 renderer,
629 TRUE);
630 gtk_cell_layout_set_attributes (
631 GTK_CELL_LAYOUT (self->bit_depth),
632 renderer,
633 "text", 0,
634 NULL);
635
636 gtk_combo_box_set_active (
637 self->bit_depth,
638 0);
639 }
640
641 /**
642 * Creates the combo box model for the pattern.
643 */
644 static GtkTreeModel *
create_filename_pattern_store()645 create_filename_pattern_store ()
646 {
647 GtkTreeIter iter;
648 GtkTreeStore *store;
649
650 store = gtk_tree_store_new (1, G_TYPE_STRING);
651
652 gtk_tree_store_append (store, &iter, NULL);
653 gtk_tree_store_set (
654 store, &iter,
655 0, _("<name>.<format>"),
656 -1);
657 gtk_tree_store_append (store, &iter, NULL);
658 gtk_tree_store_set (
659 store, &iter,
660 0, _("<date>_<name>.<format>"),
661 -1);
662
663 return GTK_TREE_MODEL (store);
664 }
665
666 static void
on_filename_pattern_changed(GtkComboBox * widget,ExportDialogWidget * self)667 on_filename_pattern_changed (
668 GtkComboBox * widget,
669 ExportDialogWidget * self)
670 {
671 g_settings_set_enum (
672 S_EXPORT, "filename-pattern",
673 gtk_combo_box_get_active (widget));
674
675 update_text (self);
676 }
677
678 static void
setup_filename_pattern_combo_box(ExportDialogWidget * self)679 setup_filename_pattern_combo_box (
680 ExportDialogWidget * self)
681 {
682 GtkTreeModel * model =
683 create_filename_pattern_store ();
684 gtk_combo_box_set_model (
685 self->filename_pattern, model);
686 gtk_cell_layout_clear (
687 GTK_CELL_LAYOUT (self->filename_pattern));
688 GtkCellRenderer* renderer =
689 gtk_cell_renderer_text_new ();
690 gtk_cell_layout_pack_start (
691 GTK_CELL_LAYOUT (self->filename_pattern),
692 renderer, TRUE);
693 gtk_cell_layout_set_attributes (
694 GTK_CELL_LAYOUT (self->filename_pattern),
695 renderer, "text", 0, NULL);
696
697 gtk_combo_box_set_active (
698 self->filename_pattern,
699 g_settings_get_enum (
700 S_EXPORT, "filename-pattern"));
701
702 g_signal_connect (
703 G_OBJECT (self->filename_pattern), "changed",
704 G_CALLBACK (on_filename_pattern_changed), self);
705 }
706
707 /**
708 * Creates the combo box model for the audio
709 * formats.
710 */
711 static GtkTreeModel *
create_formats_store()712 create_formats_store ()
713 {
714 GtkTreeIter iter;
715 GtkTreeStore *store;
716
717 store =
718 gtk_tree_store_new (NUM_AUDIO_FORMAT_COLUMNS,
719 G_TYPE_STRING,
720 G_TYPE_INT);
721
722 for (int i = 0; i < NUM_AUDIO_FORMATS; i++)
723 {
724 gtk_tree_store_append (store, &iter, NULL);
725 const char * str =
726 exporter_stringize_audio_format (i, false);
727 gtk_tree_store_set (
728 store, &iter,
729 COLUMN_AUDIO_FORMAT_LABEL, str,
730 COLUMN_AUDIO_FORMAT, i,
731 -1);
732 }
733
734 return GTK_TREE_MODEL (store);
735 }
736
737 static void
on_format_changed(GtkComboBox * widget,ExportDialogWidget * self)738 on_format_changed (
739 GtkComboBox * widget,
740 ExportDialogWidget * self)
741 {
742 update_text (self);
743 AudioFormat format =
744 gtk_combo_box_get_active (widget);
745
746 g_settings_set_enum (
747 S_EXPORT, "format", format);
748
749 #define SET_SENSITIVE(x) \
750 gtk_widget_set_sensitive ( \
751 GTK_WIDGET (self->x), 1)
752
753 #define SET_UNSENSITIVE(x) \
754 gtk_widget_set_sensitive ( \
755 GTK_WIDGET (self->x), 0)
756
757 SET_UNSENSITIVE (export_genre);
758 SET_UNSENSITIVE (export_artist);
759 SET_UNSENSITIVE (export_title);
760 SET_UNSENSITIVE (bit_depth);
761 SET_UNSENSITIVE (dither);
762
763 switch (format)
764 {
765 case AUDIO_FORMAT_MIDI:
766 break;
767 case AUDIO_FORMAT_OGG_VORBIS:
768 case AUDIO_FORMAT_OGG_OPUS:
769 SET_SENSITIVE (export_genre);
770 SET_SENSITIVE (export_artist);
771 SET_SENSITIVE (export_title);
772 SET_SENSITIVE (dither);
773 break;
774 default:
775 SET_SENSITIVE (export_genre);
776 SET_SENSITIVE (export_artist);
777 SET_SENSITIVE (export_title);
778 SET_SENSITIVE (bit_depth);
779 SET_SENSITIVE (dither);
780 break;
781 }
782
783 #undef SET_SENSITIVE
784 #undef SET_UNSENSITIVE
785 }
786
787 static void
setup_formats_combo_box(ExportDialogWidget * self)788 setup_formats_combo_box (
789 ExportDialogWidget * self)
790 {
791 GtkTreeModel * model =
792 create_formats_store ();
793 gtk_combo_box_set_model (
794 self->format,
795 model);
796 gtk_cell_layout_clear (
797 GTK_CELL_LAYOUT (self->format));
798 GtkCellRenderer* renderer =
799 gtk_cell_renderer_text_new ();
800 gtk_cell_layout_pack_start (
801 GTK_CELL_LAYOUT (self->format),
802 renderer,
803 TRUE);
804 gtk_cell_layout_set_attributes (
805 GTK_CELL_LAYOUT (self->format),
806 renderer,
807 "text", COLUMN_AUDIO_FORMAT_LABEL,
808 NULL);
809
810 gtk_combo_box_set_active (
811 self->format,
812 g_settings_get_enum (S_EXPORT, "format"));
813 }
814
815 static void
on_cancel_clicked(GtkButton * btn,ExportDialogWidget * self)816 on_cancel_clicked (GtkButton * btn,
817 ExportDialogWidget * self)
818 {
819 gtk_window_close (GTK_WINDOW (self));
820 /*gtk_widget_destroy (GTK_WIDGET (self));*/
821 }
822
823 static void
on_progress_dialog_closed(GtkDialog * dialog,int response_id,ExportDialogWidget * self)824 on_progress_dialog_closed (
825 GtkDialog * dialog,
826 int response_id,
827 ExportDialogWidget * self)
828 {
829 update_text (self);
830 }
831
832 /**
833 * @param track If non-NULL, assumed to be a stem
834 * for this track.
835 */
836 static void
init_export_info(ExportDialogWidget * self,ExportSettings * info,Track * track)837 init_export_info (
838 ExportDialogWidget * self,
839 ExportSettings * info,
840 Track * track)
841 {
842 memset (info, 0, sizeof (ExportSettings));
843 info->format =
844 gtk_combo_box_get_active (self->format);
845 g_settings_set_enum (
846 S_EXPORT, "format", info->format);
847 info->depth =
848 gtk_combo_box_get_active (self->bit_depth);
849 g_settings_set_enum (
850 S_EXPORT, "bit-depth", info->depth);
851 info->dither =
852 gtk_toggle_button_get_active (self->dither);
853 g_settings_set_boolean (
854 S_EXPORT, "dither", info->dither);
855 info->artist =
856 g_strdup (
857 gtk_entry_get_text (self->export_artist));
858 info->title =
859 g_strdup (
860 gtk_entry_get_text (self->export_title));
861 info->genre =
862 g_strdup (
863 gtk_entry_get_text (self->export_genre));
864 g_settings_set_string (
865 S_EXPORT, "artist", info->artist);
866 g_settings_set_string (
867 S_EXPORT, "title", info->title);
868 g_settings_set_string (
869 S_EXPORT, "genre", info->genre);
870
871 #define SET_TIME_RANGE(x) \
872 g_settings_set_enum ( \
873 S_EXPORT, "time-range", TIME_RANGE_##x); \
874 info->time_range = TIME_RANGE_##x
875
876 if (gtk_toggle_button_get_active (
877 self->time_range_song))
878 {
879 SET_TIME_RANGE (SONG);
880 }
881 else if (gtk_toggle_button_get_active (
882 self->time_range_loop))
883 {
884 SET_TIME_RANGE (LOOP);
885 }
886 else if (gtk_toggle_button_get_active (
887 self->time_range_custom))
888 {
889 SET_TIME_RANGE (CUSTOM);
890 }
891
892 info->file_uri =
893 get_export_filename (self, true, track);
894
895 info->bounce_with_parents = true;
896
897 info->mode = EXPORT_MODE_TRACKS;
898 info->progress_info.has_error = false;
899 info->progress_info.cancelled = false;
900 strcpy (info->progress_info.error_str, "");
901 }
902
903 static void
on_export_clicked(GtkButton * btn,ExportDialogWidget * self)904 on_export_clicked (
905 GtkButton * btn,
906 ExportDialogWidget * self)
907 {
908 bool export_stems =
909 g_settings_get_boolean (
910 S_EXPORT, "export-stems");
911
912 int num_tracks;
913 Track ** tracks =
914 get_enabled_tracks (self, &num_tracks);
915 if (!tracks)
916 {
917 ui_show_error_message (
918 MAIN_WINDOW, _("No tracks to export"));
919 return;
920 }
921
922 /* make exports dir if not there yet */
923 char * exports_dir = get_exports_dir ();
924 io_mkdir (exports_dir);
925 g_free (exports_dir);
926
927 if (export_stems)
928 {
929 /* export each track individually */
930 for (int i = 0; i < num_tracks; i++)
931 {
932 /* unmark all tracks for bounce */
933 tracklist_mark_all_tracks_for_bounce (
934 TRACKLIST, false);
935
936 Track * track = tracks[i];
937 track_mark_for_bounce (
938 track, F_BOUNCE, F_MARK_REGIONS,
939 F_MARK_CHILDREN, F_MARK_PARENTS);
940
941 ExportSettings info;
942 init_export_info (self, &info, track);
943
944 g_message ("exporting %s", info.file_uri);
945
946 /* start exporting in a new thread */
947 GThread * thread =
948 g_thread_new (
949 "export_thread",
950 (GThreadFunc)
951 exporter_generic_export_thread,
952 &info);
953
954 /* create a progress dialog and block */
955 ExportProgressDialogWidget * progress_dialog =
956 export_progress_dialog_widget_new (
957 &info, true, true, F_CANCELABLE);
958 gtk_window_set_transient_for (
959 GTK_WINDOW (progress_dialog),
960 GTK_WINDOW (self));
961 g_signal_connect (
962 G_OBJECT (progress_dialog), "response",
963 G_CALLBACK (on_progress_dialog_closed),
964 self);
965 gtk_dialog_run (
966 GTK_DIALOG (progress_dialog));
967 gtk_widget_destroy (
968 GTK_WIDGET (progress_dialog));
969
970 g_thread_join (thread);
971
972 g_free (info.file_uri);
973
974 track->bounce = false;
975 }
976 }
977 else /* if exporting mixdown */
978 {
979 ExportSettings info;
980 init_export_info (self, &info, NULL);
981
982 /* unmark all tracks for bounce */
983 tracklist_mark_all_tracks_for_bounce (
984 TRACKLIST, false);
985
986 /* mark all checked tracks for bounce */
987 for (int i = 0; i < num_tracks; i++)
988 {
989 Track * track = tracks[i];
990 track_mark_for_bounce (
991 track, F_BOUNCE, F_MARK_REGIONS,
992 F_NO_MARK_CHILDREN, F_MARK_PARENTS);
993 }
994
995 g_message ("exporting %s", info.file_uri);
996
997 /* start exporting in a new thread */
998 GThread * thread =
999 g_thread_new (
1000 "export_thread",
1001 (GThreadFunc) exporter_generic_export_thread,
1002 &info);
1003
1004 /* create a progress dialog and block */
1005 ExportProgressDialogWidget * progress_dialog =
1006 export_progress_dialog_widget_new (
1007 &info, true, true, F_CANCELABLE);
1008 gtk_window_set_transient_for (
1009 GTK_WINDOW (progress_dialog),
1010 GTK_WINDOW (self));
1011 g_signal_connect (
1012 G_OBJECT (progress_dialog), "response",
1013 G_CALLBACK (on_progress_dialog_closed), self);
1014 gtk_dialog_run (GTK_DIALOG (progress_dialog));
1015 gtk_widget_destroy (GTK_WIDGET (progress_dialog));
1016
1017 g_thread_join (thread);
1018
1019 g_free (info.file_uri);
1020 }
1021 }
1022
1023 /**
1024 * Visible function for tracks tree model.
1025 *
1026 * Used for filtering based on selected options.
1027 */
1028 static gboolean
tracks_tree_model_visible_func(GtkTreeModel * model,GtkTreeIter * iter,ExportDialogWidget * self)1029 tracks_tree_model_visible_func (
1030 GtkTreeModel * model,
1031 GtkTreeIter * iter,
1032 ExportDialogWidget * self)
1033 {
1034 bool visible = true;
1035
1036 return visible;
1037 }
1038
1039 static void
add_group_track_children(ExportDialogWidget * self,GtkTreeStore * tree_store,GtkTreeIter * iter,Track * track)1040 add_group_track_children (
1041 ExportDialogWidget * self,
1042 GtkTreeStore * tree_store,
1043 GtkTreeIter * iter,
1044 Track * track)
1045 {
1046 /* add the group */
1047 GtkTreeIter group_iter;
1048 gtk_tree_store_append (
1049 tree_store, &group_iter, iter);
1050 gtk_tree_store_set (
1051 tree_store, &group_iter,
1052 TRACK_COLUMN_CHECKBOX, true,
1053 TRACK_COLUMN_ICON, track->icon_name,
1054 TRACK_COLUMN_BG_RGBA, &track->color,
1055 TRACK_COLUMN_DUMMY_TEXT, dummy_text,
1056 TRACK_COLUMN_NAME, track->name,
1057 TRACK_COLUMN_TRACK, track,
1058 -1);
1059
1060 g_debug (
1061 "%s: track '%s'", __func__, track->name);
1062
1063 /* add the children */
1064 for (int i = 0; i < track->num_children; i++)
1065 {
1066 Track * child =
1067 tracklist_find_track_by_name_hash (
1068 TRACKLIST, track->children[i]);
1069 g_return_if_fail (
1070 IS_TRACK_AND_NONNULL (child));
1071
1072 g_debug ("child: '%s'", child->name);
1073
1074 if (child->num_children > 0)
1075 {
1076 add_group_track_children (
1077 self, tree_store, &group_iter, child);
1078 }
1079 else
1080 {
1081 GtkTreeIter child_iter;
1082 gtk_tree_store_append (
1083 tree_store, &child_iter, &group_iter);
1084 gtk_tree_store_set (
1085 tree_store, &child_iter,
1086 TRACK_COLUMN_CHECKBOX, true,
1087 TRACK_COLUMN_ICON, child->icon_name,
1088 TRACK_COLUMN_BG_RGBA, &child->color,
1089 TRACK_COLUMN_DUMMY_TEXT, dummy_text,
1090 TRACK_COLUMN_NAME, child->name,
1091 TRACK_COLUMN_TRACK, child,
1092 -1);
1093 }
1094 }
1095 }
1096
1097 static GtkTreeModel *
create_model_for_tracks(ExportDialogWidget * self)1098 create_model_for_tracks (
1099 ExportDialogWidget * self)
1100 {
1101 /* checkbox, icon, foreground rgba,
1102 * background rgba, name, track */
1103 GtkTreeStore * tree_store =
1104 gtk_tree_store_new (
1105 NUM_TRACK_COLUMNS,
1106 G_TYPE_BOOLEAN,
1107 G_TYPE_STRING,
1108 GDK_TYPE_RGBA,
1109 G_TYPE_STRING,
1110 G_TYPE_STRING,
1111 G_TYPE_POINTER);
1112
1113 /*GtkTreeIter iter;*/
1114 /*gtk_tree_store_append (tree_store, &iter, NULL);*/
1115 add_group_track_children (
1116 self, tree_store, NULL, P_MASTER_TRACK);
1117
1118 self->tracks_store = tree_store;
1119
1120 GtkTreeModel * model =
1121 gtk_tree_model_filter_new (
1122 GTK_TREE_MODEL (tree_store), NULL);
1123 gtk_tree_model_filter_set_visible_func (
1124 GTK_TREE_MODEL_FILTER (model),
1125 (GtkTreeModelFilterVisibleFunc)
1126 tracks_tree_model_visible_func,
1127 self, NULL);
1128
1129 return model;
1130 }
1131
1132 /**
1133 * This toggles on all parents recursively if
1134 * something is toggled.
1135 */
1136 static void
set_track_toggle_on_parent_recursively(ExportDialogWidget * self,GtkTreeIter * iter,bool toggled)1137 set_track_toggle_on_parent_recursively (
1138 ExportDialogWidget * self,
1139 GtkTreeIter * iter,
1140 bool toggled)
1141 {
1142 if (!toggled)
1143 {
1144 return;
1145 }
1146
1147 GtkTreeModel * model =
1148 GTK_TREE_MODEL (self->tracks_store);
1149
1150 /* enable the parent if toggled */
1151 GtkTreeIter parent_iter;
1152 bool has_parent =
1153 gtk_tree_model_iter_parent (
1154 model, &parent_iter, iter);
1155 if (has_parent)
1156 {
1157 /* set new value on widget */
1158 gtk_tree_store_set (
1159 GTK_TREE_STORE (model), &parent_iter,
1160 TRACK_COLUMN_CHECKBOX, toggled, -1);
1161
1162 set_track_toggle_on_parent_recursively (
1163 self, &parent_iter, toggled);
1164 }
1165 }
1166
1167 static void
set_track_toggle_recursively(ExportDialogWidget * self,GtkTreeIter * iter,bool toggled)1168 set_track_toggle_recursively (
1169 ExportDialogWidget * self,
1170 GtkTreeIter * iter,
1171 bool toggled)
1172 {
1173 GtkTreeModel * model =
1174 GTK_TREE_MODEL (self->tracks_store);
1175
1176 /* if group track, also enable/disable children
1177 * recursively */
1178 bool has_children =
1179 gtk_tree_model_iter_has_child (model, iter);
1180 if (has_children)
1181 {
1182 int num_children =
1183 gtk_tree_model_iter_n_children (
1184 model, iter);
1185
1186 for (int i = 0; i < num_children; i++)
1187 {
1188 GtkTreeIter child_iter;
1189 gtk_tree_model_iter_nth_child (
1190 model, &child_iter, iter, i);
1191
1192 /* set new value on widget */
1193 gtk_tree_store_set (
1194 GTK_TREE_STORE (model), &child_iter,
1195 TRACK_COLUMN_CHECKBOX, toggled, -1);
1196
1197 /* recurse */
1198 set_track_toggle_recursively (
1199 self, &child_iter, toggled);
1200 }
1201 }
1202 }
1203
1204 static void
on_track_toggled(GtkCellRendererToggle * cell,gchar * path_str,ExportDialogWidget * self)1205 on_track_toggled (
1206 GtkCellRendererToggle * cell,
1207 gchar * path_str,
1208 ExportDialogWidget * self)
1209 {
1210 GtkTreeModel * model =
1211 GTK_TREE_MODEL (self->tracks_store);
1212 g_debug ("path str: %s", path_str);
1213
1214 /* get tree path and iter */
1215 GtkTreePath * path =
1216 gtk_tree_path_new_from_string (path_str);
1217 GtkTreeIter iter;
1218 bool ret =
1219 gtk_tree_model_get_iter (model, &iter, path);
1220 g_return_if_fail (ret);
1221 g_return_if_fail (
1222 gtk_tree_store_iter_is_valid (
1223 GTK_TREE_STORE (model), &iter));
1224
1225 /* get toggled */
1226 gboolean toggled;
1227 gtk_tree_model_get (
1228 model, &iter,
1229 TRACK_COLUMN_CHECKBOX, &toggled, -1);
1230 g_return_if_fail (
1231 gtk_tree_store_iter_is_valid (
1232 GTK_TREE_STORE (model), &iter));
1233
1234 /* get new value */
1235 toggled ^= 1;
1236
1237 /* set new value on widget */
1238 gtk_tree_store_set (
1239 GTK_TREE_STORE (model), &iter,
1240 TRACK_COLUMN_CHECKBOX, toggled, -1);
1241
1242 /* if exporting mixdown (single file) */
1243 if (!g_settings_get_boolean (
1244 S_EXPORT, "export-stems"))
1245 {
1246 /* toggle parents if toggled */
1247 set_track_toggle_on_parent_recursively (
1248 self, &iter, toggled);
1249
1250 /* propagate value to children recursively */
1251 set_track_toggle_recursively (
1252 self, &iter, toggled);
1253 }
1254
1255 update_text (self);
1256
1257 /* clean up */
1258 gtk_tree_path_free (path);
1259 }
1260
1261 static void
enable_or_disable_tracks(ExportDialogWidget * self,bool enable)1262 enable_or_disable_tracks (
1263 ExportDialogWidget * self,
1264 bool enable)
1265 {
1266 int num_tracks;
1267 Track ** tracks =
1268 get_selected_tracks (self, &num_tracks);
1269
1270 if (tracks)
1271 {
1272 /* TODO enable/disable */
1273 }
1274 }
1275
1276 static void
on_tracks_disable(GtkMenuItem * menuitem,ExportDialogWidget * self)1277 on_tracks_disable (
1278 GtkMenuItem * menuitem,
1279 ExportDialogWidget * self)
1280 {
1281 g_message ("tracks disable");
1282 enable_or_disable_tracks (self, false);
1283 }
1284
1285 static void
on_tracks_enable(GtkMenuItem * menuitem,ExportDialogWidget * self)1286 on_tracks_enable (
1287 GtkMenuItem * menuitem,
1288 ExportDialogWidget * self)
1289 {
1290 g_message ("tracks enable");
1291 enable_or_disable_tracks (self, true);
1292 }
1293
1294 static void
show_tracks_context_menu(ExportDialogWidget * self)1295 show_tracks_context_menu (
1296 ExportDialogWidget * self)
1297 {
1298 GtkWidget *menuitem;
1299 GtkWidget * menu = gtk_menu_new();
1300
1301 /* FIXME this is allocating memory every time */
1302
1303 menuitem =
1304 gtk_menu_item_new_with_label (_("Enable"));
1305 gtk_widget_set_visible (menuitem, true);
1306 gtk_menu_shell_append (
1307 GTK_MENU_SHELL (menu),
1308 GTK_WIDGET (menuitem));
1309 g_signal_connect (
1310 G_OBJECT (menuitem), "activate",
1311 G_CALLBACK (on_tracks_enable), self);
1312
1313 menuitem =
1314 gtk_menu_item_new_with_label (_("Disable"));
1315 gtk_widget_set_visible (menuitem, true);
1316 gtk_menu_shell_append (
1317 GTK_MENU_SHELL (menu),
1318 GTK_WIDGET (menuitem));
1319 g_signal_connect (
1320 G_OBJECT (menuitem), "activate",
1321 G_CALLBACK (on_tracks_disable), self);
1322
1323 gtk_menu_attach_to_widget (
1324 GTK_MENU (menu),
1325 GTK_WIDGET (self), NULL);
1326 gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
1327 }
1328
1329 static void
on_track_right_click(GtkGestureMultiPress * gesture,gint n_press,gdouble x_dbl,gdouble y_dbl,ExportDialogWidget * self)1330 on_track_right_click (
1331 GtkGestureMultiPress * gesture,
1332 gint n_press,
1333 gdouble x_dbl,
1334 gdouble y_dbl,
1335 ExportDialogWidget * self)
1336 {
1337 if (n_press != 1)
1338 return;
1339
1340 show_tracks_context_menu (self);
1341 }
1342
1343 static void
setup_treeview(ExportDialogWidget * self)1344 setup_treeview (
1345 ExportDialogWidget * self)
1346 {
1347 self->tracks_model =
1348 create_model_for_tracks (self);
1349 gtk_tree_view_set_model (
1350 self->tracks_treeview, self->tracks_model);
1351
1352 GtkTreeView * tree_view = self->tracks_treeview;
1353
1354 /* init tree view */
1355 GtkCellRenderer * renderer;
1356 GtkTreeViewColumn * column;
1357
1358 /* column for checkbox */
1359 renderer = gtk_cell_renderer_toggle_new ();
1360 column =
1361 gtk_tree_view_column_new_with_attributes (
1362 "icon", renderer,
1363 "active", TRACK_COLUMN_CHECKBOX,
1364 NULL);
1365 gtk_tree_view_append_column (tree_view, column);
1366 g_signal_connect (
1367 renderer, "toggled",
1368 G_CALLBACK (on_track_toggled), self);
1369
1370 /* column for color */
1371 renderer = gtk_cell_renderer_text_new ();
1372 column =
1373 gtk_tree_view_column_new_with_attributes (
1374 "name", renderer,
1375 "text", TRACK_COLUMN_DUMMY_TEXT,
1376 "background-rgba", TRACK_COLUMN_BG_RGBA,
1377 NULL);
1378 gtk_tree_view_append_column (tree_view, column);
1379
1380 /* column for icon */
1381 renderer = gtk_cell_renderer_pixbuf_new ();
1382 column =
1383 gtk_tree_view_column_new_with_attributes (
1384 "icon", renderer,
1385 "icon-name", TRACK_COLUMN_ICON,
1386 NULL);
1387 gtk_tree_view_append_column (tree_view, column);
1388
1389 /* column for name */
1390 renderer = gtk_cell_renderer_text_new ();
1391 column =
1392 gtk_tree_view_column_new_with_attributes (
1393 "name", renderer,
1394 "text", TRACK_COLUMN_NAME,
1395 NULL);
1396 gtk_tree_view_append_column (tree_view, column);
1397
1398 /* set search column */
1399 gtk_tree_view_set_search_column (
1400 tree_view, TRACK_COLUMN_NAME);
1401
1402 /* hide headers */
1403 gtk_tree_view_set_headers_visible (
1404 GTK_TREE_VIEW (tree_view), false);
1405
1406 gtk_tree_selection_set_mode (
1407 gtk_tree_view_get_selection (tree_view),
1408 GTK_SELECTION_MULTIPLE);
1409
1410 /* connect right click handler */
1411 GtkGestureMultiPress * mp =
1412 GTK_GESTURE_MULTI_PRESS (
1413 gtk_gesture_multi_press_new (
1414 GTK_WIDGET (tree_view)));
1415 gtk_gesture_single_set_button (
1416 GTK_GESTURE_SINGLE (mp),
1417 GDK_BUTTON_SECONDARY);
1418 g_signal_connect (
1419 G_OBJECT (mp), "pressed",
1420 G_CALLBACK (on_track_right_click), self);
1421
1422 #if 0
1423 /* set search func */
1424 gtk_tree_view_set_search_equal_func (
1425 GTK_TREE_VIEW (tree_view),
1426 (GtkTreeViewSearchEqualFunc)
1427 plugin_search_equal_func,
1428 self, NULL);
1429
1430 GtkTreeSelection * sel =
1431 gtk_tree_view_get_selection (
1432 GTK_TREE_VIEW (tree_view));
1433 g_signal_connect (
1434 G_OBJECT (sel), "changed",
1435 G_CALLBACK (on_selection_changed), self);
1436 #endif
1437 }
1438
1439 /**
1440 * Creates a new export dialog.
1441 */
1442 ExportDialogWidget *
export_dialog_widget_new()1443 export_dialog_widget_new ()
1444 {
1445 ExportDialogWidget * self =
1446 g_object_new (EXPORT_DIALOG_WIDGET_TYPE, NULL);
1447
1448 update_text (self);
1449
1450 g_signal_connect (
1451 G_OBJECT (self->export_button), "clicked",
1452 G_CALLBACK (on_export_clicked), self);
1453 g_signal_connect (
1454 G_OBJECT (self->format), "changed",
1455 G_CALLBACK (on_format_changed), self);
1456 g_signal_connect (
1457 G_OBJECT (self->time_range_song), "toggled",
1458 G_CALLBACK (on_song_toggled), self);
1459 g_signal_connect (
1460 G_OBJECT (self->time_range_loop), "toggled",
1461 G_CALLBACK (on_loop_toggled), self);
1462 g_signal_connect (
1463 G_OBJECT (self->time_range_custom), "toggled",
1464 G_CALLBACK (on_custom_toggled), self);
1465 g_signal_connect (
1466 G_OBJECT (self->mixdown_toggle), "toggled",
1467 G_CALLBACK (on_mixdown_toggled), self);
1468 g_signal_connect (
1469 G_OBJECT (self->stems_toggle), "toggled",
1470 G_CALLBACK (on_stems_toggled), self);
1471
1472 return self;
1473 }
1474
1475 static void
export_dialog_widget_class_init(ExportDialogWidgetClass * _klass)1476 export_dialog_widget_class_init (
1477 ExportDialogWidgetClass * _klass)
1478 {
1479 GtkWidgetClass * klass = GTK_WIDGET_CLASS (_klass);
1480 resources_set_class_template (
1481 klass, "export_dialog.ui");
1482
1483 #define BIND_CHILD(x) \
1484 gtk_widget_class_bind_template_child ( \
1485 klass, ExportDialogWidget, x)
1486
1487 BIND_CHILD (cancel_button);
1488 BIND_CHILD (export_button);
1489 BIND_CHILD (export_artist);
1490 BIND_CHILD (export_title);
1491 BIND_CHILD (export_genre);
1492 BIND_CHILD (filename_pattern);
1493 BIND_CHILD (bit_depth);
1494 BIND_CHILD (time_range_song);
1495 BIND_CHILD (time_range_loop);
1496 BIND_CHILD (time_range_custom);
1497 BIND_CHILD (format);
1498 BIND_CHILD (dither);
1499 BIND_CHILD (output_label);
1500 BIND_CHILD (tracks_treeview);
1501 BIND_CHILD (mixdown_toggle);
1502 BIND_CHILD (stems_toggle);
1503
1504 #undef BIND_CHILD
1505
1506 gtk_widget_class_bind_template_callback (
1507 klass, on_cancel_clicked);
1508 }
1509
1510 static void
export_dialog_widget_init(ExportDialogWidget * self)1511 export_dialog_widget_init (
1512 ExportDialogWidget * self)
1513 {
1514 gtk_widget_init_template (GTK_WIDGET (self));
1515
1516 gtk_entry_set_text (
1517 GTK_ENTRY (self->export_artist),
1518 g_settings_get_string (S_EXPORT, "artist"));
1519 gtk_entry_set_text (
1520 GTK_ENTRY (self->export_title),
1521 g_settings_get_string (S_EXPORT, "title"));
1522 gtk_entry_set_text (
1523 GTK_ENTRY (self->export_genre),
1524 g_settings_get_string (S_EXPORT, "genre"));
1525
1526 gtk_toggle_button_set_active (
1527 self->time_range_song, false);
1528 gtk_toggle_button_set_active (
1529 self->time_range_loop, false);
1530 gtk_toggle_button_set_active (
1531 self->time_range_custom, false);
1532 switch (g_settings_get_enum (S_EXPORT, "time-range"))
1533 {
1534 case 0: // loop
1535 gtk_toggle_button_set_active (
1536 self->time_range_loop, true);
1537 break;
1538 case 1: // song
1539 gtk_toggle_button_set_active (
1540 self->time_range_song, true);
1541 break;
1542 case 2: // custom
1543 gtk_toggle_button_set_active (
1544 self->time_range_custom, true);
1545 break;
1546 }
1547 bool export_stems =
1548 g_settings_get_boolean (
1549 S_EXPORT, "export-stems");
1550 gtk_toggle_button_set_active (
1551 self->mixdown_toggle, !export_stems);
1552 gtk_toggle_button_set_active (
1553 self->stems_toggle, export_stems);
1554
1555 gtk_toggle_button_set_active (
1556 GTK_TOGGLE_BUTTON (self->dither),
1557 g_settings_get_boolean (
1558 S_EXPORT, "dither"));
1559
1560 setup_bit_depth_combo_box (self);
1561 setup_formats_combo_box (self);
1562 setup_filename_pattern_combo_box (self);
1563
1564 setup_treeview (self);
1565
1566 on_format_changed (self->format, self);
1567 }
1568