1 /*
2     Converter UI for DeaDBeeF Player
3     Copyright (C) 2009-2015 Alexey Yakovenko and other contributors
4 
5     This software is provided 'as-is', without any express or implied
6     warranty.  In no event will the authors be held liable for any damages
7     arising from the use of this software.
8 
9     Permission is granted to anyone to use this software for any purpose,
10     including commercial applications, and to alter it and redistribute it
11     freely, subject to the following restrictions:
12 
13     1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17 
18     2. Altered source versions must be plainly marked as such, and must not be
19      misrepresented as being the original software.
20 
21     3. This notice may not be removed or altered from any source distribution.
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #  include "../../config.h"
26 #endif
27 #if HAVE_SYS_CDEFS_H
28 #include <sys/cdefs.h>
29 #endif
30 #include <limits.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <assert.h>
34 #include <dirent.h>
35 #include <unistd.h>
36 #include "converter.h"
37 #include "support.h"
38 #include "interface.h"
39 #include "../gtkui/gtkui_api.h"
40 
41 DB_functions_t *deadbeef;
42 
43 ddb_converter_t *converter_plugin;
44 ddb_gtkui_t *gtkui_plugin;
45 
46 static int converter_active;
47 
48 typedef struct {
49     GtkWidget *converter;
50     ddb_encoder_preset_t *current_encoder_preset;
51     ddb_dsp_preset_t *current_dsp_preset;
52 
53     DB_playItem_t **convert_items;
54     ddb_playlist_t *convert_playlist;
55 
56     int convert_items_count;
57     char *outfolder;
58     char *outfile;
59     int preserve_folder_structure;
60     int write_to_source_folder;
61     int output_bps;
62     int output_is_float;
63     int overwrite_action;
64     ddb_encoder_preset_t *encoder_preset;
65     ddb_dsp_preset_t *dsp_preset;
66     GtkWidget *progress;
67     GtkWidget *progress_entry;
68     int cancelled;
69 } converter_ctx_t;
70 
71 converter_ctx_t *current_ctx;
72 
73 enum {
74     PRESET_TYPE_ENCODER,
75     PRESET_TYPE_DSP
76 };
77 
78 static void
fill_presets(GtkListStore * mdl,ddb_preset_t * head,int type)79 fill_presets (GtkListStore *mdl, ddb_preset_t *head, int type) {
80     ddb_preset_t *p = head;
81     while (p) {
82         GtkTreeIter iter;
83         gtk_list_store_append (mdl, &iter);
84         const char *s = p->title;
85         if (type == PRESET_TYPE_ENCODER && ((ddb_encoder_preset_t *)p)->readonly) {
86             char stock[1000];
87             snprintf (stock, sizeof (stock), _("[Built-in] %s"), p->title);
88             s = stock;
89         }
90         gtk_list_store_set (mdl, &iter, 0, s, -1);
91         p = p->next;
92     }
93 }
94 
95 void
on_converter_progress_cancel(GtkDialog * dialog,gint response_id,gpointer user_data)96 on_converter_progress_cancel (GtkDialog *dialog, gint response_id, gpointer user_data) {
97     converter_ctx_t *ctx = user_data;
98     ctx->cancelled = 1;
99 }
100 
101 typedef struct {
102     GtkWidget *entry;
103     char *text;
104 } update_progress_info_t;
105 
106 static gboolean
update_progress_cb(gpointer ctx)107 update_progress_cb (gpointer ctx) {
108     update_progress_info_t *info = ctx;
109     gtk_entry_set_text (GTK_ENTRY (info->entry), info->text);
110     free (info->text);
111     g_object_unref (info->entry);
112     free (info);
113     return FALSE;
114 }
115 
116 static gboolean
destroy_progress_cb(gpointer ctx)117 destroy_progress_cb (gpointer ctx) {
118     gtk_widget_destroy (ctx);
119     return FALSE;
120 }
121 
122 struct overwrite_prompt_ctx {
123     const char *fname;
124     uintptr_t mutex;
125     uintptr_t cond;
126     int result;
127 };
128 
129 static gboolean
overwrite_prompt_cb(void * ctx)130 overwrite_prompt_cb (void *ctx) {
131     struct overwrite_prompt_ctx *ctl = ctx;
132     GtkWidget *mainwin = gtkui_plugin->get_mainwin ();
133     GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (mainwin), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("The file already exists. Overwrite?"));
134     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (mainwin));
135     gtk_window_set_title (GTK_WINDOW (dlg), _("Converter warning"));
136     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), "%s", ctl->fname);
137 
138     int response = gtk_dialog_run (GTK_DIALOG (dlg));
139     gtk_widget_destroy (dlg);
140     ctl->result = response == GTK_RESPONSE_YES ? 1 : 0;
141     deadbeef->cond_signal (ctl->cond);
142     return FALSE;
143 }
144 
145 static int
overwrite_prompt(const char * outpath)146 overwrite_prompt (const char *outpath) {
147     struct overwrite_prompt_ctx ctl;
148     ctl.mutex = deadbeef->mutex_create ();
149     ctl.cond = deadbeef->cond_create ();
150     ctl.fname = outpath;
151     ctl.result = 0;
152     gdk_threads_add_idle (overwrite_prompt_cb, &ctl);
153     deadbeef->cond_wait (ctl.cond, ctl.mutex);
154     deadbeef->cond_free (ctl.cond);
155     deadbeef->mutex_free (ctl.mutex);
156     return ctl.result;
157 }
158 
159 static void
converter_worker(void * ctx)160 converter_worker (void *ctx) {
161     deadbeef->background_job_increment ();
162     converter_ctx_t *conv = ctx;
163 
164     char root[2000] = "";
165     int rootlen = 0;
166     // prepare for preserving folder struct
167     if (conv->preserve_folder_structure && conv->convert_items_count >= 1) {
168         // start with the 1st track path
169         deadbeef->pl_get_meta (conv->convert_items[0], ":URI", root, sizeof (root));
170         char *sep = strrchr (root, '/');
171         if (sep) {
172             *sep = 0;
173         }
174         // reduce
175         rootlen = strlen (root);
176         for (int n = 1; n < conv->convert_items_count; n++) {
177             deadbeef->pl_lock ();
178             const char *path = deadbeef->pl_find_meta (conv->convert_items[n], ":URI");
179             if (strncmp (path, root, rootlen)) {
180                 // find where path splits
181                 char *r = root;
182                 while (*path && *r) {
183                     if (*path != *r) {
184                         // find new separator
185                         while (r > root && *r != '/') {
186                             r--;
187                         }
188                         *r = 0;
189                         rootlen = r-root;
190                         break;
191                     }
192                     path++;
193                     r++;
194                 }
195             }
196             deadbeef->pl_unlock ();
197         }
198     }
199 
200     for (int n = 0; n < conv->convert_items_count; n++) {
201         update_progress_info_t *info = malloc (sizeof (update_progress_info_t));
202         info->entry = conv->progress_entry;
203         g_object_ref (info->entry);
204         deadbeef->pl_lock ();
205         info->text = strdup (deadbeef->pl_find_meta (conv->convert_items[n], ":URI"));
206         deadbeef->pl_unlock ();
207         g_idle_add (update_progress_cb, info);
208 
209         char outpath[2000];
210         converter_plugin->get_output_path2 (conv->convert_items[n], conv->convert_playlist, conv->outfolder, conv->outfile, conv->encoder_preset, conv->preserve_folder_structure, root, conv->write_to_source_folder, outpath, sizeof (outpath));
211 
212         int skip = 0;
213         char *real_out = realpath(outpath, NULL);
214         if (real_out) {
215             skip = 1;
216             deadbeef->pl_lock();
217             char *real_in = realpath(deadbeef->pl_find_meta(conv->convert_items[n], ":URI"), NULL);
218             deadbeef->pl_unlock();
219             const int paths_match = real_in && !strcmp(real_in, real_out);
220             free(real_in);
221             free(real_out);
222             if (paths_match) {
223                 fprintf (stderr, "converter: destination file is the same as source file, skipping\n");
224             }
225             else if (conv->overwrite_action == 2 || (conv->overwrite_action == 1 && overwrite_prompt(outpath))) {
226                 unlink (outpath);
227                 skip = 0;
228             }
229         }
230 
231         if (!skip) {
232             converter_plugin->convert (conv->convert_items[n], outpath, conv->output_bps, conv->output_is_float, conv->encoder_preset, conv->dsp_preset, &conv->cancelled);
233         }
234         if (conv->cancelled) {
235             for (; n < conv->convert_items_count; n++) {
236                 deadbeef->pl_item_unref (conv->convert_items[n]);
237             }
238             break;
239         }
240         deadbeef->pl_item_unref (conv->convert_items[n]);
241     }
242     g_idle_add (destroy_progress_cb, conv->progress);
243     if (conv->convert_items) {
244         free (conv->convert_items);
245     }
246     if (conv->convert_playlist) {
247         deadbeef->plt_unref (conv->convert_playlist);
248     }
249     if (conv->outfolder) {
250         free (conv->outfolder);
251     }
252     if (conv->outfile) {
253         free (conv->outfile);
254     }
255     converter_plugin->encoder_preset_free (conv->encoder_preset);
256     converter_plugin->dsp_preset_free (conv->dsp_preset);
257     free (conv);
258     deadbeef->background_job_decrement ();
259 }
260 
261 int
converter_process(converter_ctx_t * conv)262 converter_process (converter_ctx_t *conv)
263 {
264     conv->outfolder = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (conv->converter, "output_folder"))));
265     const char *outfile = gtk_entry_get_text (GTK_ENTRY (lookup_widget (conv->converter, "output_file")));
266     if (outfile[0] == 0) {
267         outfile = "%artist% - %title%";
268     }
269     conv->outfile = strdup (outfile);
270     conv->preserve_folder_structure = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (conv->converter, "preserve_folders")));
271     conv->write_to_source_folder = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (conv->converter, "write_to_source_folder")));
272     conv->overwrite_action = gtk_combo_box_get_active (GTK_COMBO_BOX (lookup_widget (conv->converter, "overwrite_action")));
273 
274     GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "output_format"));
275     int selected_format = gtk_combo_box_get_active (combo);
276     switch (selected_format) {
277     case 1 ... 4:
278         conv->output_bps = selected_format * 8;
279         conv->output_is_float = 0;
280         break;
281     case 5:
282         conv->output_bps = 32;
283         conv->output_is_float = 1;
284         break;
285     default:
286         conv->output_bps = -1; // same as input, or encoder default
287         break;
288     }
289 
290     combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "encoder"));
291     int enc_preset = gtk_combo_box_get_active (combo);
292     ddb_encoder_preset_t *encoder_preset = NULL;
293 
294     if (enc_preset >= 0) {
295         encoder_preset = converter_plugin->encoder_preset_get_for_idx (enc_preset);
296     }
297     if (!encoder_preset) {
298         GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (conv->converter), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Please select encoder"));
299         gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (conv->converter));
300         gtk_window_set_title (GTK_WINDOW (dlg), _("Converter error"));
301 
302         gtk_dialog_run (GTK_DIALOG (dlg));
303         gtk_widget_destroy (dlg);
304         return -1;
305     }
306 
307     combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "dsp_preset"));
308     int dsp_idx = gtk_combo_box_get_active (combo) - 1;
309 
310     ddb_dsp_preset_t *dsp_preset = NULL;
311     if (dsp_idx >= 0) {
312         dsp_preset = converter_plugin->dsp_preset_get_for_idx (dsp_idx);
313     }
314 
315     if (encoder_preset) {
316         conv->encoder_preset = converter_plugin->encoder_preset_alloc ();
317         converter_plugin->encoder_preset_copy (conv->encoder_preset, encoder_preset);
318     }
319     if (dsp_preset) {
320         conv->dsp_preset = converter_plugin->dsp_preset_alloc ();
321         converter_plugin->dsp_preset_copy (conv->dsp_preset, dsp_preset);
322     }
323 
324     GtkWidget *progress = gtk_dialog_new_with_buttons (_("Converting..."), GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
325     GtkWidget *vbox = gtk_dialog_get_content_area (GTK_DIALOG (progress));
326     GtkWidget *entry = gtk_entry_new ();
327     gtk_widget_set_size_request (entry, 400, -1);
328     gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE);
329     gtk_widget_show (entry);
330     gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 12);
331 
332     g_signal_connect ((gpointer)progress, "response", G_CALLBACK (on_converter_progress_cancel), conv);
333 
334     gtk_widget_show (progress);
335 
336     conv->progress = progress;
337     conv->progress_entry = entry;
338     intptr_t tid = deadbeef->thread_start (converter_worker, conv);
339     deadbeef->thread_detach (tid);
340     return 0;
341 }
342 
343 void
344 on_write_to_source_folder_toggled      (GtkToggleButton *togglebutton,
345                                         gpointer         user_data);
346 
347 static gboolean
converter_show_cb(void * data)348 converter_show_cb (void *data) {
349     int ctx = (intptr_t)data;
350     converter_ctx_t *conv = malloc (sizeof (converter_ctx_t));
351     current_ctx = conv;
352     memset (conv, 0, sizeof (converter_ctx_t));
353 
354     deadbeef->pl_lock ();
355     switch (ctx) {
356     case DDB_ACTION_CTX_MAIN:
357     case DDB_ACTION_CTX_SELECTION:
358         {
359             // copy list
360             ddb_playlist_t *plt = deadbeef->plt_get_curr ();
361             if (plt) {
362                 conv->convert_playlist = plt;
363                 conv->convert_items_count = deadbeef->plt_getselcount (plt);
364                 if (0 < conv->convert_items_count) {
365                     conv->convert_items = malloc (sizeof (DB_playItem_t *) * conv->convert_items_count);
366                     if (conv->convert_items) {
367                         int n = 0;
368                         DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN);
369                         while (it) {
370                             if (deadbeef->pl_is_selected (it)) {
371                                 assert (n < conv->convert_items_count);
372                                 deadbeef->pl_item_ref (it);
373                                 conv->convert_items[n++] = it;
374                             }
375                             DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN);
376                             deadbeef->pl_item_unref (it);
377                             it = next;
378                         }
379                     }
380                 }
381             }
382             break;
383         }
384     case DDB_ACTION_CTX_PLAYLIST:
385         {
386             // copy list
387             ddb_playlist_t *plt = deadbeef->action_get_playlist ();
388             if (plt) {
389                 conv->convert_playlist = plt;
390                 conv->convert_items_count = deadbeef->plt_get_item_count (plt, PL_MAIN);
391                 if (0 < conv->convert_items_count) {
392                     conv->convert_items = malloc (sizeof (DB_playItem_t *) * conv->convert_items_count);
393                     if (conv->convert_items) {
394                         int n = 0;
395                         DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN);
396                         while (it) {
397                             conv->convert_items[n++] = it;
398                             it = deadbeef->pl_get_next (it, PL_MAIN);
399                         }
400                     }
401                 }
402             }
403             break;
404         }
405     case DDB_ACTION_CTX_NOWPLAYING:
406         {
407             DB_playItem_t *it = deadbeef->streamer_get_playing_track ();
408             if (it) {
409                 conv->convert_playlist = deadbeef->pl_get_playlist (it);
410                 conv->convert_items_count = 1;
411                 conv->convert_items = malloc (sizeof (DB_playItem_t *) * conv->convert_items_count);
412                 if (conv->convert_items) {
413                     conv->convert_items[0] = it;
414                 }
415             }
416         }
417         break;
418     }
419     deadbeef->pl_unlock ();
420 
421     conv->converter = create_converterdlg ();
422     deadbeef->conf_lock ();
423     const char *out_folder = deadbeef->conf_get_str_fast ("converter.output_folder", "");
424     if (!out_folder[0]) {
425         out_folder = getenv("HOME");
426     }
427     gtk_entry_set_text (GTK_ENTRY (lookup_widget (conv->converter, "output_folder")), out_folder);
428     gtk_entry_set_text (GTK_ENTRY (lookup_widget (conv->converter, "output_file")), deadbeef->conf_get_str_fast ("converter.output_file_tf", ""));
429     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (conv->converter, "preserve_folders")), deadbeef->conf_get_int ("converter.preserve_folder_structure", 0));
430     int write_to_source_folder = deadbeef->conf_get_int ("converter.write_to_source_folder", 0);
431     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (conv->converter, "write_to_source_folder")), write_to_source_folder);
432 
433     g_signal_connect ((gpointer) lookup_widget (conv->converter, "write_to_source_folder"), "toggled",
434             G_CALLBACK (on_write_to_source_folder_toggled),
435             conv);
436 
437     gtk_widget_set_sensitive (lookup_widget (conv->converter, "output_folder"), !write_to_source_folder);
438     gtk_widget_set_sensitive (lookup_widget (conv->converter, "preserve_folders"), !write_to_source_folder);
439     gtk_combo_box_set_active (GTK_COMBO_BOX (lookup_widget (conv->converter, "overwrite_action")), deadbeef->conf_get_int ("converter.overwrite_action", 0));
440     deadbeef->conf_unlock ();
441 
442     GtkComboBox *combo;
443     // fill encoder presets
444     combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "encoder"));
445     GtkListStore *mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
446     fill_presets (mdl, (ddb_preset_t *)converter_plugin->encoder_preset_get_list (), PRESET_TYPE_ENCODER);
447     gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.encoder_preset", 0));
448 
449     // fill dsp presets
450     combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "dsp_preset"));
451     mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
452     GtkTreeIter iter;
453     gtk_list_store_append (mdl, &iter);
454     gtk_list_store_set (mdl, &iter, 0, "Pass through", -1);
455     fill_presets (mdl, (ddb_preset_t *)converter_plugin->dsp_preset_get_list (), PRESET_TYPE_DSP);
456 
457     gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.dsp_preset", -1) + 1);
458 
459     // select output format
460     combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "output_format"));
461     gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.output_format", 0));
462 
463     // overwrite action
464     combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "overwrite_action"));
465     gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.overwrite_action", 0));
466 
467     for (;;) {
468         int response = gtk_dialog_run (GTK_DIALOG (conv->converter));
469         if (response == GTK_RESPONSE_OK) {
470             int err = converter_process (conv);
471             if (err != 0) {
472                 continue;
473             }
474             gtk_widget_destroy (conv->converter);
475         }
476         else {
477             // FIXME: clean up properly
478             gtk_widget_destroy (conv->converter);
479             if (conv->convert_items) {
480                 for (int n = 0; n < conv->convert_items_count; n++) {
481                     deadbeef->pl_item_unref (conv->convert_items[n]);
482                 }
483                 free (conv->convert_items);
484             }
485             free (conv);
486         }
487         current_ctx = NULL;
488         break;
489     }
490     converter_active = 0;
491     return FALSE;
492 }
493 
494 static int
converter_show(DB_plugin_action_t * act,int ctx)495 converter_show (DB_plugin_action_t *act, int ctx) {
496     if (converter_active) {
497         return -1;
498     }
499     converter_active = 1;
500     if (converter_plugin->misc.plugin.version_minor >= 1) {
501         // reload all presets
502         converter_plugin->free_encoder_presets ();
503         converter_plugin->load_encoder_presets ();
504         converter_plugin->free_dsp_presets ();
505         converter_plugin->load_dsp_presets ();
506     }
507     // this can be called from non-gtk thread
508     gdk_threads_add_idle (converter_show_cb, (void *)(intptr_t)ctx);
509     return 0;
510 }
511 
512 void
on_converter_encoder_changed(GtkComboBox * combobox,gpointer user_data)513 on_converter_encoder_changed           (GtkComboBox     *combobox,
514                                         gpointer         user_data)
515 {
516     GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder"));
517     int act = gtk_combo_box_get_active (combo);
518     deadbeef->conf_set_int ("converter.encoder_preset", act);
519     deadbeef->conf_save ();
520 }
521 
522 void
on_converter_dsp_preset_changed(GtkComboBox * combobox,gpointer user_data)523 on_converter_dsp_preset_changed        (GtkComboBox     *combobox,
524                                         gpointer         user_data)
525 {
526     GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset"));
527     int act = gtk_combo_box_get_active (combo);
528     deadbeef->conf_set_int ("converter.dsp_preset", act-1);
529     deadbeef->conf_save ();
530 }
531 
532 void
on_converter_output_browse_clicked(GtkButton * button,gpointer user_data)533 on_converter_output_browse_clicked     (GtkButton       *button,
534                                         gpointer         user_data)
535 {
536     GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Select folder..."), GTK_WINDOW (current_ctx->converter), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
537     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (current_ctx->converter));
538 
539     gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE);
540     // restore folder
541     deadbeef->conf_lock ();
542     char dir[2000];
543     deadbeef->conf_get_str ("converter.lastdir", "", dir, sizeof (dir));
544     if (!dir[0]) {
545         const char *out_folder = deadbeef->conf_get_str_fast ("converter.output_folder", "");
546         if (!out_folder[0]) {
547             out_folder = getenv("HOME");
548         }
549         snprintf (dir, sizeof (dir), "file://%s", out_folder);
550     }
551 
552     gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), dir);
553     deadbeef->conf_unlock ();
554     int response = gtk_dialog_run (GTK_DIALOG (dlg));
555     // store folder
556     gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
557     if (folder) {
558         deadbeef->conf_set_str ("converter.lastdir", folder);
559         g_free (folder);
560     }
561     if (response == GTK_RESPONSE_OK) {
562         folder = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
563         gtk_widget_destroy (dlg);
564         if (folder) {
565             GtkWidget *entry = lookup_widget (current_ctx->converter, "output_folder");
566             gtk_entry_set_text (GTK_ENTRY (entry), folder);
567             g_free (folder);
568         }
569     }
570     else {
571         gtk_widget_destroy (dlg);
572     }
573 }
574 
575 
576 void
on_output_folder_changed(GtkEntry * entry,gpointer user_data)577 on_output_folder_changed               (GtkEntry     *entry,
578                                         gpointer         user_data)
579 {
580     deadbeef->conf_set_str ("converter.output_folder", gtk_entry_get_text (entry));
581     deadbeef->conf_save ();
582 }
583 
584 
585 void
on_numthreads_changed(GtkEditable * editable,gpointer user_data)586 on_numthreads_changed                  (GtkEditable     *editable,
587                                         gpointer         user_data)
588 {
589     deadbeef->conf_set_int ("converter.threads", gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editable)));
590     deadbeef->conf_save ();
591 }
592 
593 void
on_overwrite_action_changed(GtkComboBox * combobox,gpointer user_data)594 on_overwrite_action_changed            (GtkComboBox     *combobox,
595                                         gpointer         user_data)
596 {
597     deadbeef->conf_set_int ("converter.overwrite_action", gtk_combo_box_get_active (combobox));
598     deadbeef->conf_save ();
599 }
600 
601 void
on_encoder_changed(GtkEditable * editable,gpointer user_data)602 on_encoder_changed                     (GtkEditable     *editable,
603                                         gpointer         user_data)
604 {
605      gtk_widget_set_has_tooltip (GTK_WIDGET (editable), TRUE);
606 
607      char enc[2000];
608      const char *e = gtk_entry_get_text (GTK_ENTRY (editable));
609      char *o = enc;
610      *o = 0;
611      int len = sizeof (enc);
612      while (e && *e) {
613          if (len <= 0) {
614              break;
615          }
616          if (e[0] == '%' && e[1]) {
617              if (e[1] == 'o') {
618                  int l = snprintf (o, len, "\"OUTPUT_FILE_NAME\"");
619                  o += l;
620                  len -= l;
621              }
622              else if (e[1] == 'i') {
623                  int l = snprintf (o, len, "\"TEMP_FILE_NAME\"");
624                  o += l;
625                  len -= l;
626              }
627              else {
628                  strncpy (o, e, 2);
629                  o += 2;
630                  len -= 2;
631              }
632              e += 2;
633          }
634          else {
635              *o++ = *e++;
636              *o = 0;
637              len--;
638          }
639      }
640 
641      gtk_widget_set_tooltip_text (GTK_WIDGET (editable), enc);
642 }
643 
644 void
on_output_file_changed(GtkEntry * entry,gpointer user_data)645 on_output_file_changed                 (GtkEntry        *entry,
646                                         gpointer         user_data)
647 {
648     deadbeef->conf_set_str ("converter.output_file_tf", gtk_entry_get_text (entry));
649     deadbeef->conf_save ();
650 }
651 
652 void
on_preserve_folders_toggled(GtkToggleButton * togglebutton,gpointer user_data)653 on_preserve_folders_toggled            (GtkToggleButton *togglebutton,
654                                         gpointer         user_data)
655 {
656     deadbeef->conf_set_int ("converter.preserve_folder_structure", gtk_toggle_button_get_active (togglebutton));
657     deadbeef->conf_save ();
658 }
659 
660 void
on_write_to_source_folder_toggled(GtkToggleButton * togglebutton,gpointer user_data)661 on_write_to_source_folder_toggled      (GtkToggleButton *togglebutton,
662                                         gpointer         user_data)
663 {
664     int active = gtk_toggle_button_get_active (togglebutton);
665     converter_ctx_t *conv = user_data;
666     deadbeef->conf_set_int ("converter.write_to_source_folder", active);
667     gtk_widget_set_sensitive (lookup_widget (conv->converter, "output_folder"), !active);
668     gtk_widget_set_sensitive (lookup_widget (conv->converter, "preserve_folders"), !active);
669 }
670 
671 
672 DB_decoder_t *
plug_get_decoder_for_id(const char * id)673 plug_get_decoder_for_id (const char *id) {
674     DB_decoder_t **plugins = deadbeef->plug_get_decoder_list ();
675     for (int c = 0; plugins[c]; c++) {
676         if (!strcmp (id, plugins[c]->plugin.id)) {
677             return plugins[c];
678         }
679     }
680     return NULL;
681 }
682 
683 void
init_encoder_preset_from_dlg(GtkWidget * dlg,ddb_encoder_preset_t * p)684 init_encoder_preset_from_dlg (GtkWidget *dlg, ddb_encoder_preset_t *p) {
685     p->title = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (dlg, "title"))));
686     p->ext = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (dlg, "ext"))));
687     p->encoder = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (dlg, "encoder"))));
688     int method_idx = gtk_combo_box_get_active (GTK_COMBO_BOX (lookup_widget (dlg, "method")));
689     switch (method_idx) {
690     case 0:
691         p->method = DDB_ENCODER_METHOD_PIPE;
692         break;
693     case 1:
694         p->method = DDB_ENCODER_METHOD_FILE;
695         break;
696     }
697 
698     p->id3v2_version = gtk_combo_box_get_active (GTK_COMBO_BOX (lookup_widget (dlg, "id3v2_version")));
699     p->tag_id3v2 = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "id3v2")));
700     p->tag_id3v1 = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "id3v1")));
701     p->tag_apev2 = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "apev2")));
702     p->tag_flac = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "flac")));
703     p->tag_oggvorbis = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "oggvorbis")));
704 }
705 
706 int
edit_encoder_preset(char * title,GtkWidget * toplevel)707 edit_encoder_preset (char *title, GtkWidget *toplevel) {
708     GtkWidget *dlg = create_convpreset_editor ();
709     gtk_window_set_title (GTK_WINDOW (dlg), title);
710     gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK);
711 
712     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel));
713     ddb_encoder_preset_t *p = current_ctx->current_encoder_preset;
714 
715     if (p->title) {
716         gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "title")), p->title);
717     }
718     if (p->ext) {
719         gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "ext")), p->ext);
720     }
721     if (p->encoder) {
722         gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "encoder")), p->encoder);
723     }
724     gtk_combo_box_set_active (GTK_COMBO_BOX (lookup_widget (dlg, "method")), p->method);
725 
726     gtk_combo_box_set_active (GTK_COMBO_BOX (lookup_widget (dlg, "id3v2_version")), p->id3v2_version);
727     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "id3v2")), p->tag_id3v2);
728     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "id3v1")), p->tag_id3v1);
729     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "apev2")), p->tag_apev2);
730     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "flac")), p->tag_flac);
731     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "oggvorbis")), p->tag_oggvorbis);
732 
733     ddb_encoder_preset_t *old = p;
734     int r = GTK_RESPONSE_CANCEL;
735     for (;;) {
736         r = gtk_dialog_run (GTK_DIALOG (dlg));
737         if (r == GTK_RESPONSE_OK) {
738             ddb_encoder_preset_t *p = converter_plugin->encoder_preset_alloc ();
739             if (p) {
740                 init_encoder_preset_from_dlg (dlg, p);
741                 int err = 0;
742 
743                 ddb_encoder_preset_t *pp = converter_plugin->encoder_preset_get_list ();
744                 for (; pp; pp = pp->next) {
745                     if (pp != old && !strcmp (pp->title, p->title)) {
746                         err = -2;
747                         break;
748                     }
749                 }
750 
751                 if (!err) {
752                     err = converter_plugin->encoder_preset_save (p, 1);
753                 }
754                 if (!err) {
755                     if (old->title && strcmp (p->title, old->title)) {
756                         char path[1024];
757                         if (snprintf (path, sizeof (path), "%s/presets/encoders/%s.txt", deadbeef->get_config_dir (), old->title) > 0) {
758                             unlink (path);
759                         }
760                     }
761                     free (old->title);
762                     free (old->ext);
763                     free (old->encoder);
764 
765                     converter_plugin->encoder_preset_copy (old, p);
766                     converter_plugin->encoder_preset_free (p);
767                 }
768                 else {
769                     GtkWidget *warndlg = gtk_message_dialog_new (GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Failed to save encoder preset"));
770                     gtk_window_set_transient_for (GTK_WINDOW (warndlg), GTK_WINDOW (dlg));
771                     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (warndlg), err == -1 ? _("Check preset folder permissions, try to pick different title, or free up some disk space") : _("Preset with the same name already exists. Try to pick another title."));
772                     gtk_window_set_title (GTK_WINDOW (warndlg), _("Error"));
773 
774                     /*int response = */gtk_dialog_run (GTK_DIALOG (warndlg));
775                     gtk_widget_destroy (warndlg);
776                     continue;
777                 }
778             }
779         }
780         break;
781     }
782 
783     gtk_widget_destroy (dlg);
784     return r;
785 }
786 
787 void
refresh_encoder_lists(GtkComboBox * combo,GtkTreeView * list)788 refresh_encoder_lists (GtkComboBox *combo, GtkTreeView *list) {
789     // presets list view
790     GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list)));
791 
792     GtkTreePath *path;
793     GtkTreeViewColumn *col;
794     gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
795     int idx = -1;
796     if (path && col) {
797         int *indices = gtk_tree_path_get_indices (path);
798         idx = *indices;
799         g_free (indices);
800     }
801 
802     gtk_list_store_clear (mdl);
803     fill_presets (mdl, (ddb_preset_t *)converter_plugin->encoder_preset_get_list (), PRESET_TYPE_ENCODER);
804     if (idx != -1) {
805         path = gtk_tree_path_new_from_indices (idx, -1);
806         gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE);
807         gtk_tree_path_free (path);
808     }
809 
810     // presets combo box
811     int act = gtk_combo_box_get_active (combo);
812     mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
813     gtk_list_store_clear (mdl);
814     fill_presets (mdl, (ddb_preset_t *)converter_plugin->encoder_preset_get_list (), PRESET_TYPE_ENCODER);
815     gtk_combo_box_set_active (combo, act);
816 }
817 
818 void
on_encoder_preset_add(GtkButton * button,gpointer user_data)819 on_encoder_preset_add                     (GtkButton       *button,
820                                         gpointer         user_data)
821 {
822     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
823 
824     current_ctx->current_encoder_preset = converter_plugin->encoder_preset_alloc ();
825 
826     if (GTK_RESPONSE_OK == edit_encoder_preset (_("Add new encoder"), toplevel)) {
827         converter_plugin->encoder_preset_append (current_ctx->current_encoder_preset);
828         GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder"));
829         GtkWidget *list = lookup_widget (toplevel, "presets");
830         refresh_encoder_lists (combo, GTK_TREE_VIEW (list));
831     }
832 
833     current_ctx->current_encoder_preset = NULL;
834 }
835 
836 void
on_encoder_preset_edit(GtkButton * button,gpointer user_data)837 on_encoder_preset_edit                     (GtkButton       *button,
838                                         gpointer         user_data)
839 {
840     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
841     GtkWidget *list = lookup_widget (toplevel, "presets");
842     GtkTreePath *path;
843     GtkTreeViewColumn *col;
844     gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
845     if (!path || !col) {
846         // nothing selected
847         return;
848     }
849     int *indices = gtk_tree_path_get_indices (path);
850     int idx = *indices;
851     g_free (indices);
852 
853     ddb_encoder_preset_t *p = converter_plugin->encoder_preset_get_for_idx (idx);
854     current_ctx->current_encoder_preset = p;
855 
856     int r = edit_encoder_preset (_("Edit encoder"), toplevel);
857     if (r == GTK_RESPONSE_OK) {
858         GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder"));
859         refresh_encoder_lists (combo, GTK_TREE_VIEW (list));
860     }
861 
862     current_ctx->current_encoder_preset = NULL;
863 }
864 
865 void
on_encoder_preset_remove(GtkButton * button,gpointer user_data)866 on_encoder_preset_remove                     (GtkButton       *button,
867                                         gpointer         user_data)
868 {
869 
870     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
871     GtkWidget *list = lookup_widget (toplevel, "presets");
872     GtkTreePath *path;
873     GtkTreeViewColumn *col;
874     gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
875     if (!path || !col) {
876         // nothing selected
877         return;
878     }
879     int *indices = gtk_tree_path_get_indices (path);
880     int idx = *indices;
881     g_free (indices);
882 
883     ddb_encoder_preset_t *p = converter_plugin->encoder_preset_get_for_idx (idx);
884     if (!p) {
885         return;
886     }
887 
888     GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Remove preset"));
889     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel));
890     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), _("This action will delete the selected preset. Are you sure?"));
891     gtk_window_set_title (GTK_WINDOW (dlg), _("Warning"));
892 
893     int response = gtk_dialog_run (GTK_DIALOG (dlg));
894     gtk_widget_destroy (dlg);
895     if (response == GTK_RESPONSE_YES) {
896         char path[1024];
897         if (snprintf (path, sizeof (path), "%s/presets/encoders/%s.txt", deadbeef->get_config_dir (), p->title) > 0) {
898             unlink (path);
899         }
900 
901         converter_plugin->encoder_preset_remove (p);
902         converter_plugin->encoder_preset_free (p);
903 
904         GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder"));
905         refresh_encoder_lists (combo, GTK_TREE_VIEW (list));
906     }
907 }
908 
909 static GtkWidget *encpreset_dialog;
910 
911 static void
on_encoder_preset_cursor_changed(GtkTreeView * treeview,gpointer user_data)912 on_encoder_preset_cursor_changed (GtkTreeView     *treeview,
913                                         gpointer         user_data) {
914     if (!encpreset_dialog) {
915         return;
916     }
917     GtkWidget *edit = lookup_widget (encpreset_dialog, "edit");
918     GtkWidget *remove = lookup_widget (encpreset_dialog, "remove");
919 
920     GtkTreePath *path;
921     GtkTreeViewColumn *col;
922     gtk_tree_view_get_cursor (treeview, &path, &col);
923     if (!path || !col) {
924         // nothing selected
925         gtk_widget_set_sensitive (edit, FALSE);
926         gtk_widget_set_sensitive (remove, FALSE);
927         return;
928     }
929     int *indices = gtk_tree_path_get_indices (path);
930     int idx = *indices;
931     g_free (indices);
932 
933     ddb_encoder_preset_t *p = converter_plugin->encoder_preset_get_for_idx (idx);
934     gtk_widget_set_sensitive (edit, !p->readonly);
935     gtk_widget_set_sensitive (remove, !p->readonly);
936 }
937 
938 
939 void
on_encoder_preset_copy(GtkButton * button,gpointer user_data)940 on_encoder_preset_copy (GtkButton *button, gpointer user_data)
941 {
942     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
943 
944     GtkTreeView *treeview = GTK_TREE_VIEW (lookup_widget (toplevel, "presets"));
945 
946     GtkTreePath *path;
947     GtkTreeViewColumn *col;
948     gtk_tree_view_get_cursor (treeview, &path, &col);
949     if (!path || !col) {
950         return;
951     }
952     int *indices = gtk_tree_path_get_indices (path);
953     int idx = *indices;
954     g_free (indices);
955 
956     ddb_encoder_preset_t *p = converter_plugin->encoder_preset_get_for_idx (idx);
957 
958     current_ctx->current_encoder_preset = converter_plugin->encoder_preset_alloc ();
959     if (!current_ctx->current_encoder_preset) {
960         return;
961     }
962     converter_plugin->encoder_preset_copy (current_ctx->current_encoder_preset, p);
963 
964     if (GTK_RESPONSE_OK == edit_encoder_preset (_("Add new encoder"), toplevel)) {
965         converter_plugin->encoder_preset_append (current_ctx->current_encoder_preset);
966         GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder"));
967         refresh_encoder_lists (combo, treeview);
968     }
969 
970     current_ctx->current_encoder_preset = NULL;
971 }
972 
973 void
on_edit_encoder_presets_clicked(GtkButton * button,gpointer user_data)974 on_edit_encoder_presets_clicked        (GtkButton       *button,
975                                         gpointer         user_data)
976 {
977     GtkWidget *dlg = create_preset_list ();
978     encpreset_dialog = dlg;
979     gtk_window_set_title (GTK_WINDOW (dlg), _("Encoders"));
980     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (current_ctx->converter));
981     g_signal_connect ((gpointer)lookup_widget (dlg, "add"), "clicked", G_CALLBACK (on_encoder_preset_add), NULL);
982     g_signal_connect ((gpointer)lookup_widget (dlg, "remove"), "clicked", G_CALLBACK (on_encoder_preset_remove), NULL);
983     g_signal_connect ((gpointer)lookup_widget (dlg, "edit"), "clicked", G_CALLBACK (on_encoder_preset_edit), NULL);
984     g_signal_connect ((gpointer)lookup_widget (dlg, "copy"), "clicked", G_CALLBACK (on_encoder_preset_copy), NULL);
985 
986     GtkWidget *list = lookup_widget (dlg, "presets");
987     g_signal_connect ((gpointer)list, "cursor-changed", G_CALLBACK (on_encoder_preset_cursor_changed), NULL);
988     GtkCellRenderer *title_cell = gtk_cell_renderer_text_new ();
989     GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("Title"), title_cell, "text", 0, NULL);
990     gtk_tree_view_append_column (GTK_TREE_VIEW (list), GTK_TREE_VIEW_COLUMN (col));
991     GtkListStore *mdl = gtk_list_store_new (1, G_TYPE_STRING);
992     gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (mdl));
993     fill_presets (mdl, (ddb_preset_t *)converter_plugin->encoder_preset_get_list (), PRESET_TYPE_ENCODER);
994     int curr = deadbeef->conf_get_int ("converter.encoder_preset", -1);
995     if (curr != -1) {
996         GtkTreePath *path = gtk_tree_path_new_from_indices (curr, -1);
997         if (path && gtk_tree_path_get_depth (path) > 0) {
998             gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE);
999             gtk_tree_path_free (path);
1000         }
1001     }
1002     on_encoder_preset_cursor_changed (GTK_TREE_VIEW (list), NULL);
1003     gtk_dialog_run (GTK_DIALOG (dlg));
1004     gtk_widget_destroy (dlg);
1005     encpreset_dialog = NULL;
1006 }
1007 
1008 ///// dsp preset gui
1009 
1010 void
fill_dsp_plugin_list(GtkListStore * mdl)1011 fill_dsp_plugin_list (GtkListStore *mdl) {
1012     struct DB_dsp_s **dsp = deadbeef->plug_get_dsp_list ();
1013     int i;
1014     for (i = 0; dsp[i]; i++) {
1015         GtkTreeIter iter;
1016         gtk_list_store_append (mdl, &iter);
1017         gtk_list_store_set (mdl, &iter, 0, dsp[i]->plugin.name, -1);
1018     }
1019 }
1020 
1021 void
fill_dsp_preset_chain(GtkListStore * mdl)1022 fill_dsp_preset_chain (GtkListStore *mdl) {
1023     ddb_dsp_context_t *dsp = current_ctx->current_dsp_preset->chain;
1024     while (dsp) {
1025         GtkTreeIter iter;
1026         gtk_list_store_append (mdl, &iter);
1027         gtk_list_store_set (mdl, &iter, 0, dsp->plugin->plugin.name, -1);
1028         dsp = dsp->next;
1029     }
1030 }
1031 
1032 static int
listview_get_index(GtkWidget * list)1033 listview_get_index (GtkWidget *list) {
1034     GtkTreePath *path;
1035     GtkTreeViewColumn *col;
1036     gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
1037     if (!path) {
1038         // nothing selected
1039         return -1;
1040     }
1041     int *indices = gtk_tree_path_get_indices (path);
1042     int idx = *indices;
1043     g_free (indices);
1044     return idx;
1045 }
1046 
1047 void
on_dsp_preset_add_plugin_clicked(GtkButton * button,gpointer user_data)1048 on_dsp_preset_add_plugin_clicked       (GtkButton       *button,
1049                                         gpointer         user_data)
1050 {
1051     GtkWidget *dlg = create_select_dsp_plugin ();
1052     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1053     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel));
1054     gtk_window_set_title (GTK_WINDOW (dlg), _("Add plugin to DSP chain"));
1055 
1056     GtkComboBox *combo;
1057     // fill encoder presets
1058     combo = GTK_COMBO_BOX (lookup_widget (dlg, "plugin"));
1059     GtkListStore *mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
1060     fill_dsp_plugin_list (mdl);
1061     gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.last_selected_dsp", 0));
1062 
1063     int r = gtk_dialog_run (GTK_DIALOG (dlg));
1064     if (r == GTK_RESPONSE_OK) {
1065         // create new instance of the selected plugin
1066         int idx = gtk_combo_box_get_active (combo);
1067         struct DB_dsp_s **dsp = deadbeef->plug_get_dsp_list ();
1068         int i;
1069         ddb_dsp_context_t *inst = NULL;
1070         for (i = 0; dsp[i]; i++) {
1071             if (i == idx) {
1072                 inst = dsp[i]->open ();
1073                 break;
1074             }
1075         }
1076         if (inst) {
1077             // append to DSP chain
1078             ddb_dsp_context_t *tail = current_ctx->current_dsp_preset->chain;
1079             while (tail && tail->next) {
1080                 tail = tail->next;
1081             }
1082             if (tail) {
1083                 tail->next = inst;
1084             }
1085             else {
1086                 current_ctx->current_dsp_preset->chain = inst;
1087             }
1088 
1089             // reinit list of instances
1090             GtkWidget *list = lookup_widget (toplevel, "plugins");
1091             GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list)));
1092             int idx = listview_get_index (list);
1093             gtk_list_store_clear (mdl);
1094             fill_dsp_preset_chain (mdl);
1095             GtkTreePath *path = gtk_tree_path_new_from_indices (idx, -1);
1096             gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, NULL, FALSE);
1097             gtk_tree_path_free (path);
1098         }
1099         else {
1100             fprintf (stderr, "converter: failed to add DSP plugin to chain\n");
1101         }
1102     }
1103     gtk_widget_destroy (dlg);
1104 }
1105 
1106 
1107 void
on_dsp_preset_remove_plugin_clicked(GtkButton * button,gpointer user_data)1108 on_dsp_preset_remove_plugin_clicked    (GtkButton       *button,
1109                                         gpointer         user_data)
1110 {
1111     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1112     GtkWidget *list = lookup_widget (toplevel, "plugins");
1113     GtkTreePath *path;
1114     GtkTreeViewColumn *col;
1115     gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
1116     if (!path || !col) {
1117         // nothing selected
1118         return;
1119     }
1120     int *indices = gtk_tree_path_get_indices (path);
1121     int idx = *indices;
1122     g_free (indices);
1123     if (idx == -1) {
1124         return;
1125     }
1126 
1127     ddb_dsp_context_t *p = current_ctx->current_dsp_preset->chain;
1128     ddb_dsp_context_t *prev = NULL;
1129     int i = idx;
1130     while (p && i--) {
1131         prev = p;
1132         p = p->next;
1133     }
1134     if (p) {
1135         if (prev) {
1136             prev->next = p->next;
1137         }
1138         else {
1139             current_ctx->current_dsp_preset->chain = p->next;
1140         }
1141         p->plugin->close (p);
1142         GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list)));
1143         gtk_list_store_clear (mdl);
1144         fill_dsp_preset_chain (mdl);
1145         path = gtk_tree_path_new_from_indices (idx, -1);
1146         gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE);
1147         gtk_tree_path_free (path);
1148     }
1149 }
1150 
1151 static ddb_dsp_context_t *current_dsp_context = NULL;
1152 
1153 void
dsp_ctx_set_param(const char * key,const char * value)1154 dsp_ctx_set_param (const char *key, const char *value) {
1155     current_dsp_context->plugin->set_param (current_dsp_context, atoi (key), value);
1156 }
1157 
1158 void
dsp_ctx_get_param(const char * key,char * value,int len,const char * def)1159 dsp_ctx_get_param (const char *key, char *value, int len, const char *def) {
1160     strncpy (value, def, len);
1161     current_dsp_context->plugin->get_param (current_dsp_context, atoi (key), value, len);
1162 }
1163 
1164 void
on_dsp_preset_plugin_configure_clicked(GtkButton * button,gpointer user_data)1165 on_dsp_preset_plugin_configure_clicked (GtkButton       *button,
1166                                         gpointer         user_data)
1167 {
1168     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1169     GtkWidget *list = lookup_widget (toplevel, "plugins");
1170     int idx = listview_get_index (list);
1171     if (idx == -1) {
1172         return;
1173     }
1174     ddb_dsp_context_t *p = current_ctx->current_dsp_preset->chain;
1175     int i = idx;
1176     while (p && i--) {
1177         p = p->next;
1178     }
1179     if (!p || !p->plugin->configdialog) {
1180         return;
1181     }
1182     current_dsp_context = p;
1183     ddb_dialog_t conf = {
1184         .title = p->plugin->plugin.name,
1185         .layout = p->plugin->configdialog,
1186         .set_param = dsp_ctx_set_param,
1187         .get_param = dsp_ctx_get_param,
1188         .parent = toplevel
1189     };
1190     gtkui_plugin->gui.run_dialog (&conf, 0, NULL, NULL);
1191     current_dsp_context = NULL;
1192 }
1193 
1194 static int
swap_items(GtkWidget * list,int idx)1195 swap_items (GtkWidget *list, int idx) {
1196     ddb_dsp_context_t *prev = NULL;
1197     ddb_dsp_context_t *p = current_ctx->current_dsp_preset->chain;
1198 
1199     int n = idx;
1200     while (n > 0 && p) {
1201         prev = p;
1202         p = p->next;
1203         n--;
1204     }
1205 
1206     if (!p || !p->next) {
1207         return -1;
1208     }
1209 
1210     ddb_dsp_context_t *moved = p->next;
1211 
1212     if (!moved) {
1213         return -1;
1214     }
1215 
1216     ddb_dsp_context_t *last = moved ? moved->next : NULL;
1217 
1218     if (prev) {
1219         p->next = last;
1220         prev->next = moved;
1221         moved->next = p;
1222     }
1223     else {
1224         p->next = last;
1225         current_ctx->current_dsp_preset->chain = moved;
1226         moved->next = p;
1227     }
1228     GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list)));
1229     gtk_list_store_clear (mdl);
1230     fill_dsp_preset_chain (mdl);
1231     return 0;
1232 }
1233 
1234 void
on_dsp_preset_plugin_up_clicked(GtkButton * button,gpointer user_data)1235 on_dsp_preset_plugin_up_clicked        (GtkButton       *button,
1236                                         gpointer         user_data)
1237 {
1238     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1239     GtkWidget *list = lookup_widget (toplevel, "plugins");
1240     int idx = listview_get_index (list);
1241     if (idx <= 0) {
1242         return;
1243     }
1244 
1245     if (-1 == swap_items (list, idx-1)) {
1246         return;
1247     }
1248     GtkTreePath *path = gtk_tree_path_new_from_indices (idx-1, -1);
1249     gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, NULL, FALSE);
1250     gtk_tree_path_free (path);
1251 }
1252 
1253 
1254 void
on_dsp_preset_plugin_down_clicked(GtkButton * button,gpointer user_data)1255 on_dsp_preset_plugin_down_clicked      (GtkButton       *button,
1256                                         gpointer         user_data)
1257 {
1258     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1259     GtkWidget *list = lookup_widget (toplevel, "plugins");
1260     int idx = listview_get_index (list);
1261     if (idx == -1) {
1262         return;
1263     }
1264 
1265     if (-1 == swap_items (list, idx)) {
1266         return;
1267     }
1268     GtkTreePath *path = gtk_tree_path_new_from_indices (idx+1, -1);
1269     gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, NULL, FALSE);
1270     gtk_tree_path_free (path);
1271 }
1272 
1273 
1274 int
edit_dsp_preset(const char * title,GtkWidget * toplevel,ddb_dsp_preset_t * orig)1275 edit_dsp_preset (const char *title, GtkWidget *toplevel, ddb_dsp_preset_t *orig) {
1276     int r = GTK_RESPONSE_CANCEL;
1277 
1278     GtkWidget *dlg = create_dsppreset_editor ();
1279     gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK);
1280     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel));
1281     gtk_window_set_title (GTK_WINDOW (dlg), title);
1282 
1283     ddb_dsp_preset_t *p = current_ctx->current_dsp_preset;
1284 
1285     // title
1286     if (p->title) {
1287         gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "title")), p->title);
1288     }
1289 
1290     {
1291         GtkWidget *list = lookup_widget (dlg, "plugins");
1292         GtkCellRenderer *title_cell = gtk_cell_renderer_text_new ();
1293         GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("Plugin"), title_cell, "text", 0, NULL);
1294         gtk_tree_view_append_column (GTK_TREE_VIEW (list), GTK_TREE_VIEW_COLUMN (col));
1295         GtkListStore *mdl = gtk_list_store_new (1, G_TYPE_STRING);
1296         gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (mdl));
1297 
1298         fill_dsp_preset_chain (mdl);
1299         GtkTreePath *path = gtk_tree_path_new_from_indices (0, -1);
1300         gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, NULL, FALSE);
1301         gtk_tree_path_free (path);
1302     }
1303 
1304     for (;;) {
1305         r = gtk_dialog_run (GTK_DIALOG (dlg));
1306 
1307         if (r == GTK_RESPONSE_OK) {
1308             const char *title = gtk_entry_get_text (GTK_ENTRY (lookup_widget (dlg, "title")));
1309             int err = 0;
1310 
1311             // don't allow duplicate title with existing presets
1312             ddb_dsp_preset_t *pp = converter_plugin->dsp_preset_get_list ();
1313             for (; pp; pp = pp->next) {
1314                 if (pp != orig && !strcmp (pp->title, title)) {
1315                     err = -2;
1316                     break;
1317                 }
1318             }
1319 
1320             if (!err) {
1321                 if (current_ctx->current_dsp_preset->title) {
1322                     free (current_ctx->current_dsp_preset->title);
1323                 }
1324                 current_ctx->current_dsp_preset->title = strdup (title);
1325                 err = converter_plugin->dsp_preset_save (current_ctx->current_dsp_preset, 1);
1326             }
1327 
1328             if (err < 0) {
1329                 GtkWidget *warndlg = gtk_message_dialog_new (GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Failed to save DSP preset"));
1330                 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (warndlg), err == -1 ? _("Check preset folder permissions, try to pick different title, or free up some disk space") : _("Preset with the same name already exists. Try to pick another title."));
1331                 gtk_window_set_title (GTK_WINDOW (warndlg), _("Error"));
1332 
1333                 gtk_window_set_transient_for (GTK_WINDOW (warndlg), GTK_WINDOW (dlg));
1334                 /*int response = */gtk_dialog_run (GTK_DIALOG (warndlg));
1335                 gtk_widget_destroy (warndlg);
1336                 continue;
1337             }
1338 
1339         }
1340 
1341         break;
1342     }
1343 
1344     gtk_widget_destroy (dlg);
1345     return r;
1346 }
1347 
1348 void
refresh_dsp_lists(GtkComboBox * combo,GtkTreeView * list)1349 refresh_dsp_lists (GtkComboBox *combo, GtkTreeView *list) {
1350     // presets list view
1351     GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list)));
1352 
1353     GtkTreePath *path;
1354     GtkTreeViewColumn *col;
1355     int idx = -1;
1356 
1357     gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
1358     if (path && col) {
1359         int *indices = gtk_tree_path_get_indices (path);
1360         idx = *indices;
1361         g_free (indices);
1362     }
1363 
1364     gtk_list_store_clear (mdl);
1365     fill_presets (mdl, (ddb_preset_t *)converter_plugin->dsp_preset_get_list (), PRESET_TYPE_DSP);
1366     if (idx != -1) {
1367         path = gtk_tree_path_new_from_indices (idx, -1);
1368         gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE);
1369         gtk_tree_path_free (path);
1370     }
1371 
1372     // presets combo box
1373     int act = gtk_combo_box_get_active (combo);
1374     mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
1375     gtk_list_store_clear (mdl);
1376     GtkTreeIter iter;
1377     gtk_list_store_append (mdl, &iter);
1378     gtk_list_store_set (mdl, &iter, 0, "Pass through", -1);
1379     fill_presets (mdl, (ddb_preset_t *)converter_plugin->dsp_preset_get_list (), PRESET_TYPE_DSP);
1380     gtk_combo_box_set_active (combo, act);
1381 }
1382 
1383 
1384 void
on_dsp_preset_add(GtkButton * button,gpointer user_data)1385 on_dsp_preset_add                     (GtkButton       *button,
1386                                         gpointer         user_data)
1387 {
1388 
1389     current_ctx->current_dsp_preset = converter_plugin->dsp_preset_alloc ();
1390 
1391     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1392 
1393     if (GTK_RESPONSE_OK == edit_dsp_preset (_("New DSP Preset"), toplevel, NULL)) {
1394         converter_plugin->dsp_preset_append (current_ctx->current_dsp_preset);
1395         GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset"));
1396         GtkWidget *list = lookup_widget (toplevel, "presets");
1397         refresh_dsp_lists (combo, GTK_TREE_VIEW (list));
1398     }
1399     else {
1400         converter_plugin->dsp_preset_free (current_ctx->current_dsp_preset);
1401     }
1402 
1403     current_ctx->current_dsp_preset = NULL;
1404 }
1405 
1406 void
on_dsp_preset_copy(GtkButton * button,gpointer user_data)1407 on_dsp_preset_copy (GtkButton *button, gpointer user_data)
1408 {
1409     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1410 
1411     GtkTreeView *treeview = GTK_TREE_VIEW (lookup_widget (toplevel, "presets"));
1412 
1413     GtkTreePath *path;
1414     GtkTreeViewColumn *col;
1415     gtk_tree_view_get_cursor (treeview, &path, &col);
1416     if (!path || !col) {
1417         return;
1418     }
1419     int *indices = gtk_tree_path_get_indices (path);
1420     int idx = *indices;
1421     g_free (indices);
1422 
1423     ddb_dsp_preset_t *p = converter_plugin->dsp_preset_get_for_idx (idx);
1424 
1425     current_ctx->current_dsp_preset = converter_plugin->dsp_preset_alloc ();
1426     if (!current_ctx->current_dsp_preset) {
1427         return;
1428     }
1429     converter_plugin->dsp_preset_copy (current_ctx->current_dsp_preset, p);
1430 
1431     if (GTK_RESPONSE_OK == edit_dsp_preset (_("New DSP Preset"), toplevel, NULL)) {
1432         converter_plugin->dsp_preset_append (current_ctx->current_dsp_preset);
1433         GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset"));
1434         refresh_dsp_lists (combo, treeview);
1435     }
1436     else {
1437         converter_plugin->dsp_preset_free (current_ctx->current_dsp_preset);
1438     }
1439 
1440     current_ctx->current_dsp_preset = NULL;
1441 }
1442 
1443 void
on_dsp_preset_remove(GtkButton * button,gpointer user_data)1444 on_dsp_preset_remove                     (GtkButton       *button,
1445                                         gpointer         user_data)
1446 {
1447     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1448     GtkWidget *list = lookup_widget (toplevel, "presets");
1449     GtkTreePath *path;
1450     GtkTreeViewColumn *col;
1451     gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
1452     if (!path || !col) {
1453         // nothing selected
1454         return;
1455     }
1456     int *indices = gtk_tree_path_get_indices (path);
1457     int idx = *indices;
1458     g_free (indices);
1459 
1460     ddb_dsp_preset_t *p = converter_plugin->dsp_preset_get_for_idx (idx);
1461     if (!p) {
1462         return;
1463     }
1464 
1465     GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Remove preset"));
1466     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel));
1467     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), _("This action will delete the selected preset. Are you sure?"));
1468     gtk_window_set_title (GTK_WINDOW (dlg), _("Warning"));
1469 
1470     int response = gtk_dialog_run (GTK_DIALOG (dlg));
1471     gtk_widget_destroy (dlg);
1472     if (response == GTK_RESPONSE_YES) {
1473         char path[1024];
1474         if (snprintf (path, sizeof (path), "%s/presets/dsp/%s.txt", deadbeef->get_config_dir (), p->title) > 0) {
1475             unlink (path);
1476         }
1477 
1478         converter_plugin->dsp_preset_remove (p);
1479         converter_plugin->dsp_preset_free (p);
1480 
1481         GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset"));
1482         refresh_dsp_lists (combo, GTK_TREE_VIEW (list));
1483     }
1484 }
1485 
1486 void
on_dsp_preset_edit(GtkButton * button,gpointer user_data)1487 on_dsp_preset_edit                     (GtkButton       *button,
1488                                         gpointer         user_data)
1489 {
1490     GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1491 
1492     GtkWidget *list = lookup_widget (toplevel, "presets");
1493     GtkTreePath *path;
1494     GtkTreeViewColumn *col;
1495     gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
1496     if (!path || !col) {
1497         // nothing selected
1498         return;
1499     }
1500     int *indices = gtk_tree_path_get_indices (path);
1501     int idx = *indices;
1502     g_free (indices);
1503     if (idx == -1) {
1504         return;
1505     }
1506 
1507     ddb_dsp_preset_t *p = converter_plugin->dsp_preset_get_for_idx (idx);
1508     if (!p) {
1509         return;
1510     }
1511 
1512     current_ctx->current_dsp_preset = converter_plugin->dsp_preset_alloc ();
1513     converter_plugin->dsp_preset_copy (current_ctx->current_dsp_preset, p);
1514 
1515     int r = edit_dsp_preset (_("Edit DSP Preset"), toplevel, p);
1516     if (r == GTK_RESPONSE_OK) {
1517         // replace preset
1518         converter_plugin->dsp_preset_replace (p, current_ctx->current_dsp_preset);
1519         converter_plugin->dsp_preset_free (p);
1520         GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset"));
1521         refresh_dsp_lists (combo, GTK_TREE_VIEW (list));
1522     }
1523     else {
1524         converter_plugin->dsp_preset_free (current_ctx->current_dsp_preset);
1525     }
1526 
1527     current_ctx->current_dsp_preset = NULL;
1528 }
1529 
1530 void
on_edit_dsp_presets_clicked(GtkButton * button,gpointer user_data)1531 on_edit_dsp_presets_clicked            (GtkButton       *button,
1532                                         gpointer         user_data)
1533 {
1534     GtkWidget *dlg = create_preset_list ();
1535     gtk_window_set_title (GTK_WINDOW (dlg), _("DSP Presets"));
1536     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (current_ctx->converter));
1537     g_signal_connect ((gpointer)lookup_widget (dlg, "add"), "clicked", G_CALLBACK (on_dsp_preset_add), NULL);
1538     g_signal_connect ((gpointer)lookup_widget (dlg, "remove"), "clicked", G_CALLBACK (on_dsp_preset_remove), NULL);
1539     g_signal_connect ((gpointer)lookup_widget (dlg, "edit"), "clicked", G_CALLBACK (on_dsp_preset_edit), NULL);
1540     g_signal_connect ((gpointer)lookup_widget (dlg, "copy"), "clicked", G_CALLBACK (on_dsp_preset_copy), NULL);
1541 
1542     GtkWidget *list = lookup_widget (dlg, "presets");
1543     GtkCellRenderer *title_cell = gtk_cell_renderer_text_new ();
1544     GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("Title"), title_cell, "text", 0, NULL);
1545     gtk_tree_view_append_column (GTK_TREE_VIEW (list), GTK_TREE_VIEW_COLUMN (col));
1546     GtkListStore *mdl = gtk_list_store_new (1, G_TYPE_STRING);
1547     gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (mdl));
1548     fill_presets (mdl, (ddb_preset_t *)converter_plugin->dsp_preset_get_list (), PRESET_TYPE_DSP);
1549     int curr = deadbeef->conf_get_int ("converter.dsp_preset", -1);
1550     if (curr >= 0) {
1551         GtkTreePath *path = gtk_tree_path_new_from_indices (curr, -1);
1552         gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE);
1553         gtk_tree_path_free (path);
1554     }
1555     gtk_dialog_run (GTK_DIALOG (dlg));
1556     gtk_widget_destroy (dlg);
1557 }
1558 
1559 
1560 void
on_converter_output_format_changed(GtkComboBox * combobox,gpointer user_data)1561 on_converter_output_format_changed     (GtkComboBox     *combobox,
1562                                         gpointer         user_data)
1563 {
1564     int idx = gtk_combo_box_get_active (combobox);
1565     deadbeef->conf_set_int ("converter.output_format", idx);
1566     deadbeef->conf_save ();
1567 }
1568 
1569 GtkWidget*
title_formatting_help_link_create(gchar * widget_name,gchar * string1,gchar * string2,gint int1,gint int2)1570 title_formatting_help_link_create (gchar *widget_name, gchar *string1, gchar *string2,
1571                 gint int1, gint int2)
1572 {
1573     GtkWidget *link = gtk_link_button_new_with_label ("http://github.com/Alexey-Yakovenko/deadbeef/wiki/Title-formatting-2.0", _("Help"));
1574     return link;
1575 }
1576 
1577 GtkWidget*
encoder_cmdline_help_link_create(gchar * widget_name,gchar * string1,gchar * string2,gint int1,gint int2)1578 encoder_cmdline_help_link_create (gchar *widget_name, gchar *string1, gchar *string2,
1579                 gint int1, gint int2)
1580 {
1581     GtkWidget *link = gtk_link_button_new_with_label ("http://github.com/Alexey-Yakovenko/deadbeef/wiki/Encoder-Command-Line", _("Help"));
1582     return link;
1583 }
1584 
1585 static DB_plugin_action_t convert_action = {
1586     .title = "Convert",
1587     .name = "convert",
1588     .flags = DB_ACTION_MULTIPLE_TRACKS | DB_ACTION_SINGLE_TRACK | DB_ACTION_ADD_MENU,
1589     .callback2 = converter_show,
1590     .next = NULL
1591 };
1592 
1593 static DB_plugin_action_t *
convgui_get_actions(DB_playItem_t * it)1594 convgui_get_actions (DB_playItem_t *it)
1595 {
1596     return &convert_action;
1597 }
1598 
1599 static int
convgui_connect(void)1600 convgui_connect (void) {
1601     gtkui_plugin = (ddb_gtkui_t *)deadbeef->plug_get_for_id (DDB_GTKUI_PLUGIN_ID);
1602     converter_plugin = (ddb_converter_t *)deadbeef->plug_get_for_id ("converter");
1603     if (!gtkui_plugin) {
1604         fprintf (stderr, "convgui: gtkui plugin not found\n");
1605         return -1;
1606     }
1607     if (!converter_plugin) {
1608         fprintf (stderr, "convgui: converter plugin not found\n");
1609         return -1;
1610     }
1611 #define REQ_CONV_VERSION 4
1612     if (!PLUG_TEST_COMPAT(&converter_plugin->misc.plugin, 1, REQ_CONV_VERSION)) {
1613         fprintf (stderr, "convgui: need converter>=1.%d, but found %d.%d\n", REQ_CONV_VERSION, converter_plugin->misc.plugin.version_major, converter_plugin->misc.plugin.version_minor);
1614         return -1;
1615     }
1616     return 0;
1617 }
1618 
1619 static void
import_legacy_tf(const char * key_from,const char * key_to)1620 import_legacy_tf (const char *key_from, const char *key_to) {
1621     deadbeef->conf_lock ();
1622     if (!deadbeef->conf_get_str_fast (key_to, NULL)
1623             && deadbeef->conf_get_str_fast (key_from, NULL)) {
1624         char old[200], new[200];
1625         deadbeef->conf_get_str (key_from, "", old, sizeof (old));
1626         deadbeef->tf_import_legacy (old, new, sizeof (new));
1627         deadbeef->conf_set_str (key_to, new);
1628     }
1629     deadbeef->conf_unlock ();
1630 }
1631 
1632 static int
convgui_start(void)1633 convgui_start (void) {
1634     import_legacy_tf ("converter.output_file", "converter.output_file_tf");
1635     return 0;
1636 }
1637 
1638 DB_misc_t plugin = {
1639     .plugin.api_vmajor = 1,
1640     .plugin.api_vminor = 5,
1641     .plugin.version_major = 1,
1642     .plugin.version_minor = 2,
1643     .plugin.type = DB_PLUGIN_MISC,
1644 #if GTK_CHECK_VERSION(3,0,0)
1645     .plugin.name = "Converter GTK3 UI",
1646 #else
1647     .plugin.name = "Converter GTK2 UI",
1648 #endif
1649     .plugin.descr = "GTK User interface for the Converter plugin\n"
1650         "Usage:\n"
1651         "· select some tracks in playlist\n"
1652         "· right click\n"
1653         "· select «Convert»\n",
1654     .plugin.copyright =
1655         "Converter UI for DeaDBeeF Player\n"
1656         "Copyright (C) 2009-2015 Alexey Yakovenko and other contributors\n"
1657         "\n"
1658         "This software is provided 'as-is', without any express or implied\n"
1659         "warranty.  In no event will the authors be held liable for any damages\n"
1660         "arising from the use of this software.\n"
1661         "\n"
1662         "Permission is granted to anyone to use this software for any purpose,\n"
1663         "including commercial applications, and to alter it and redistribute it\n"
1664         "freely, subject to the following restrictions:\n"
1665         "\n"
1666         "1. The origin of this software must not be misrepresented; you must not\n"
1667         " claim that you wrote the original software. If you use this software\n"
1668         " in a product, an acknowledgment in the product documentation would be\n"
1669         " appreciated but is not required.\n"
1670         "\n"
1671         "2. Altered source versions must be plainly marked as such, and must not be\n"
1672         " misrepresented as being the original software.\n"
1673         "\n"
1674         "3. This notice may not be removed or altered from any source distribution.\n"
1675     ,
1676     .plugin.website = "http://deadbeef.sf.net",
1677     .plugin.get_actions = convgui_get_actions,
1678     .plugin.connect = convgui_connect,
1679     .plugin.start = convgui_start,
1680 };
1681 
1682 DB_plugin_t *
1683 #if GTK_CHECK_VERSION(3,0,0)
converter_gtk3_load(DB_functions_t * api)1684 converter_gtk3_load (DB_functions_t *api) {
1685 #else
1686 converter_gtk2_load (DB_functions_t *api) {
1687 #endif
1688     deadbeef = api;
1689     return DB_PLUGIN (&plugin);
1690 }
1691 
1692