1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * presets.c
4 * Copyright (C) John Stebbins 2008-2021 <stebbins@stebbins>
5 *
6 * presets.c is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License version 2, as published by the Free Software
10 * Foundation.
11 *
12 * presets.c is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with main.c. If not, write to:
19 * The Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor
21 * Boston, MA 02110-1301, USA.
22 */
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <glib.h>
28 #include <glib-object.h>
29 #include <glib/gstdio.h>
30 #include <glib/gi18n.h>
31 #include <string.h>
32 #include "ghbcompat.h"
33 #include "handbrake/handbrake.h"
34 #include "settings.h"
35 #include "callbacks.h"
36 #include "audiohandler.h"
37 #include "subtitlehandler.h"
38 #include "hb-backend.h"
39 #include "resources.h"
40 #include "presets.h"
41 #include "values.h"
42 #include "handbrake/lang.h"
43 #include "videohandler.h"
44
45 #define MAX_NESTED_PRESET 3
46
47 static GhbValue *prefsDict = NULL;
48 static gboolean prefs_modified = FALSE;
49 static gchar *override_user_config_dir = NULL;
50
51 static void store_prefs(void);
52 static void store_presets(void);
53
54 hb_preset_index_t*
ghb_tree_get_index(GtkTreeModel * store,GtkTreeIter * iter)55 ghb_tree_get_index(GtkTreeModel *store, GtkTreeIter *iter)
56 {
57 GtkTreePath *treepath;
58 int *indices, len;
59 hb_preset_index_t *path;
60
61 treepath = gtk_tree_model_get_path(store, iter);
62 indices = gtk_tree_path_get_indices(treepath);
63 len = gtk_tree_path_get_depth(treepath);
64 path = hb_preset_index_init(indices, len);
65 gtk_tree_path_free(treepath);
66
67 return path;
68 }
69
70 hb_preset_index_t*
ghb_tree_path_get_index(GtkTreePath * treepath)71 ghb_tree_path_get_index(GtkTreePath *treepath)
72 {
73 int *indices, len;
74
75 indices = gtk_tree_path_get_indices(treepath);
76 len = gtk_tree_path_get_depth(treepath);
77
78 return hb_preset_index_init(indices, len);
79 }
80
81 // This only handle limited depth
82 GtkTreePath*
ghb_tree_path_new_from_index(const hb_preset_index_t * path)83 ghb_tree_path_new_from_index(const hb_preset_index_t *path)
84 {
85 if (path == NULL || path->depth == 0)
86 return NULL;
87
88 #if GTK_CHECK_VERSION(3, 12, 0)
89 return gtk_tree_path_new_from_indicesv((int*)path->index, path->depth);
90 #else
91 switch (path->depth)
92 {
93 case 1:
94 return gtk_tree_path_new_from_indices(
95 path->index[0], -1);
96 case 2:
97 return gtk_tree_path_new_from_indices(
98 path->index[0], path->index[1], -1);
99 case 3:
100 return gtk_tree_path_new_from_indices(
101 path->index[0], path->index[1], path->index[2], -1);
102 case 4:
103 return gtk_tree_path_new_from_indices(
104 path->index[0], path->index[1], path->index[2],
105 path->index[3], -1);
106 case 5:
107 return gtk_tree_path_new_from_indices(
108 path->index[0], path->index[1], path->index[2],
109 path->index[3], path->index[4], -1);
110 case 6:
111 return gtk_tree_path_new_from_indices(
112 path->index[0], path->index[1], path->index[2],
113 path->index[3], path->index[4], path->index[5], -1);
114 case 7:
115 return gtk_tree_path_new_from_indices(
116 path->index[0], path->index[1], path->index[2],
117 path->index[3], path->index[4], path->index[5],
118 path->index[6], -1);
119 case 8:
120 return gtk_tree_path_new_from_indices(
121 path->index[0], path->index[1], path->index[2],
122 path->index[3], path->index[4], path->index[5],
123 path->index[6], path->index[7], -1);
124 default:
125 g_warning("Preset path depth too deep");
126 return NULL;
127 }
128 #endif
129 }
130
131 #if 0
132 void
133 dump_preset_indices(const gchar *msg, hb_preset_index_t *path)
134 {
135 gint ii;
136
137 g_message("%s indices: len %d", msg, path->depth);
138 for (ii = 0; ii < path->depth; ii++)
139 {
140 printf("%d ", path->index[ii]);
141 }
142 printf("\n");
143 }
144 #endif
145
146 void
ghb_presets_list_show_default(signal_user_data_t * ud)147 ghb_presets_list_show_default(signal_user_data_t *ud)
148 {
149 hb_preset_index_t *path;
150
151 path = hb_presets_get_default_index();
152 if (path == NULL || path->depth == 0)
153 return;
154
155 GtkTreeView *treeview;
156 GtkTreeStore *store;
157 GtkTreePath *treepath;
158 GtkTreeIter iter;
159
160 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
161 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
162 treepath = ghb_tree_path_new_from_index(path);
163 if (treepath)
164 {
165 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath))
166 {
167 gtk_tree_store_set(store, &iter,
168 1, 800,
169 2, 2 ,
170 -1);
171 }
172 gtk_tree_path_free(treepath);
173 }
174 free(path);
175 }
176
177 void
ghb_presets_list_clear_default(signal_user_data_t * ud)178 ghb_presets_list_clear_default(signal_user_data_t *ud)
179 {
180 hb_preset_index_t *path;
181
182 path = hb_presets_get_default_index();
183 if (path == NULL || path->depth == 0)
184 return;
185
186 GtkTreeView *treeview;
187 GtkTreeStore *store;
188 GtkTreePath *treepath;
189 GtkTreeIter iter;
190
191 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
192 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
193 treepath = ghb_tree_path_new_from_index(path);
194 if (treepath)
195 {
196 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath))
197 {
198 gtk_tree_store_set(store, &iter,
199 1, 400,
200 2, 0 ,
201 -1);
202 }
203 gtk_tree_path_free(treepath);
204 }
205 free(path);
206 }
207
208 static gint
preset_get_type(hb_preset_index_t * path)209 preset_get_type(hb_preset_index_t *path)
210 {
211 GhbValue *dict;
212 dict = hb_preset_get(path);
213 if (dict)
214 {
215 return ghb_dict_get_int(dict, "Type");
216 }
217 else
218 {
219 g_warning("ghb_preset_get_type (): internal preset lookup error");
220 return 0;
221 }
222 return 0;
223 }
224
225 static gboolean
preset_is_folder(hb_preset_index_t * path)226 preset_is_folder(hb_preset_index_t *path)
227 {
228 GhbValue *dict;
229 gboolean folder = FALSE;
230
231 dict = hb_preset_get(path);
232 if (dict)
233 {
234 folder = ghb_dict_get_bool(dict, "Folder");
235 }
236 return folder;
237 }
238
239 void
ghb_preset_to_settings(GhbValue * settings,GhbValue * preset)240 ghb_preset_to_settings(GhbValue *settings, GhbValue *preset)
241 {
242 // Remove legacy x264Option
243 ghb_dict_remove(settings, "x264Option");
244
245 // Initialize defaults
246 ghb_settings_init(settings, "Initialization");
247
248 // Initialize the ui settings from a preset
249 ghb_dict_copy(settings, preset);
250
251 // Fix up all the internal settings that are derived from preset values.
252
253 ghb_dict_set(settings, "scale_height", ghb_value_dup(
254 ghb_dict_get_value(settings, "PictureHeight")));
255
256 ghb_dict_set(settings, "scale_width", ghb_value_dup(
257 ghb_dict_get_value(settings, "PictureWidth")));
258
259 if (ghb_dict_get_bool(settings, "PictureAutoCrop"))
260 {
261 ghb_dict_set_string(settings, "crop_mode", "auto");
262 }
263 else
264 {
265 int ct = ghb_dict_get_int(settings, "PictureTopCrop");
266 int cb = ghb_dict_get_int(settings, "PictureBottomCrop");
267 int cl = ghb_dict_get_int(settings, "PictureLeftCrop");
268 int cr = ghb_dict_get_int(settings, "PictureRightCrop");
269 if (ct == 0 && cb == 0 && cl == 0 && cr == 0)
270 {
271 ghb_dict_set_string(settings, "crop_mode", "none");
272 }
273 else
274 {
275 ghb_dict_set_string(settings, "crop_mode", "custom");
276 }
277 }
278
279 int width, height;
280 const gchar * resolution_limit;
281
282 width = ghb_dict_get_int(settings, "PictureWidth");
283 height = ghb_dict_get_int(settings, "PictureHeight");
284 resolution_limit = ghb_lookup_resolution_limit(width, height);
285 ghb_dict_set_string(settings, "resolution_limit", resolution_limit);
286
287 const char * rotate = ghb_dict_get_string(settings, "PictureRotate");
288 if (rotate != NULL)
289 {
290 int angle, hflip;
291 char * angle_opt;
292 hb_dict_t * filter_settings;
293
294 filter_settings = hb_generate_filter_settings(HB_FILTER_ROTATE,
295 NULL, NULL, rotate);
296 angle = hb_dict_get_int(filter_settings, "angle");
297 hflip = hb_dict_get_bool(filter_settings, "hflip");
298 angle_opt = g_strdup_printf("%d", angle);
299 ghb_dict_set_string(settings, "rotate", angle_opt);
300 ghb_dict_set_int(settings, "hflip", hflip);
301 g_free(angle_opt);
302 hb_value_free(&filter_settings);
303 }
304 gint vqtype;
305
306 vqtype = ghb_dict_get_int(settings, "VideoQualityType");
307
308 // VideoQualityType/0/1/2 - vquality_type_/target/bitrate/constant
309 // *note: target is no longer used
310 switch (vqtype)
311 {
312 case 0:
313 {
314 ghb_dict_set_bool(settings, "vquality_type_bitrate", TRUE);
315 ghb_dict_set_bool(settings, "vquality_type_constant", FALSE);
316 } break;
317 case 1:
318 {
319 ghb_dict_set_bool(settings, "vquality_type_bitrate", TRUE);
320 ghb_dict_set_bool(settings, "vquality_type_constant", FALSE);
321 } break;
322 case 2:
323 {
324 ghb_dict_set_bool(settings, "vquality_type_bitrate", FALSE);
325 ghb_dict_set_bool(settings, "vquality_type_constant", TRUE);
326 } break;
327 default:
328 {
329 ghb_dict_set_bool(settings, "vquality_type_bitrate", FALSE);
330 ghb_dict_set_bool(settings, "vquality_type_constant", TRUE);
331 } break;
332 }
333
334 const gchar *mode = ghb_dict_get_string(settings, "VideoFramerateMode");
335 if (mode != NULL && strcmp(mode, "cfr") == 0)
336 {
337 ghb_dict_set_bool(settings, "VideoFramerateCFR", TRUE);
338 ghb_dict_set_bool(settings, "VideoFrameratePFR", FALSE);
339 ghb_dict_set_bool(settings, "VideoFramerateVFR", FALSE);
340 }
341 else if (mode != NULL && strcmp(mode, "pfr") == 0)
342 {
343 ghb_dict_set_bool(settings, "VideoFramerateCFR", FALSE);
344 ghb_dict_set_bool(settings, "VideoFrameratePFR", TRUE);
345 ghb_dict_set_bool(settings, "VideoFramerateVFR", FALSE);
346 }
347 else
348 {
349 ghb_dict_set_bool(settings, "VideoFramerateCFR", FALSE);
350 ghb_dict_set_bool(settings, "VideoFrameratePFR", FALSE);
351 ghb_dict_set_bool(settings, "VideoFramerateVFR", TRUE);
352 }
353
354 int encoder;
355 const char *videoPreset;
356
357 encoder = ghb_get_video_encoder(settings);
358 videoPreset = ghb_dict_get_string(settings, "VideoPreset");
359 ghb_set_video_preset(settings, encoder, videoPreset);
360
361 char *videoTune;
362 char *tune = NULL;
363 char *saveptr;
364 char *tok;
365
366 videoTune = g_strdup(ghb_dict_get_string(settings, "VideoTune"));
367 if (videoTune != NULL)
368 {
369 tok = strtok_r(videoTune, ",./-+", &saveptr);
370 ghb_dict_set_bool(settings, "x264FastDecode", FALSE);
371 ghb_dict_set_bool(settings, "x264ZeroLatency", FALSE);
372 while (tok != NULL)
373 {
374 if (!strcasecmp(tok, "fastdecode"))
375 {
376 ghb_dict_set_bool(settings, "x264FastDecode", TRUE);
377 }
378 else if (!strcasecmp(tok, "zerolatency"))
379 {
380 ghb_dict_set_bool(settings, "x264ZeroLatency", TRUE);
381 }
382 else if (tune == NULL)
383 {
384 tune = g_strdup(tok);
385 }
386 else
387 {
388 ghb_log("Superfluous tunes! %s", tok);
389 }
390 tok = strtok_r(NULL, ",./-+", &saveptr);
391 }
392 g_free(videoTune);
393 }
394 if (tune != NULL)
395 {
396 ghb_dict_set_string(settings, "VideoTune", tune);
397 g_free(tune);
398 }
399
400 const char *videoProfile;
401 videoProfile = ghb_dict_get_string(settings, "VideoProfile");
402 if (videoProfile != NULL)
403 ghb_dict_set_string(settings, "VideoProfile", videoProfile);
404
405 const char *videoLevel;
406 videoLevel = ghb_dict_get_string(settings, "VideoLevel");
407 if (videoLevel != NULL)
408 ghb_dict_set_string(settings, "VideoLevel", videoLevel);
409
410 if (ghb_dict_get(settings, "x264OptionExtra") != NULL)
411 {
412 const char *optionExtra;
413 optionExtra = ghb_dict_get_string(settings, "x264OptionExtra");
414 ghb_dict_set_string(settings, "VideoOptionExtra", optionExtra);
415 }
416
417 // Extract copy mask to check box booleans
418 GhbValue *copy_mask;
419 copy_mask = ghb_dict_get(preset, "AudioCopyMask");
420 if (copy_mask != NULL)
421 {
422 int count = ghb_array_len(copy_mask);
423 int ii;
424 for (ii = 0; ii < count; ii++)
425 {
426 GhbValue *val = ghb_array_get(copy_mask, ii);
427 const char *s = ghb_value_get_string(val);
428 int acodec = hb_audio_encoder_get_from_name(s);
429 switch (acodec)
430 {
431 default:
432 break;
433 case HB_ACODEC_MP2_PASS:
434 ghb_dict_set_bool(settings, "AudioAllowMP2Pass", 1);
435 break;
436 case HB_ACODEC_LAME:
437 case HB_ACODEC_MP3_PASS:
438 ghb_dict_set_bool(settings, "AudioAllowMP3Pass", 1);
439 break;
440 case HB_ACODEC_CA_AAC:
441 case HB_ACODEC_FDK_AAC:
442 case HB_ACODEC_FFAAC:
443 case HB_ACODEC_AAC_PASS:
444 ghb_dict_set_bool(settings, "AudioAllowAACPass", 1);
445 break;
446 case HB_ACODEC_AC3:
447 case HB_ACODEC_AC3_PASS:
448 ghb_dict_set_bool(settings, "AudioAllowAC3Pass", 1);
449 break;
450 case HB_ACODEC_DCA:
451 case HB_ACODEC_DCA_PASS:
452 ghb_dict_set_bool(settings, "AudioAllowDTSPass", 1);
453 break;
454 case HB_ACODEC_DCA_HD:
455 case HB_ACODEC_DCA_HD_PASS:
456 ghb_dict_set_bool(settings, "AudioAllowDTSHDPass", 1);
457 break;
458 case HB_ACODEC_FFEAC3:
459 case HB_ACODEC_EAC3_PASS:
460 ghb_dict_set_bool(settings, "AudioAllowEAC3Pass", 1);
461 break;
462 case HB_ACODEC_FFFLAC:
463 case HB_ACODEC_FFFLAC24:
464 case HB_ACODEC_FLAC_PASS:
465 ghb_dict_set_bool(settings, "AudioAllowFLACPass", 1);
466 break;
467 case HB_ACODEC_FFTRUEHD:
468 case HB_ACODEC_TRUEHD_PASS:
469 ghb_dict_set_bool(settings, "AudioAllowTRUEHDPass", 1);
470 break;
471 }
472 }
473 }
474 }
475
476 // Initialization order of some widgets matter because the value of
477 // these widgets are used to establish limits on the values that
478 // other widgets are allowed to take.
479 //
480 // So make sure these get initialized first.
481 static const char *widget_priority_list[] =
482 {
483 "preview_count",
484 "PtoPType",
485 "VideoEncoder",
486 "VideoQualityGranularity",
487 "AudioEncoder",
488 "PictureDeinterlaceFilter",
489 "PictureDeinterlacePreset",
490 NULL
491 };
492
493 void
ghb_settings_to_ui(signal_user_data_t * ud,GhbValue * dict)494 ghb_settings_to_ui(signal_user_data_t *ud, GhbValue *dict)
495 {
496 GhbDictIter iter;
497 const gchar *key;
498 GhbValue *gval;
499 int ii;
500 GhbValue *tmp = ghb_value_dup(dict);
501
502 if (dict == NULL)
503 return;
504
505 for (ii = 0; widget_priority_list[ii] != NULL; ii++)
506 {
507 key = widget_priority_list[ii];
508 gval = ghb_dict_get_value(tmp, key);
509 if (gval != NULL)
510 ghb_ui_settings_update(ud, dict, key, gval);
511 }
512
513 iter = ghb_dict_iter_init(tmp);
514 // middle (void*) cast prevents gcc warning "dereferencing type-punned
515 // pointer will break strict-aliasing rules"
516 while (ghb_dict_iter_next(tmp, &iter, &key, &gval))
517 {
518 ghb_ui_settings_update(ud, dict, key, gval);
519 }
520 ghb_value_free(&tmp);
521 }
522
523 char*
preset_get_fullname(hb_preset_index_t * path,const char * sep,gboolean escape)524 preset_get_fullname(hb_preset_index_t *path, const char * sep, gboolean escape)
525 {
526 int ii;
527 GString *gstr;
528 hb_preset_index_t *tmp;
529 GhbValue *dict;
530
531 gstr = g_string_new("");
532 tmp = hb_preset_index_dup(path);
533 for (ii = 1; ii <= path->depth; ii++)
534 {
535 const char *name;
536 tmp->depth = ii;
537 dict = hb_preset_get(tmp);
538 if (dict == NULL)
539 {
540 break;
541 }
542 name = ghb_dict_get_string(dict, "PresetName");
543 if (name != NULL)
544 {
545 g_string_append(gstr, sep);
546 if (escape)
547 {
548 char * esc = g_markup_escape_text(name, -1);
549 g_string_append(gstr, esc);
550 g_free(esc);
551 }
552 else
553 {
554 g_string_append(gstr, name);
555 }
556 }
557 }
558 free(tmp);
559 char *str = g_string_free(gstr, FALSE);
560 return str;
561 }
562
563 static void
set_preset_menu_button_label(signal_user_data_t * ud,hb_preset_index_t * path)564 set_preset_menu_button_label(signal_user_data_t *ud, hb_preset_index_t *path)
565 {
566 char * fullname, * text;
567 const char * description;
568 GtkLabel * label;
569 GtkWidget * widget;
570 GhbValue * dict;
571 int type;
572
573 dict = hb_preset_get(path);
574 type = ghb_dict_get_int(dict, "Type");
575 fullname = preset_get_fullname(path, " <span alpha=\"70%\">></span> ", TRUE);
576 label = GTK_LABEL(GHB_WIDGET(ud->builder, "presets_menu_button_label"));
577 text = g_strdup_printf("%s%s", type == HB_PRESET_TYPE_CUSTOM ?
578 "Custom" : "Official", fullname);
579 gtk_label_set_markup(label, text);
580 free(fullname);
581 free(text);
582
583 description = ghb_dict_get_string(dict, "PresetDescription");
584 widget = GHB_WIDGET(ud->builder, "presets_menu_button");
585 gtk_widget_set_tooltip_text(widget, description);
586 }
587
588 void
ghb_preset_menu_button_refresh(signal_user_data_t * ud,const char * fullname,int type)589 ghb_preset_menu_button_refresh(signal_user_data_t *ud,
590 const char *fullname, int type)
591 {
592 hb_preset_index_t * path;
593
594 path = hb_preset_search_index(fullname, 0, type);
595 set_preset_menu_button_label(ud, path);
596 }
597
598 static void
select_preset2(signal_user_data_t * ud,hb_preset_index_t * path)599 select_preset2(signal_user_data_t *ud, hb_preset_index_t *path)
600 {
601 GtkTreeView *treeview;
602 GtkTreeSelection *selection;
603 GtkTreeModel *store;
604 GtkTreeIter iter;
605 GtkTreePath *treepath;
606
607 if (path == NULL || path->depth == 0)
608 return;
609
610 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
611 selection = gtk_tree_view_get_selection (treeview);
612 store = gtk_tree_view_get_model (treeview);
613 treepath = ghb_tree_path_new_from_index(path);
614 if (treepath != NULL)
615 {
616 gtk_tree_view_expand_to_path(treeview, treepath);
617 if (gtk_tree_model_get_iter(store, &iter, treepath))
618 {
619 gtk_tree_selection_select_iter(selection, &iter);
620 }
621 else
622 {
623 if (gtk_tree_model_get_iter_first(store, &iter))
624 gtk_tree_selection_select_iter(selection, &iter);
625 }
626 // Make the selection visible in scroll window if it is not.
627 gtk_tree_view_scroll_to_cell(treeview, treepath, NULL, FALSE, 0, 0);
628 gtk_tree_path_free(treepath);
629 }
630 set_preset_menu_button_label(ud, path);
631
632 int type = preset_get_type(path);
633 GSimpleAction * action;
634
635 action = G_SIMPLE_ACTION(g_action_map_lookup_action(
636 G_ACTION_MAP(ud->app), "preset-rename"));
637 g_simple_action_set_enabled(action, type == HB_PRESET_TYPE_CUSTOM);
638 action = G_SIMPLE_ACTION(g_action_map_lookup_action(
639 G_ACTION_MAP(ud->app), "preset-save"));
640 g_simple_action_set_enabled(action, type == HB_PRESET_TYPE_CUSTOM);
641 }
642
643 void
ghb_select_preset(signal_user_data_t * ud,const char * name,int type)644 ghb_select_preset(signal_user_data_t *ud, const char *name, int type)
645 {
646 hb_preset_index_t *path;
647
648 path = hb_preset_search_index(name, 1, type);
649 if (path != NULL)
650 {
651 select_preset2(ud, path);
652 free(path);
653 }
654 }
655
656 void
ghb_select_default_preset(signal_user_data_t * ud)657 ghb_select_default_preset(signal_user_data_t *ud)
658 {
659 hb_preset_index_t *path;
660
661 path = hb_presets_get_default_index();
662 if (path == NULL || path->depth == 0)
663 {
664 // No default set, find original "default" preset
665 g_free(path);
666 path = hb_preset_search_index("Fast 1080p30", 1, HB_PRESET_TYPE_ALL);
667 }
668 if (path == NULL || path->depth == 0)
669 {
670 int index[2] = {0, 0};
671
672 // Could not find original default, try first available preset
673 g_free(path);
674 path = hb_preset_index_init(index, 2);
675 }
676 if (path != NULL)
677 {
678 select_preset2(ud, path);
679 g_free(path);
680 }
681 }
682
683 gchar *
ghb_get_user_config_dir(gchar * subdir)684 ghb_get_user_config_dir(gchar *subdir)
685 {
686 const gchar * dir, * ghb = "ghb";
687 gchar * config;
688
689 if (override_user_config_dir != NULL)
690 {
691 dir = override_user_config_dir;
692 }
693 else
694 {
695 dir = g_get_user_config_dir();
696 }
697 if (dir == NULL || !g_file_test(dir, G_FILE_TEST_IS_DIR))
698 {
699 dir = g_get_home_dir();
700 ghb = ".ghb";
701 }
702 if (dir == NULL || !g_file_test(dir, G_FILE_TEST_IS_DIR))
703 {
704 // Last ditch, use CWD
705 dir = "./";
706 ghb = ".ghb";
707 }
708 config = g_strdup_printf("%s/%s", dir, ghb);
709 if (!g_file_test(config, G_FILE_TEST_IS_DIR))
710 g_mkdir (config, 0755);
711 if (subdir)
712 {
713 gchar **split;
714 gint ii;
715
716 split = g_strsplit(subdir, G_DIR_SEPARATOR_S, -1);
717 for (ii = 0; split[ii] != NULL; ii++)
718 {
719 gchar *tmp;
720
721 tmp = g_strdup_printf ("%s/%s", config, split[ii]);
722 g_free(config);
723 config = tmp;
724 if (!g_file_test(config, G_FILE_TEST_IS_DIR))
725 g_mkdir (config, 0755);
726 }
727 g_strfreev(split);
728 }
729 return config;
730 }
731
732 void
ghb_override_user_config_dir(char * dir)733 ghb_override_user_config_dir(char *dir)
734 {
735 override_user_config_dir = dir;
736 }
737
738 static void
write_config_file(const gchar * name,GhbValue * dict)739 write_config_file(const gchar *name, GhbValue *dict)
740 {
741 gchar *config, *path;
742
743 config = ghb_get_user_config_dir(NULL);
744 path = g_strdup_printf ("%s/%s", config, name);
745 g_free(config);
746 ghb_json_write_file(path, dict);
747 g_free(path);
748 }
749
750 void
ghb_write_settings_file(const gchar * path,GhbValue * dict)751 ghb_write_settings_file(const gchar *path, GhbValue *dict)
752 {
753 ghb_json_write_file(path, dict);
754 }
755
756 static int
presets_add_config_file(const gchar * name)757 presets_add_config_file(const gchar *name)
758 {
759 gchar *config, *path;
760 hb_value_t *preset;
761
762 config = ghb_get_user_config_dir(NULL);
763 path = g_strdup_printf ("%s/%s", config, name);
764 g_free(config);
765 if (!g_file_test(path, G_FILE_TEST_IS_REGULAR))
766 return -1;
767 preset = hb_presets_read_file(path);
768 g_free(path);
769 if (preset != NULL)
770 {
771 int hb_major, hb_minor, hb_micro;
772 int major, minor, micro;
773 hb_presets_version(preset, &major, &minor, µ);
774 hb_presets_current_version(&hb_major, &hb_minor, &hb_micro);
775 if (major != hb_major)
776 {
777 // Make a backup whenever the preset version changes
778 config = ghb_get_user_config_dir(NULL);
779 path = g_strdup_printf ("%s/presets.%d.%d.%d.json",
780 config, major, minor, micro);
781 hb_value_write_json(preset, path);
782 g_free(config);
783 g_free(path);
784 }
785
786 if (major > hb_major)
787 {
788 // Change in major indicates non-backward compatible preset changes.
789 // We can't successfully load presets that were generated by
790 // a newer version of handbrake than is currently running.
791 hb_value_free(&preset);
792 return -2;
793 }
794
795 hb_value_t *imported;
796 hb_presets_import(preset, &imported);
797 hb_presets_add(imported);
798 if (major != hb_major || minor != hb_minor || micro != hb_micro)
799 {
800 // Reload hb builtin presets
801 hb_presets_builtin_update();
802 store_presets();
803 }
804 hb_value_free(&imported);
805 hb_value_free(&preset);
806 return 0;
807 }
808 return -1;
809 }
810
811 static GhbValue*
read_config_file(const gchar * name)812 read_config_file(const gchar *name)
813 {
814 gchar *config, *path;
815 GhbValue *gval = NULL;
816
817 config = ghb_get_user_config_dir(NULL);
818 path = g_strdup_printf ("%s/%s", config, name);
819 g_free(config);
820 if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
821 {
822 gval = ghb_json_parse_file(path);
823 if (gval == NULL)
824 gval = hb_plist_parse_file(path);
825 }
826 g_free(path);
827 return gval;
828 }
829
830 GhbValue*
ghb_read_settings_file(const gchar * path)831 ghb_read_settings_file(const gchar *path)
832 {
833 GhbValue *gval = NULL;
834
835 if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
836 {
837 gval = ghb_json_parse_file(path);
838 if (gval == NULL)
839 gval = hb_plist_parse_file(path);
840 }
841 return gval;
842 }
843
844 #if 0
845 // Currently unused, but keeping around just in case...
846 gboolean
847 ghb_lock_file(const gchar *name)
848 {
849 #if !defined(_WIN32)
850 gchar *config, *path;
851 int fd, lock = 0;
852
853 config = ghb_get_user_config_dir(NULL);
854 path = g_strdup_printf ("%s/%s", config, name);
855 fd = open(path, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
856 if (fd >= 0)
857 lock = lockf(fd, F_TLOCK, 0);
858 if (lock)
859 close(fd);
860 g_free(config);
861 g_free(path);
862 return !lock;
863 #else
864 return 1;
865 #endif
866 }
867 #endif
868
869 void
ghb_write_pid_file()870 ghb_write_pid_file()
871 {
872 #if !defined(_WIN32)
873 gchar *config, *path;
874 pid_t pid;
875 FILE *fp;
876 int fd;
877
878 pid = getpid();
879 config = ghb_get_user_config_dir(NULL);
880 path = g_strdup_printf ("%s/ghb.pid.%d", config, pid);
881 fp = g_fopen(path, "w");
882
883 if (fp != NULL)
884 {
885 fprintf(fp, "%d\n", pid);
886 fclose(fp);
887 }
888
889 fd = open(path, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
890 if (fd >= 0)
891 {
892 lockf(fd, F_TLOCK, 0);
893 }
894
895 g_free(config);
896 g_free(path);
897 #endif
898 }
899
900 int
ghb_find_pid_file()901 ghb_find_pid_file()
902 {
903 const gchar *file;
904 gchar *config;
905
906 config = ghb_get_user_config_dir(NULL);
907
908 if (g_file_test(config, G_FILE_TEST_IS_DIR))
909 {
910 GDir *gdir;
911 gdir = g_dir_open(config, 0, NULL);
912 file = g_dir_read_name(gdir);
913 while (file)
914 {
915 if (strncmp(file, "ghb.pid.", 8) == 0)
916 {
917 gchar *path;
918 int pid;
919
920 sscanf(file, "ghb.pid.%d", &pid);
921 path = g_strdup_printf("%s/%s", config, file);
922
923 #if !defined(_WIN32)
924 int fd, lock = 1;
925
926 fd = open(path, O_RDWR);
927 if (fd >= 0)
928 {
929 lock = lockf(fd, F_TLOCK, 0);
930 }
931 if (lock == 0)
932 {
933 close(fd);
934 g_dir_close(gdir);
935 g_unlink(path);
936 g_free(path);
937 g_free(config);
938 return pid;
939 }
940 g_free(path);
941 close(fd);
942 #else
943 g_dir_close(gdir);
944 g_unlink(path);
945 g_free(path);
946 g_free(config);
947 return pid;
948 #endif
949 }
950 file = g_dir_read_name(gdir);
951 }
952 g_dir_close(gdir);
953 }
954 g_free(config);
955 return -1;
956 }
957
958 static void
remove_config_file(const gchar * name)959 remove_config_file(const gchar *name)
960 {
961 gchar *config, *path;
962
963 config = ghb_get_user_config_dir(NULL);
964 path = g_strdup_printf ("%s/%s", config, name);
965 if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
966 {
967 g_unlink(path);
968 }
969 g_free(path);
970 g_free(config);
971 }
972
973 void
ghb_pref_save(GhbValue * settings,const gchar * key)974 ghb_pref_save(GhbValue *settings, const gchar *key)
975 {
976 const GhbValue *value, *value2;
977
978 value = ghb_dict_get_value(settings, key);
979 if (value != NULL)
980 {
981 GhbValue *dict;
982 dict = ghb_dict_get(prefsDict, "Preferences");
983 if (dict == NULL) return;
984 value2 = ghb_dict_get(dict, key);
985 if (ghb_value_cmp(value, value2) != 0)
986 {
987 ghb_dict_set(dict, key, ghb_value_dup(value));
988 store_prefs();
989 prefs_modified = FALSE;
990 }
991 }
992 }
993
994 void
ghb_pref_set(GhbValue * settings,const gchar * key)995 ghb_pref_set(GhbValue *settings, const gchar *key)
996 {
997 const GhbValue *value, *value2;
998
999 value = ghb_dict_get_value(settings, key);
1000 if (value != NULL)
1001 {
1002 GhbValue *dict;
1003 dict = ghb_dict_get(prefsDict, "Preferences");
1004 if (dict == NULL) return;
1005 value2 = ghb_dict_get(dict, key);
1006 if (ghb_value_cmp(value, value2) != 0)
1007 {
1008 ghb_dict_set(dict, key, ghb_value_dup(value));
1009 prefs_modified = TRUE;
1010 }
1011 }
1012 }
1013
1014 void
ghb_prefs_store(void)1015 ghb_prefs_store(void)
1016 {
1017 if (prefs_modified)
1018 {
1019 store_prefs();
1020 prefs_modified = FALSE;
1021 }
1022 }
1023
1024 void
ghb_settings_init(GhbValue * settings,const char * name)1025 ghb_settings_init(GhbValue *settings, const char *name)
1026 {
1027 GhbValue *internal;
1028
1029 GhbValue *internalDict = ghb_resource_get("internal-defaults");
1030 // Setting a ui widget will cause the corresponding setting
1031 // to be set, but it also triggers a callback that can
1032 // have the side effect of using other settings values
1033 // that have not yet been set. So set *all* settings first
1034 // then update the ui.
1035 internal = ghb_dict_get(internalDict, name);
1036 ghb_dict_copy(settings, internal);
1037 }
1038
1039 void
ghb_settings_close()1040 ghb_settings_close()
1041 {
1042 if (prefsDict)
1043 ghb_value_free(&prefsDict);
1044 }
1045
1046 #if defined(_WIN32)
1047 gchar*
FindFirstCDROM(void)1048 FindFirstCDROM(void)
1049 {
1050 gint ii, drives;
1051 gchar drive[5];
1052
1053 strcpy(drive, "A:" G_DIR_SEPARATOR_S);
1054 drives = GetLogicalDrives();
1055 for (ii = 0; ii < 26; ii++)
1056 {
1057 if (drives & 0x01)
1058 {
1059 guint dtype;
1060
1061 drive[0] = 'A' + ii;
1062 dtype = GetDriveType(drive);
1063 if (dtype == DRIVE_CDROM)
1064 {
1065 return g_strdup(drive);
1066 }
1067 }
1068 drives >>= 1;
1069 }
1070 return NULL;
1071 }
1072 #endif
1073
1074 void
ghb_prefs_load(signal_user_data_t * ud)1075 ghb_prefs_load(signal_user_data_t *ud)
1076 {
1077 GhbValue *dict, *internal;
1078 GhbValue *internalDict;
1079
1080 internalDict = ghb_resource_get("internal-defaults");
1081 prefsDict = read_config_file("preferences.json");
1082 if (prefsDict == NULL)
1083 prefsDict = read_config_file("preferences");
1084 if (prefsDict == NULL)
1085 prefsDict = ghb_dict_new();
1086 dict = ghb_dict_get(prefsDict, "Preferences");
1087 internal = ghb_dict_get(internalDict, "Preferences");
1088 if (dict == NULL && internal != NULL)
1089 {
1090 dict = ghb_value_dup(internal);
1091 ghb_dict_set(prefsDict, "Preferences", dict);
1092
1093 const gchar *dir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1094 if (dir == NULL)
1095 {
1096 dir = ".";
1097 }
1098 ghb_dict_set_string(dict, "ExportDirectory", dir);
1099
1100 dir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS);
1101 if (dir == NULL)
1102 {
1103 dir = ".";
1104 }
1105 ghb_dict_set_string(dict, "destination_dir", dir);
1106
1107 ghb_dict_set_string(dict, "SrtDir", dir);
1108 #if defined(_WIN32)
1109 gchar *source;
1110
1111 source = FindFirstCDROM();
1112 if (source == NULL)
1113 {
1114 source = g_strdup("C:" G_DIR_SEPARATOR_S);
1115 }
1116 ghb_dict_set_string(dict, "default_source", source);
1117 g_free(source);
1118 #endif
1119 store_prefs();
1120 }
1121 ghb_dict_remove(dict, "show_presets");
1122 }
1123
1124 void
ghb_prefs_to_settings(GhbValue * settings)1125 ghb_prefs_to_settings(GhbValue *settings)
1126 {
1127 // Initialize the ui from presets file.
1128 GhbValue *dict;
1129
1130 if (prefsDict == NULL)
1131 return;
1132
1133 // Setting a ui widget will cause the corresponding setting
1134 // to be set, but it also triggers a callback that can
1135 // have the side effect of using other settings values
1136 // that have not yet been set. So set *all* settings first
1137 // then update the ui.
1138 dict = ghb_dict_get(prefsDict, "Preferences");
1139 ghb_dict_copy(settings, dict);
1140 }
1141
1142 static const gchar*
get_preset_color(gint type,gboolean is_folder)1143 get_preset_color(gint type, gboolean is_folder)
1144 {
1145 const gchar *color;
1146
1147 if (type == HB_PRESET_TYPE_CUSTOM)
1148 {
1149 color = "DimGray";
1150 if (is_folder)
1151 {
1152 color = "black";
1153 }
1154 }
1155 else
1156 {
1157 color = "blue";
1158 if (is_folder)
1159 {
1160 color = "Navy";
1161 }
1162 }
1163 return color;
1164 }
1165
1166 hb_preset_index_t *
get_selected_path(signal_user_data_t * ud)1167 get_selected_path(signal_user_data_t *ud)
1168 {
1169 GtkTreeView *treeview;
1170 GtkTreeSelection *selection;
1171 GtkTreeModel *store;
1172 GtkTreeIter iter;
1173
1174 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1175 selection = gtk_tree_view_get_selection(treeview);
1176 if (gtk_tree_selection_get_selected(selection, &store, &iter))
1177 {
1178 return ghb_tree_get_index(store, &iter);
1179 }
1180 return NULL;
1181 }
1182
1183 G_MODULE_EXPORT gboolean
presets_window_delete_cb(GtkWidget * xwidget,GdkEvent * event,signal_user_data_t * ud)1184 presets_window_delete_cb(
1185 GtkWidget *xwidget,
1186 #if !GTK_CHECK_VERSION(3, 90, 0)
1187 GdkEvent *event,
1188 #endif
1189 signal_user_data_t *ud)
1190 {
1191 GSimpleAction * action;
1192 GVariant * state = g_variant_new_boolean(FALSE);
1193
1194 action = G_SIMPLE_ACTION(g_action_map_lookup_action(
1195 G_ACTION_MAP(ud->app), "show-presets"));
1196 g_action_change_state(G_ACTION(action), state);
1197 return TRUE;
1198 }
1199
1200 G_MODULE_EXPORT void
presets_sz_alloc_cb(GtkWidget * widget,int width,int height,int baseline,signal_user_data_t * ud)1201 presets_sz_alloc_cb(
1202 GtkWidget *widget,
1203 #if GTK_CHECK_VERSION(3, 90, 0)
1204 int width,
1205 int height,
1206 int baseline,
1207 #else
1208 GdkRectangle *rect,
1209 #endif
1210 signal_user_data_t *ud)
1211 {
1212 if (gtk_widget_get_visible(widget))
1213 {
1214 gint w, h, ww, wh;
1215 w = ghb_dict_get_int(ud->prefs, "presets_window_width");
1216 h = ghb_dict_get_int(ud->prefs, "presets_window_height");
1217
1218 gtk_window_get_size(GTK_WINDOW(widget), &ww, &wh);
1219 if ( w != ww || h != wh )
1220 {
1221 ghb_dict_set_int(ud->prefs, "presets_window_width", ww);
1222 ghb_dict_set_int(ud->prefs, "presets_window_height", wh);
1223 ghb_pref_set(ud->prefs, "presets_window_width");
1224 ghb_pref_set(ud->prefs, "presets_window_height");
1225 ghb_prefs_store();
1226 }
1227 }
1228 }
1229
1230 G_MODULE_EXPORT void
preset_select_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)1231 preset_select_action_cb(GSimpleAction *action, GVariant *param,
1232 signal_user_data_t *ud)
1233 {
1234 const char * preset_path = g_variant_get_string(param, NULL);
1235 int type = preset_path[0] - '0';
1236
1237 ghb_select_preset(ud, &preset_path[1], type);
1238 }
1239
1240 G_MODULE_EXPORT void
preset_reload_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)1241 preset_reload_action_cb(GSimpleAction *action, GVariant *param,
1242 signal_user_data_t *ud)
1243 {
1244 const char * preset_path;
1245 int type;
1246
1247 type = ghb_dict_get_int(ud->settings, "Type");
1248 preset_path = ghb_dict_get_string(ud->settings, "PresetFullName");
1249 if (preset_path != NULL)
1250 {
1251 ghb_select_preset(ud, preset_path, type);
1252 }
1253 }
1254
1255 void
ghb_presets_menu_init(signal_user_data_t * ud)1256 ghb_presets_menu_init(signal_user_data_t *ud)
1257 {
1258 GMenu * menu = g_menu_new();
1259 hb_preset_index_t * path;
1260 GhbValue * presets;
1261 int menu_count, submenu_count, type, ii, jj, kk;
1262 char ** official_names;
1263
1264 // Add official presets
1265 path = hb_preset_index_init(NULL, 0);
1266 presets = hb_presets_get_folder_children(path);
1267 if (presets == NULL)
1268 {
1269 g_warning(_("ghb_presets_menu_init: Failed to find presets folder."));
1270 g_free(path);
1271 return;
1272 }
1273
1274 menu_count = ghb_array_len(presets);
1275 // Menus can't contain the same name twice. Since our preset list
1276 // allows official and custom preset categories with the same name
1277 // I must modify one of them when duplicates exist :(
1278 official_names = calloc(menu_count + 1, sizeof(char*));
1279 kk = 0;
1280 path->depth++;
1281 // Process Official Presets in first pass, then Custom Presets
1282 for (type = 0; type < 2; type++)
1283 {
1284 GMenu * section = g_menu_new();
1285 for (ii = 0; ii < menu_count; ii++)
1286 {
1287 GhbValue * dict;
1288 const gchar * folder_name;
1289 gint folder_type;
1290 gboolean is_folder;
1291 GhbValue * folder;
1292 GString * folder_str;
1293 char * menu_item_name;
1294
1295 path->index[path->depth-1] = ii;
1296
1297 dict = ghb_array_get(presets, ii);
1298 folder_name = ghb_dict_get_string(dict, "PresetName");
1299 folder_type = ghb_dict_get_int(dict, "Type");
1300 is_folder = ghb_dict_get_bool(dict, "Folder");
1301
1302 if (folder_type != type)
1303 {
1304 continue;
1305 }
1306
1307 if (type == HB_PRESET_TYPE_OFFICIAL)
1308 {
1309 // Add folder name to list of official names
1310 official_names[kk++] = g_strdup(folder_name);
1311 }
1312
1313 folder_str = g_string_new("");
1314 g_string_append_printf(folder_str, "%d/%s",
1315 folder_type, folder_name);
1316 if (is_folder)
1317 {
1318 GMenu * submenu = g_menu_new();
1319
1320 folder = hb_presets_get_folder_children(path);
1321 submenu_count = ghb_array_len(folder);
1322 for (jj = 0; jj < submenu_count; jj++)
1323 {
1324 const gchar * name;
1325 GString * preset_str = g_string_new(folder_str->str);
1326
1327 dict = ghb_array_get(folder, jj);
1328 name = ghb_dict_get_string(dict, "PresetName");
1329 type = ghb_dict_get_int(dict, "Type");
1330 is_folder = ghb_dict_get_bool(dict, "Folder");
1331
1332 // Sanity check, Preset types must match their folder
1333 if (type != folder_type)
1334 {
1335 continue;
1336 }
1337 // Enforce 2 level limit
1338 if (is_folder)
1339 {
1340 continue;
1341 }
1342 g_string_append_printf(preset_str, "/%s", name);
1343
1344 char * preset_path;
1345 char * detail_action;
1346
1347 preset_path = g_string_free(preset_str, FALSE);
1348 detail_action = g_strdup_printf("app.preset-select(\"%s\")",
1349 preset_path);
1350 g_menu_append(submenu, name, detail_action);
1351 free(preset_path);
1352 free(detail_action);
1353 }
1354 if (type == HB_PRESET_TYPE_CUSTOM &&
1355 ghb_strv_contains((const char**)official_names, folder_name))
1356 {
1357 menu_item_name = g_strdup_printf("My %s", folder_name);
1358 }
1359 else
1360 {
1361 menu_item_name = g_strdup(folder_name);
1362 }
1363 g_menu_append_submenu(section, menu_item_name,
1364 G_MENU_MODEL(submenu));
1365 g_free(menu_item_name);
1366 }
1367 g_string_free(folder_str, TRUE);
1368 }
1369 g_menu_append_section(menu,
1370 type == HB_PRESET_TYPE_CUSTOM ?
1371 "Custom" : "Official",
1372 G_MENU_MODEL(section));
1373 }
1374 g_free(path);
1375 g_strfreev(official_names);
1376
1377 GtkMenuButton * mb;
1378
1379 mb = GTK_MENU_BUTTON(GHB_WIDGET(ud->builder, "presets_menu_button"));
1380 gtk_menu_button_set_menu_model(mb, G_MENU_MODEL(menu));
1381 }
1382
1383
1384 void
ghb_presets_menu_clear(signal_user_data_t * ud)1385 ghb_presets_menu_clear(signal_user_data_t *ud)
1386 {
1387 GtkMenuButton * mb;
1388 GMenuModel * mm;
1389
1390 mb = GTK_MENU_BUTTON(GHB_WIDGET(ud->builder, "presets_menu_button"));
1391 mm = gtk_menu_button_get_menu_model(mb);
1392 gtk_menu_button_set_menu_model(mb, NULL);
1393 g_object_unref(G_OBJECT(mm));
1394 }
1395
1396 void
ghb_presets_menu_reinit(signal_user_data_t * ud)1397 ghb_presets_menu_reinit(signal_user_data_t *ud)
1398 {
1399 ghb_presets_menu_clear(ud);
1400 ghb_presets_menu_init(ud);
1401 }
1402
1403 void
ghb_presets_list_init(signal_user_data_t * ud,const hb_preset_index_t * path)1404 ghb_presets_list_init(signal_user_data_t *ud, const hb_preset_index_t *path)
1405 {
1406 hb_preset_index_t *next_path;
1407 GhbValue *folder;
1408 GtkTreeView *treeview;
1409 GtkTreeStore *store;
1410 GtkTreePath *parent_path;
1411 GtkTreeIter iter, parent_iter, *piter;
1412 gint count, ii;
1413
1414 if (path == NULL)
1415 {
1416 hb_preset_index_t *p = hb_preset_index_init(NULL, 0);
1417 ghb_presets_list_init(ud, p);
1418 free(p);
1419 return;
1420 }
1421 next_path = hb_preset_index_dup(path);
1422 folder = hb_presets_get_folder_children(path);
1423 if (folder == NULL)
1424 {
1425 g_warning(_("Failed to find parent folder when adding child."));
1426 g_free(next_path);
1427 return;
1428 }
1429 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1430 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1431 parent_path = ghb_tree_path_new_from_index(path);
1432 if (parent_path != NULL)
1433 {
1434 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &parent_iter,
1435 parent_path);
1436 piter = &parent_iter;
1437 gtk_tree_path_free(parent_path);
1438 }
1439 else
1440 {
1441 piter = NULL;
1442 }
1443 count = ghb_array_len(folder);
1444 next_path->depth++;
1445 for (ii = 0; ii < count; ii++)
1446 {
1447 GhbValue *dict;
1448 const gchar *name;
1449 const gchar *color;
1450 gint type;
1451 const gchar *description;
1452 gboolean is_folder;
1453 gboolean def;
1454
1455 next_path->index[next_path->depth-1] = ii;
1456
1457 // Additional settings, add row
1458 dict = ghb_array_get(folder, ii);
1459 name = ghb_dict_get_string(dict, "PresetName");
1460 description = ghb_dict_get_string(dict, "PresetDescription");
1461 type = ghb_dict_get_int(dict, "Type");
1462 is_folder = ghb_dict_get_bool(dict, "Folder");
1463 def = ghb_dict_get_bool(dict, "Default");
1464 color = get_preset_color(type, is_folder);
1465
1466 gtk_tree_store_append(store, &iter, piter);
1467 gtk_tree_store_set(store, &iter,
1468 0, name,
1469 1, def ? 800 : 400,
1470 2, def ? 2 : 0,
1471 3, color,
1472 4, description,
1473 5, type == HB_PRESET_TYPE_OFFICIAL ? 0 : 1,
1474 -1);
1475 if (is_folder)
1476 {
1477 ghb_presets_list_init(ud, next_path);
1478 if (ghb_dict_get_bool(dict, "FolderOpen"))
1479 {
1480 GtkTreePath *path;
1481 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1482 gtk_tree_view_expand_to_path(treeview, path);
1483 gtk_tree_path_free(path);
1484 }
1485 }
1486 }
1487 g_free(next_path);
1488 if (path == NULL)
1489 {
1490 ghb_presets_list_show_default(ud);
1491 }
1492 }
1493
1494 static void
presets_list_clear(signal_user_data_t * ud)1495 presets_list_clear(signal_user_data_t *ud)
1496 {
1497 GtkTreeView *treeview;
1498 GtkTreeModel *store;
1499 GtkTreeIter iter;
1500 gboolean valid;
1501
1502 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1503 store = gtk_tree_view_get_model(treeview);
1504 valid = gtk_tree_model_get_iter_first(store, &iter);
1505 while (valid)
1506 {
1507 gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1508 valid = gtk_tree_model_get_iter_first(store, &iter);
1509 }
1510 }
1511
1512 void
ghb_presets_list_reinit(signal_user_data_t * ud)1513 ghb_presets_list_reinit(signal_user_data_t *ud)
1514 {
1515 presets_list_clear(ud);
1516 ghb_presets_list_init(ud, NULL);
1517 }
1518
1519 static void
presets_list_update_item(signal_user_data_t * ud,const hb_preset_index_t * path,gboolean recurse)1520 presets_list_update_item(
1521 signal_user_data_t *ud,
1522 const hb_preset_index_t *path,
1523 gboolean recurse)
1524 {
1525 GhbValue *dict;
1526 GtkTreeView *treeview;
1527 GtkTreeStore *store;
1528 GtkTreePath *treepath;
1529 GtkTreeIter iter;
1530 const gchar *name;
1531 const gchar *description;
1532 gint type;
1533 gboolean is_folder;
1534 gboolean def;
1535 const gchar *color;
1536
1537 dict = hb_preset_get(path);
1538 if (dict == NULL)
1539 return;
1540
1541 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1542 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1543 treepath = ghb_tree_path_new_from_index(path);
1544 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
1545
1546 // Additional settings, add row
1547 name = ghb_dict_get_string(dict, "PresetName");
1548 description = ghb_dict_get_string(dict, "PresetDescription");
1549 type = ghb_dict_get_int(dict, "Type");
1550 is_folder = ghb_dict_get_bool(dict, "Folder");
1551 def = ghb_dict_get_bool(dict, "Default");
1552 color = get_preset_color(type, is_folder);
1553
1554 gtk_tree_store_set(store, &iter,
1555 0, name,
1556 1, def ? 800 : 400,
1557 2, def ? 2 : 0,
1558 3, color,
1559 4, description,
1560 5, type == HB_PRESET_TYPE_OFFICIAL ? 0 : 1,
1561 -1);
1562 if (recurse && is_folder)
1563 {
1564 ghb_presets_list_init(ud, path);
1565 }
1566 }
1567
1568 static void
presets_list_append(signal_user_data_t * ud,const hb_preset_index_t * path)1569 presets_list_append(signal_user_data_t *ud, const hb_preset_index_t *path)
1570 {
1571 hb_preset_index_t *folder_path;
1572 hb_value_t *dict;
1573 GtkTreeView *treeview;
1574 GtkTreeStore *store;
1575 GtkTreePath *folder_treepath;
1576 GtkTreeIter iter, parent_iter, *piter;
1577 const gchar *name;
1578 const gchar *description;
1579 gint type;
1580 gboolean is_folder;
1581 gboolean def;
1582 const gchar *color;
1583
1584 folder_path = hb_preset_index_dup(path);
1585 folder_path->depth--;
1586
1587 dict = hb_preset_get(path);
1588 if (dict == NULL)
1589 {
1590 g_message("Ack! Desync between presets and preset list");
1591 return;
1592 }
1593
1594 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1595 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1596
1597 folder_treepath = ghb_tree_path_new_from_index(folder_path);
1598 if (folder_treepath != NULL)
1599 {
1600 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &parent_iter,
1601 folder_treepath);
1602 piter = &parent_iter;
1603 gtk_tree_path_free(folder_treepath);
1604 }
1605 else
1606 {
1607 piter = NULL;
1608 }
1609
1610 // Additional settings, add row
1611 name = ghb_dict_get_string(dict, "PresetName");
1612 description = ghb_dict_get_string(dict, "PresetDescription");
1613 type = ghb_dict_get_int(dict, "Type");
1614 is_folder = ghb_dict_get_bool(dict, "Folder");
1615 def = ghb_dict_get_bool(dict, "Default");
1616 color = get_preset_color(type, is_folder);
1617
1618 gtk_tree_store_append(store, &iter, piter);
1619 gtk_tree_store_set(store, &iter,
1620 0, name,
1621 1, def ? 800 : 400,
1622 2, def ? 2 : 0,
1623 3, color,
1624 4, description,
1625 5, type == HB_PRESET_TYPE_OFFICIAL ? 0 : 1,
1626 -1);
1627 if (is_folder)
1628 {
1629 ghb_presets_list_init(ud, path);
1630 }
1631 }
1632
1633 static void
presets_list_remove(signal_user_data_t * ud,hb_preset_index_t * path)1634 presets_list_remove(signal_user_data_t *ud, hb_preset_index_t *path)
1635 {
1636 GtkTreeView *treeview;
1637 GtkTreePath *treepath;
1638 GtkTreeIter iter;
1639 GtkTreeStore *store;
1640
1641 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1642 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1643 treepath = ghb_tree_path_new_from_index(path);
1644 if (treepath)
1645 {
1646 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath))
1647 gtk_tree_store_remove(store, &iter);
1648 gtk_tree_path_free(treepath);
1649 }
1650 }
1651
1652 void
ghb_save_queue(GhbValue * queue)1653 ghb_save_queue(GhbValue *queue)
1654 {
1655 pid_t pid;
1656 char *name;
1657
1658 pid = getpid();
1659 name = g_strdup_printf ("queue.%d", pid);
1660 write_config_file(name, queue);
1661 g_free(name);
1662 }
1663
1664 GhbValue*
ghb_load_old_queue(int pid)1665 ghb_load_old_queue(int pid)
1666 {
1667 GhbValue *queue;
1668 char *name;
1669
1670 name = g_strdup_printf ("queue.%d", pid);
1671 queue = read_config_file(name);
1672 g_free(name);
1673 return queue;
1674 }
1675
1676 void
ghb_remove_old_queue_file(int pid)1677 ghb_remove_old_queue_file(int pid)
1678 {
1679 char *name;
1680
1681 name = g_strdup_printf ("queue.%d", pid);
1682 remove_config_file(name);
1683 g_free(name);
1684 }
1685
ghb_create_copy_mask(GhbValue * settings)1686 GhbValue* ghb_create_copy_mask(GhbValue *settings)
1687 {
1688 GhbValue *copy_mask = ghb_array_new();
1689 if (ghb_dict_get_bool(settings, "AudioAllowMP2Pass"))
1690 {
1691 ghb_array_append(copy_mask, ghb_string_value_new("copy:mp2"));
1692 }
1693 if (ghb_dict_get_bool(settings, "AudioAllowMP3Pass"))
1694 {
1695 ghb_array_append(copy_mask, ghb_string_value_new("copy:mp3"));
1696 }
1697 if (ghb_dict_get_bool(settings, "AudioAllowAACPass"))
1698 {
1699 ghb_array_append(copy_mask, ghb_string_value_new("copy:aac"));
1700 }
1701 if (ghb_dict_get_bool(settings, "AudioAllowAC3Pass"))
1702 {
1703 ghb_array_append(copy_mask, ghb_string_value_new("copy:ac3"));
1704 }
1705 if (ghb_dict_get_bool(settings, "AudioAllowDTSPass"))
1706 {
1707 ghb_array_append(copy_mask, ghb_string_value_new("copy:dts"));
1708 }
1709 if (ghb_dict_get_bool(settings, "AudioAllowDTSHDPass"))
1710 {
1711 ghb_array_append(copy_mask, ghb_string_value_new("copy:dtshd"));
1712 }
1713 if (ghb_dict_get_bool(settings, "AudioAllowEAC3Pass"))
1714 {
1715 ghb_array_append(copy_mask, ghb_string_value_new("copy:eac3"));
1716 }
1717 if (ghb_dict_get_bool(settings, "AudioAllowFLACPass"))
1718 {
1719 ghb_array_append(copy_mask, ghb_string_value_new("copy:flac"));
1720 }
1721 if (ghb_dict_get_bool(settings, "AudioAllowTRUEHDPass"))
1722 {
1723 ghb_array_append(copy_mask, ghb_string_value_new("copy:truehd"));
1724 }
1725 return copy_mask;
1726 }
1727
1728 // Translate internal values to preset key, value pairs
1729 GhbValue*
ghb_settings_to_preset(GhbValue * settings)1730 ghb_settings_to_preset(GhbValue *settings)
1731 {
1732 GhbValue *preset = ghb_value_dup(settings);
1733
1734 gboolean br, constant;
1735
1736 ghb_dict_remove(preset, "title");
1737 ghb_dict_set_bool(preset, "Default", 0);
1738
1739 br = ghb_dict_get_bool(preset, "vquality_type_bitrate");
1740 constant = ghb_dict_get_bool(preset, "vquality_type_constant");
1741
1742 const char * angle = ghb_dict_get_string(preset, "rotate");
1743 int hflip = ghb_dict_get_int(preset, "hflip");
1744 char * rot_flip = g_strdup_printf("angle=%s:hflip=%d", angle, hflip);
1745 ghb_dict_set_string(preset, "PictureRotate", rot_flip);
1746 g_free(rot_flip);
1747
1748 const gchar * crop_mode;
1749
1750 crop_mode = ghb_dict_get_string(settings, "crop_mode");
1751 ghb_dict_set_bool(preset, "PictureAutoCrop", !strcmp(crop_mode, "auto"));
1752
1753 // VideoQualityType/0/1/2 - vquality_type_/target/bitrate/constant
1754 // *note: target is no longer used
1755 if (br)
1756 {
1757 ghb_dict_set_int(preset, "VideoQualityType", 1);
1758 }
1759 else if (constant)
1760 {
1761 ghb_dict_set_int(preset, "VideoQualityType", 2);
1762 }
1763
1764 if (ghb_dict_get_bool(preset, "VideoFramerateCFR"))
1765 {
1766 ghb_dict_set_string(preset, "VideoFramerateMode", "cfr");
1767 }
1768 else if (ghb_dict_get_bool(preset, "VideoFrameratePFR"))
1769 {
1770 ghb_dict_set_string(preset, "VideoFramerateMode", "pfr");
1771 }
1772 else
1773 {
1774 ghb_dict_set_string(preset, "VideoFramerateMode", "vfr");
1775 }
1776
1777 GhbValue *alist, *adict;
1778 gint count, ii;
1779
1780 alist = ghb_dict_get(preset, "AudioList");
1781 count = ghb_array_len(alist);
1782 for (ii = 0; ii < count; ii++)
1783 {
1784 gdouble drc;
1785
1786 adict = ghb_array_get(alist, ii);
1787 drc = ghb_dict_get_double(adict, "AudioTrackDRCSlider");
1788 if (drc < 1.0)
1789 {
1790 ghb_dict_set_double(adict, "AudioTrackDRCSlider", 0.0);
1791 }
1792 }
1793
1794 GhbValue *copy_mask = ghb_create_copy_mask(preset);
1795 ghb_dict_set(preset, "AudioCopyMask", copy_mask);
1796
1797 GString *str = g_string_new("");
1798 const char *sep = "";
1799 const char *tune = ghb_dict_get_string(preset, "VideoTune");
1800 if (tune != NULL && strcasecmp(tune, "none"))
1801 {
1802 g_string_append_printf(str, "%s", tune);
1803 sep = ",";
1804 }
1805 int encoder = ghb_get_video_encoder(settings);
1806 if (encoder & HB_VCODEC_X264_MASK)
1807 {
1808 if (ghb_dict_get_bool(preset, "x264FastDecode"))
1809 {
1810 g_string_append_printf(str, "%s%s", sep, "fastdecode");
1811 sep = ",";
1812 }
1813 if (ghb_dict_get_bool(preset, "x264ZeroLatency"))
1814 {
1815 g_string_append_printf(str, "%s%s", sep, "zerolatency");
1816 }
1817 }
1818 char *tunes;
1819 tunes = g_string_free(str, FALSE);
1820 ghb_dict_set_string(preset, "VideoTune", tunes);
1821 g_free(tunes);
1822
1823 GhbValue *in_val, *out_val;
1824
1825 // Convert PictureModulus to correct data type
1826 in_val = ghb_dict_get(preset, "PictureModulus");
1827 out_val = ghb_value_xform(in_val, GHB_INT);
1828 if (out_val != NULL)
1829 ghb_dict_set(preset, "PictureModulus", out_val);
1830
1831 return preset;
1832 }
1833
1834 static guint prefs_timeout_id = 0;
1835
1836 static gboolean
delayed_store_prefs(gpointer data)1837 delayed_store_prefs(gpointer data)
1838 {
1839 write_config_file("preferences.json", prefsDict);
1840 prefs_timeout_id = 0;
1841 return FALSE;
1842 }
1843
1844 static void
store_presets()1845 store_presets()
1846 {
1847 gchar *config, *path;
1848 hb_value_t *presets;
1849
1850 config = ghb_get_user_config_dir(NULL);
1851 path = g_strdup_printf ("%s/%s", config, "presets.json");
1852 presets = hb_presets_get();
1853 hb_presets_write_json(presets, path);
1854 g_free(config);
1855 g_free(path);
1856 }
1857
1858 static void
store_prefs(void)1859 store_prefs(void)
1860 {
1861 if (prefs_timeout_id != 0)
1862 {
1863 GMainContext *mc;
1864 GSource *source;
1865
1866 mc = g_main_context_default();
1867 source = g_main_context_find_source_by_id(mc, prefs_timeout_id);
1868 if (source != NULL)
1869 g_source_destroy(source);
1870 }
1871 prefs_timeout_id = g_timeout_add_seconds(1, (GSourceFunc)delayed_store_prefs, NULL);
1872 }
1873
1874 void
ghb_presets_load(signal_user_data_t * ud)1875 ghb_presets_load(signal_user_data_t *ud)
1876 {
1877 int result = presets_add_config_file("presets.json");
1878 if (result == -2)
1879 {
1880 // The above can fail if the presets file was written by a
1881 // more recent version of HandBrake than is currently running.
1882 // Look for a backup version that matches the currently running
1883 // version.
1884 GtkWindow *hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
1885 gchar *message = g_strdup_printf(
1886 _("Presets found are newer than what is supported by this version of HandBrake!\n\n"
1887 "Would you like to continue?"));
1888 if (!ghb_message_dialog(hb_window, GTK_MESSAGE_WARNING, message,
1889 _("Get me out of here!"), _("Load backup presets")))
1890 {
1891 g_free(message);
1892 exit(1);
1893 }
1894 g_free(message);
1895
1896 gchar *name;
1897 int major, minor, micro;
1898
1899 hb_presets_current_version(&major, &minor, µ);
1900 name = g_strdup_printf("presets.%d.%d.%d.json", major, minor, micro);
1901 ghb_log("Failed to read presets file, trying backup (%s)...", name);
1902 if (presets_add_config_file(name) < 0)
1903 {
1904 ghb_log("Failed to read backup presets, using defaults...");
1905 hb_presets_builtin_update();
1906 // Don't store defaults unless the user explicitly saves
1907 // a new preset. This would overwrite the presets file
1908 // that was generated by a newer version of HandBrake.
1909 }
1910 g_free(name);
1911 }
1912 else if (result < 0)
1913 {
1914 if (presets_add_config_file("presets") < 0)
1915 {
1916 ghb_log("Failed to read presets file, initializing new presets...");
1917 hb_presets_builtin_update();
1918 store_presets();
1919 }
1920 }
1921 ghb_update_ui_combo_box(ud, "PresetCategory", NULL, FALSE);
1922 }
1923
1924 static void
settings_save(signal_user_data_t * ud,const char * category,const char * name,const char * desc,gboolean set_def)1925 settings_save(signal_user_data_t *ud, const char * category,
1926 const char *name, const char * desc, gboolean set_def)
1927 {
1928 GhbValue * preset, * new_preset;
1929 gboolean def = FALSE;
1930 hb_preset_index_t * folder_path, * path;
1931 char * fullname;
1932
1933 folder_path = hb_preset_search_index(category, 0, HB_PRESET_TYPE_CUSTOM);
1934 if (folder_path->depth <= 0)
1935 {
1936 GhbValue * new_folder;
1937 new_folder = ghb_dict_new();
1938 ghb_dict_set_string(new_folder, "PresetName", category);
1939 ghb_dict_set(new_folder, "ChildrenArray", ghb_array_new());
1940 ghb_dict_set_int(new_folder, "Type", HB_PRESET_TYPE_CUSTOM);
1941 ghb_dict_set_bool(new_folder, "Folder", TRUE);
1942 int index = hb_preset_append(folder_path, new_folder);
1943 if (index >= 0)
1944 {
1945 folder_path->index[folder_path->depth++] = index;
1946 presets_list_append(ud, folder_path);
1947 }
1948 else
1949 {
1950 ghb_log("Failed to create category (%s)...", category);
1951 return;
1952 }
1953 ghb_value_free(&new_folder);
1954 }
1955
1956 new_preset = ghb_settings_to_preset(ud->settings);
1957 ghb_dict_set_int(new_preset, "Type", HB_PRESET_TYPE_CUSTOM);
1958 ghb_dict_set_string(new_preset, "PresetName", name);
1959 ghb_dict_set_string(new_preset, "PresetDescription", desc);
1960
1961 fullname = g_strdup_printf("/%s/%s", category, name);
1962 path = hb_preset_search_index(fullname, 0, HB_PRESET_TYPE_CUSTOM);
1963 preset = hb_preset_get(path);
1964 if (preset != NULL)
1965 {
1966 // Replacing an existing preset
1967 def = ghb_dict_get_bool(preset, "Default");
1968
1969 // If we are replacing the default preset, re-mark it as default
1970 ghb_dict_set_bool(new_preset, "Default", def);
1971 // Already exists, update its description
1972 if (hb_preset_set(path, new_preset) >= 0)
1973 {
1974 presets_list_update_item(ud, path, FALSE);
1975 }
1976 }
1977 else
1978 {
1979 // Check if the new preset is also the new default preset
1980 if (set_def)
1981 {
1982 ghb_dict_set_bool(new_preset, "Default", set_def);
1983 ghb_presets_list_clear_default(ud);
1984 hb_presets_clear_default();
1985 }
1986
1987 // Adding a new preset
1988 // Append to the folder
1989 int index = hb_preset_append(folder_path, new_preset);
1990 if (index >= 0)
1991 {
1992 folder_path->index[folder_path->depth++] = index;
1993 presets_list_append(ud, folder_path);
1994 }
1995 *path = *folder_path;
1996 }
1997
1998
1999 free(fullname);
2000 ghb_value_free(&new_preset);
2001
2002 store_presets();
2003 ghb_presets_menu_reinit(ud);
2004
2005 ud->dont_clear_presets = TRUE;
2006 // Make the new preset the selected item
2007 select_preset2(ud, path);
2008 ud->dont_clear_presets = FALSE;
2009
2010 free(folder_path);
2011 free(path);
2012
2013 return;
2014 }
2015
2016 G_MODULE_EXPORT void
preset_import_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2017 preset_import_action_cb(GSimpleAction *action, GVariant *param,
2018 signal_user_data_t *ud)
2019 {
2020 GtkWindow *hb_window;
2021 GtkWidget *dialog;
2022 GtkResponseType response;
2023 const gchar *exportDir;
2024 gchar *filename;
2025 GtkFileFilter *filter;
2026
2027 hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
2028 dialog = gtk_file_chooser_dialog_new("Import Preset", hb_window,
2029 GTK_FILE_CHOOSER_ACTION_OPEN,
2030 GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2031 GHB_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2032 NULL);
2033
2034 filter = gtk_file_filter_new();
2035 gtk_file_filter_set_name(filter, _("All (*)"));
2036 gtk_file_filter_add_pattern(filter, "*");
2037 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
2038
2039 filter = gtk_file_filter_new();
2040 gtk_file_filter_set_name(filter, _("Presets (*.json)"));
2041 gtk_file_filter_add_pattern(filter, "*.json");
2042 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
2043 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2044
2045 filter = gtk_file_filter_new();
2046 gtk_file_filter_set_name(filter, _("Legacy Presets (*.plist)"));
2047 gtk_file_filter_add_pattern(filter, "*.plist");
2048 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
2049
2050 exportDir = ghb_dict_get_string(ud->prefs, "ExportDirectory");
2051 if (exportDir == NULL || exportDir[0] == '\0')
2052 {
2053 exportDir = ".";
2054 }
2055 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), exportDir);
2056
2057 response = gtk_dialog_run(GTK_DIALOG(dialog));
2058 gtk_widget_hide(dialog);
2059 if (response == GTK_RESPONSE_ACCEPT)
2060 {
2061 gchar *dir;
2062 int index;
2063
2064 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2065 if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR))
2066 {
2067 gtk_widget_destroy(dialog);
2068 g_free(filename);
2069 return;
2070 }
2071 // import the preset
2072 index = hb_presets_add_path(filename);
2073
2074 exportDir = ghb_dict_get_string(ud->prefs, "ExportDirectory");
2075 dir = g_path_get_dirname(filename);
2076 if (strcmp(dir, exportDir) != 0)
2077 {
2078 ghb_dict_set_string(ud->prefs, "ExportDirectory", dir);
2079 ghb_pref_save(ud->prefs, "ExportDirectory");
2080 }
2081 g_free(filename);
2082 g_free(dir);
2083 store_presets();
2084
2085 // Re-init the UI preset list
2086 ghb_presets_list_reinit(ud);
2087 ghb_presets_menu_reinit(ud);
2088 if (index < 0)
2089 {
2090 ghb_select_default_preset(ud);
2091 }
2092 else
2093 {
2094 hb_preset_index_t path;
2095 path.index[0] = index;
2096 path.depth = 1;
2097 select_preset2(ud, &path);
2098 }
2099 }
2100 gtk_widget_destroy(dialog);
2101 }
2102
2103 G_MODULE_EXPORT void
preset_export_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2104 preset_export_action_cb(GSimpleAction *action, GVariant *param,
2105 signal_user_data_t *ud)
2106 {
2107 hb_preset_index_t *path;
2108 GtkWindow *hb_window;
2109 GtkWidget *dialog;
2110 GtkResponseType response;
2111 const gchar *exportDir;
2112 gchar *filename;
2113 GhbValue *dict;
2114 char *preset_name;
2115
2116 path = get_selected_path(ud);
2117 if (path == NULL || path->depth <= 0)
2118 {
2119 const gchar *name;
2120 char * new_name;
2121
2122 free(path);
2123 dict = ghb_settings_to_preset(ud->settings);
2124 name = ghb_dict_get_string(dict, "PresetName");
2125 new_name = g_strdup_printf("%s (modified)", name);
2126 ghb_dict_set_string(dict, "PresetName", new_name);
2127 free(new_name);
2128 }
2129 else
2130 {
2131 dict = hb_value_dup(hb_preset_get(path));
2132 free(path);
2133 }
2134
2135 if (dict == NULL)
2136 {
2137 return;
2138 }
2139 preset_name = g_strdup(ghb_dict_get_string(dict, "PresetName"));
2140
2141 hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
2142 dialog = gtk_file_chooser_dialog_new(_("Export Preset"), hb_window,
2143 GTK_FILE_CHOOSER_ACTION_SAVE,
2144 GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2145 GHB_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2146 NULL);
2147
2148 exportDir = ghb_dict_get_string(ud->prefs, "ExportDirectory");
2149 if (exportDir == NULL || exportDir[0] == '\0')
2150 {
2151 exportDir = ".";
2152 }
2153
2154 // Clean up preset name for use as a filename. Removing leading
2155 // and trailing whitespace and filename illegal characters.
2156 g_strstrip(preset_name);
2157 g_strdelimit(preset_name, GHB_UNSAFE_FILENAME_CHARS, '_');
2158 filename = g_strdup_printf("%s.json", preset_name);
2159 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), exportDir);
2160 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
2161 g_free(filename);
2162 g_free(preset_name);
2163
2164 response = gtk_dialog_run(GTK_DIALOG(dialog));
2165 gtk_widget_hide(dialog);
2166 if (response == GTK_RESPONSE_ACCEPT)
2167 {
2168 gchar *dir;
2169
2170 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2171
2172 // export the preset
2173 hb_presets_write_json(dict, filename);
2174 exportDir = ghb_dict_get_string(ud->prefs, "ExportDirectory");
2175 dir = g_path_get_dirname(filename);
2176 if (strcmp(dir, exportDir) != 0)
2177 {
2178 ghb_dict_set_string(ud->prefs, "ExportDirectory", dir);
2179 ghb_pref_save(ud->prefs, "ExportDirectory");
2180 }
2181 g_free(dir);
2182 g_free(filename);
2183 }
2184 gtk_widget_destroy(dialog);
2185 hb_value_free(&dict);
2186 }
2187
2188 G_MODULE_EXPORT void
preset_rename_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2189 preset_rename_action_cb(GSimpleAction *action, GVariant *param,
2190 signal_user_data_t *ud)
2191 {
2192 const gchar * name;
2193 const gchar * fullname;
2194 int type;
2195 hb_preset_index_t * path;
2196 GtkWidget * dialog;
2197 GtkEntry * entry;
2198 GtkTextView * tv;
2199 GhbValue * dict;
2200 GtkResponseType response;
2201
2202 name = ghb_dict_get_string(ud->settings, "PresetName");
2203 type = ghb_dict_get_int(ud->settings, "Type");
2204 fullname = ghb_dict_get_string(ud->settings, "PresetFullName");
2205
2206 if (type != HB_PRESET_TYPE_CUSTOM)
2207 {
2208 // Only allow renaming custom presets
2209 return;
2210 }
2211 path = hb_preset_search_index(fullname, 0, type);
2212
2213 ghb_ui_update(ud, "PresetReDescription",
2214 ghb_dict_get_value(ud->settings, "PresetDescription"));
2215 tv = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "PresetReDescription"));
2216
2217 dialog = GHB_WIDGET(ud->builder, "preset_rename_dialog");
2218 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetReName"));
2219 ghb_editable_set_text(entry, name);
2220
2221 response = gtk_dialog_run(GTK_DIALOG(dialog));
2222 gtk_widget_hide(dialog);
2223 if (response == GTK_RESPONSE_OK)
2224 {
2225 GtkTextBuffer * buffer;
2226 GtkTextIter start, end;
2227 char * desc;
2228
2229 // save the new name
2230 name = ghb_editable_get_text(entry);
2231 dict = hb_preset_get(path);
2232 if (dict != NULL)
2233 {
2234 ghb_dict_set_string(dict, "PresetName", name);
2235 store_presets();
2236 }
2237
2238 buffer = gtk_text_view_get_buffer(tv);
2239 gtk_text_buffer_get_bounds(buffer, &start, &end);
2240 desc = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
2241 ghb_dict_set_string(ud->settings, "PresetDescription", desc);
2242 free(desc);
2243
2244 char * full = preset_get_fullname(path, "/", FALSE);
2245 ghb_dict_set_string(ud->settings, "PresetFullName", full);
2246 ghb_dict_set_string(ud->settings, "PresetName", name);
2247 free(full);
2248 ghb_presets_menu_reinit(ud);
2249 set_preset_menu_button_label(ud, path);
2250 }
2251 }
2252
preset_save_action(signal_user_data_t * ud,gboolean as)2253 static void preset_save_action(signal_user_data_t *ud, gboolean as)
2254 {
2255 const char * category = NULL;
2256 const gchar * name;
2257 const gchar * fullname;
2258 int type;
2259 hb_preset_index_t * path;
2260 GtkWidget * dialog;
2261 GtkWidget * widget;
2262 GtkEntry * entry;
2263 GtkTextView * tv;
2264 GhbValue * dict;
2265 GtkResponseType response;
2266
2267 name = ghb_dict_get_string(ud->settings, "PresetName");
2268 type = ghb_dict_get_int(ud->settings, "Type");
2269 fullname = ghb_dict_get_string(ud->settings, "PresetFullName");
2270
2271 ghb_ui_update(ud, "PresetSetDefault", ghb_boolean_value(FALSE));
2272
2273 path = hb_preset_search_index(fullname, 0, type);
2274
2275 // Find an appropriate default category
2276 if (path != NULL)
2277 {
2278 path->depth = 1;
2279 dict = hb_preset_get(path);
2280 if (ghb_dict_get_bool(dict, "Folder"))
2281 {
2282 category = ghb_dict_get_string(dict, "PresetName");
2283 }
2284 free(path);
2285 }
2286 if (category == NULL)
2287 {
2288 // Find first custom folder
2289 hb_value_t * presets;
2290 int ii, count;
2291
2292 presets = hb_presets_get();
2293 count = hb_value_array_len(presets);
2294 for (ii = 0; ii < count; ii++)
2295 {
2296 dict = hb_value_array_get(presets, ii);
2297 if (!ghb_dict_get_bool(dict, "Folder"))
2298 {
2299 continue;
2300 }
2301 if (ghb_dict_get_int(dict, "Type") == HB_PRESET_TYPE_CUSTOM)
2302 {
2303 category = ghb_dict_get_string(dict, "PresetName");
2304 break;
2305 }
2306 }
2307 }
2308 if (category == NULL)
2309 {
2310 // Force creation of new category
2311 category = "new";
2312 }
2313 ghb_ui_update(ud, "PresetCategory", ghb_string_value(category));
2314 tv = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "PresetDescription"));
2315
2316 dialog = GHB_WIDGET(ud->builder, "preset_save_dialog");
2317 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetName"));
2318 ghb_editable_set_text(entry, name);
2319
2320 widget = GHB_WIDGET(ud->builder, "PresetName");
2321 gtk_widget_set_sensitive(widget, as);
2322 widget = GHB_WIDGET(ud->builder, "PresetCategory");
2323 gtk_widget_set_sensitive(widget, as);
2324 widget = GHB_WIDGET(ud->builder, "PresetSetDefault");
2325 gtk_widget_set_visible(widget, as);
2326
2327 response = gtk_dialog_run(GTK_DIALOG(dialog));
2328 gtk_widget_hide(dialog);
2329 if (response == GTK_RESPONSE_OK)
2330 {
2331 GtkTextBuffer * buffer;
2332 GtkTextIter start, end;
2333 char * desc;
2334 gboolean def;
2335
2336 // save the preset
2337 name = ghb_editable_get_text(entry);
2338 category = ghb_dict_get_string(ud->settings, "PresetCategory");
2339 if (!strcmp(category, "new"))
2340 {
2341 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetCategoryName"));
2342 category = ghb_editable_get_text(entry);
2343 }
2344 if (category == NULL || category[0] == 0)
2345 {
2346 ghb_log("Invalid empty category.");
2347 return;
2348 }
2349 buffer = gtk_text_view_get_buffer(tv);
2350 gtk_text_buffer_get_bounds(buffer, &start, &end);
2351 desc = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
2352 def = ghb_dict_get_bool(ud->settings, "PresetSetDefault");
2353 settings_save(ud, category, name, desc, def);
2354 free(desc);
2355 }
2356 }
2357
2358 G_MODULE_EXPORT void
preset_save_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2359 preset_save_action_cb(GSimpleAction *action, GVariant *param,
2360 signal_user_data_t *ud)
2361 {
2362 preset_save_action(ud, FALSE);
2363 }
2364
2365 G_MODULE_EXPORT void
preset_save_as_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2366 preset_save_as_action_cb(GSimpleAction *action, GVariant *param,
2367 signal_user_data_t *ud)
2368 {
2369 preset_save_action(ud, TRUE);
2370 }
2371
2372 static void
preset_save_set_ok_sensitive(signal_user_data_t * ud)2373 preset_save_set_ok_sensitive(signal_user_data_t *ud)
2374 {
2375 GtkEntry * entry;
2376 GtkWidget * ok_button;
2377 const char * name;
2378 const char * category;
2379 const char * category_name;
2380 gboolean sensitive;
2381
2382 ok_button = GHB_WIDGET(ud->builder, "preset_ok");
2383
2384 category = ghb_dict_get_string(ud->settings, "PresetCategory");
2385 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetName"));
2386 name = ghb_editable_get_text(entry);
2387 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetCategoryName"));
2388 category_name = ghb_editable_get_text(entry);
2389
2390 sensitive = name[0] && (strcmp(category, "new") || category_name[0]);
2391 gtk_widget_set_sensitive(ok_button, sensitive);
2392 }
2393
2394 G_MODULE_EXPORT void
preset_category_changed_cb(GtkWidget * widget,signal_user_data_t * ud)2395 preset_category_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2396 {
2397 ghb_widget_to_setting(ud->settings, widget);
2398 preset_save_set_ok_sensitive(ud);
2399 ghb_check_dependency(ud, widget, NULL);
2400 }
2401
2402 G_MODULE_EXPORT void
preset_name_changed_cb(GtkWidget * widget,signal_user_data_t * ud)2403 preset_name_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2404 {
2405 preset_save_set_ok_sensitive(ud);
2406 }
2407
2408 G_MODULE_EXPORT void
presets_reload_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2409 presets_reload_action_cb(GSimpleAction *action, GVariant *param,
2410 signal_user_data_t *ud)
2411 {
2412 // Reload the builtin presets
2413 hb_presets_builtin_update();
2414 store_presets();
2415
2416 ghb_presets_list_reinit(ud);
2417 ghb_presets_menu_reinit(ud);
2418 ghb_select_default_preset(ud);
2419 }
2420
2421 G_MODULE_EXPORT void
preset_remove_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2422 preset_remove_action_cb(GSimpleAction *action, GVariant *param,
2423 signal_user_data_t *ud)
2424 {
2425 hb_preset_index_t * path;
2426 GhbValue * preset;
2427
2428 path = get_selected_path(ud);
2429 if (path != NULL)
2430 {
2431 preset = hb_preset_get(path);
2432 }
2433 if (path == NULL || preset == NULL)
2434 {
2435 const char * fullname;
2436 int type;
2437
2438 fullname = ghb_dict_get_string(ud->settings, "PresetFullName");
2439 if (fullname == NULL)
2440 {
2441 return;
2442 }
2443 type = ghb_dict_get_int(ud->settings, "Type");
2444 path = hb_preset_search_index(fullname, 0, type);
2445 if (path != NULL)
2446 {
2447 preset = hb_preset_get(path);
2448 }
2449 }
2450 if (path == NULL || preset == NULL)
2451 {
2452 return;
2453 }
2454
2455 GtkWindow * hb_window;
2456 GtkWidget * dialog;
2457 gboolean is_folder;
2458 GtkResponseType response;
2459 const char * name;
2460
2461 name = ghb_dict_get_string(preset, "PresetName");
2462 is_folder = preset_is_folder(path);
2463 hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
2464 dialog = gtk_message_dialog_new(hb_window, GTK_DIALOG_MODAL,
2465 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
2466 _("Confirm deletion of %s:\n\n%s"),
2467 is_folder ? _("folder") : _("preset"),
2468 name);
2469 response = gtk_dialog_run(GTK_DIALOG(dialog));
2470 gtk_widget_destroy(dialog);
2471 if (response == GTK_RESPONSE_YES)
2472 {
2473 int depth = path->depth;
2474
2475 // Determine which preset to highlight after deletion done
2476 hb_preset_index_t new_path = *path;
2477 // Always select a preset, not a folder
2478 if (depth == 1)
2479 {
2480 new_path.depth = 2;
2481 }
2482 // Try next
2483 new_path.index[depth - 1] = path->index[depth - 1] + 1;
2484 preset = hb_preset_get(&new_path);
2485 // After deletion, index of new selected item is one less
2486 new_path.index[depth - 1]--;
2487 if (preset == NULL && path->index[depth - 1] > 0)
2488 {
2489 // Try previous
2490 new_path.index[depth - 1] = path->index[depth - 1] - 1;
2491 preset = hb_preset_get(&new_path);
2492 }
2493 if (preset == NULL)
2494 {
2495 // perhaps we are deleting the last item in a folder
2496 // Try first item in next and previous folders
2497 depth = 1;
2498 new_path.index[1] = 0;
2499 // Try next
2500 new_path.index[depth - 1] = path->index[depth - 1] + 1;
2501 preset = hb_preset_get(&new_path);
2502 // After deletion, index of new selected item is one less
2503 new_path.index[depth - 1]--;
2504 if (preset == NULL && path->index[depth - 1] > 0)
2505 {
2506 // Try previous
2507 new_path.index[depth - 1] = path->index[depth - 1] - 1;
2508 preset = hb_preset_get(&new_path);
2509 }
2510 }
2511
2512 GtkTreeView * treeview;
2513 GtkTreeSelection * selection;
2514
2515 // unselect item to be deleted
2516 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
2517 selection = gtk_tree_view_get_selection(treeview);
2518 gtk_tree_selection_unselect_all(selection);
2519
2520 // Remove the item
2521 if (hb_preset_delete(path) >= 0)
2522 {
2523 presets_list_remove(ud, path);
2524 if (path->depth == 2)
2525 {
2526 // If deleting this item resulted in an empty folder
2527 // delete the folder
2528 int count = 0;
2529 hb_value_t * folder;
2530
2531 path->depth = 1;
2532 folder = hb_presets_get_folder_children(path);
2533 if (folder != NULL)
2534 {
2535 count = ghb_array_len(folder);
2536 }
2537 if (count == 0)
2538 {
2539 // delete the folder
2540 hb_preset_delete(path);
2541 presets_list_remove(ud, path);
2542 }
2543 }
2544 store_presets();
2545 ghb_presets_menu_reinit(ud);
2546 }
2547 if (preset != NULL)
2548 {
2549 select_preset2(ud, &new_path);
2550 }
2551 }
2552 free(path);
2553 ghb_update_ui_combo_box(ud, "PresetCategory", NULL, FALSE);
2554 }
2555
2556 // controls where valid drop locations are
2557 G_MODULE_EXPORT gboolean
2558 #if GTK_CHECK_VERSION(3, 90, 0)
presets_drag_motion_cb(GtkTreeView * tv,GdkDrop * ctx,gint x,gint y,signal_user_data_t * ud)2559 presets_drag_motion_cb(
2560 GtkTreeView *tv,
2561 GdkDrop *ctx,
2562 gint x,
2563 gint y,
2564 signal_user_data_t *ud)
2565 #else
2566 presets_drag_motion_cb(
2567 GtkTreeView *tv,
2568 GdkDragContext *ctx,
2569 gint x,
2570 gint y,
2571 guint time,
2572 signal_user_data_t *ud)
2573 #endif
2574 {
2575 GtkTreeViewDropPosition drop_pos;
2576 GtkTreeIter iter;
2577 GtkTreeView *srctv;
2578 GtkTreeModel *model;
2579 GtkTreeSelection *select;
2580 GtkTreePath *treepath;
2581 hb_preset_index_t *path;
2582 gint src_ptype, dst_ptype;
2583 gboolean src_folder, dst_folder;
2584 GhbValue *src_preset, *dst_preset;
2585 GtkWidget *widget;
2586 #if GTK_CHECK_VERSION(3, 90, 0)
2587 // Dummy time for backwards compatibility
2588 guint time = 0;
2589 #endif
2590
2591 treepath = g_object_get_data(G_OBJECT(tv), "dst-tree-path");
2592 if (treepath != NULL)
2593 {
2594 gtk_tree_path_free(treepath);
2595 }
2596 g_object_set_data(G_OBJECT(tv), "dst-tree-path", NULL);
2597
2598 #if GTK_CHECK_VERSION(3, 90, 0)
2599 GdkDrag *drag_ctx = gdk_drop_get_drag(ctx);
2600 widget = gtk_drag_get_source_widget(drag_ctx);
2601 #else
2602 widget = gtk_drag_get_source_widget(ctx);
2603 #endif
2604 if (widget == NULL || widget != GTK_WIDGET(tv))
2605 return TRUE;
2606
2607 // Get the type of the object being dragged
2608 srctv = GTK_TREE_VIEW(widget);
2609 select = gtk_tree_view_get_selection(srctv);
2610 gtk_tree_selection_get_selected(select, &model, &iter);
2611 path = ghb_tree_get_index(model, &iter);
2612
2613 src_preset = hb_preset_get(path);
2614 free(path);
2615 if (src_preset == NULL)
2616 {
2617 ghb_drag_status(ctx, 0, time);
2618 return TRUE;
2619 }
2620
2621 src_ptype = ghb_dict_get_int(src_preset, "Type");
2622 src_folder = ghb_dict_get_bool(src_preset, "Folder");
2623
2624 // The rest checks that the destination is a valid position
2625 // in the list.
2626 gtk_tree_view_get_dest_row_at_pos(tv, x, y, &treepath, &drop_pos);
2627 if (treepath == NULL)
2628 {
2629 ghb_drag_status(ctx, 0, time);
2630 return TRUE;
2631 }
2632 // Don't allow repositioning of builtin presets
2633 if (src_ptype != HB_PRESET_TYPE_CUSTOM)
2634 {
2635 gtk_tree_view_set_drag_dest_row(tv, NULL, drop_pos);
2636 ghb_drag_status(ctx, 0, time);
2637 gtk_tree_path_free(treepath);
2638 return TRUE;
2639 }
2640
2641 path = ghb_tree_path_get_index(treepath);
2642 dst_preset = hb_preset_get(path);
2643 free(path);
2644 if (dst_preset == NULL)
2645 {
2646 ghb_drag_status(ctx, 0, time);
2647 gtk_tree_path_free(treepath);
2648 return TRUE;
2649 }
2650
2651 dst_ptype = ghb_dict_get_int(dst_preset, "Type");
2652 dst_folder = ghb_dict_get_bool(dst_preset, "Folder");
2653
2654 // Don't allow mixing custom presets in the builtins
2655 if (dst_ptype != HB_PRESET_TYPE_CUSTOM)
2656 {
2657 gtk_tree_view_set_drag_dest_row(tv, NULL, drop_pos);
2658 ghb_drag_status(ctx, 0, time);
2659 gtk_tree_path_free(treepath);
2660 return TRUE;
2661 }
2662
2663 // Don't allow dropping source folder before/after a non-folder
2664 if (src_folder && !dst_folder)
2665 {
2666 gtk_tree_path_up(treepath);
2667 path = ghb_tree_path_get_index(treepath);
2668 dst_preset = hb_preset_get(path);
2669 free(path);
2670 if (dst_preset == NULL)
2671 {
2672 ghb_drag_status(ctx, 0, time);
2673 gtk_tree_path_free(treepath);
2674 return TRUE;
2675 }
2676
2677 dst_ptype = ghb_dict_get_int(dst_preset, "Type");
2678 dst_folder = ghb_dict_get_bool(dst_preset, "Folder");
2679 drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2680 }
2681
2682 // Don't allow dropping a source folder into another folder
2683 if ((src_folder || !dst_folder) &&
2684 drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
2685 {
2686 drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
2687 }
2688 if ((src_folder || !dst_folder) &&
2689 drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
2690 {
2691 drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2692 }
2693
2694 // Don't allow dropping a non-folder before a folder
2695 if (!src_folder && dst_folder && drop_pos == GTK_TREE_VIEW_DROP_BEFORE)
2696 {
2697 gtk_tree_view_set_drag_dest_row(tv, NULL, drop_pos);
2698 ghb_drag_status(ctx, 0, time);
2699 gtk_tree_path_free(treepath);
2700 return TRUE;
2701 }
2702
2703 if (!src_folder && dst_folder && drop_pos == GTK_TREE_VIEW_DROP_AFTER)
2704 {
2705 drop_pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
2706 }
2707
2708 gtk_tree_view_set_drag_dest_row(tv, treepath, drop_pos);
2709 ghb_drag_status(ctx, GDK_ACTION_MOVE, time);
2710
2711 g_object_set_data(G_OBJECT(tv), "dst-tree-path", treepath);
2712 g_object_set_data(G_OBJECT(tv), "dst-drop-pos", (gpointer)drop_pos);
2713
2714 return TRUE;
2715 }
2716
2717 G_MODULE_EXPORT void
2718 #if GTK_CHECK_VERSION(3, 90, 0)
presets_drag_data_received_cb(GtkTreeView * tv,GdkDrop * dc,GtkSelectionData * selection_data,signal_user_data_t * ud)2719 presets_drag_data_received_cb(
2720 GtkTreeView *tv,
2721 GdkDrop *dc,
2722 GtkSelectionData *selection_data,
2723 signal_user_data_t *ud)
2724 #else
2725 presets_drag_data_received_cb(
2726 GtkTreeView *tv,
2727 GdkDragContext *dc,
2728 gint x,
2729 gint y,
2730 GtkSelectionData *selection_data,
2731 guint info,
2732 guint t,
2733 signal_user_data_t *ud)
2734 #endif
2735 {
2736 GtkTreeModel *dst_model;
2737 GtkTreePath *dst_treepath = NULL;
2738 GtkTreeViewDropPosition drop_pos;
2739 GtkTreeIter dst_iter, src_iter;
2740 gint src_ptype;
2741 gboolean src_folder, dst_folder;
2742
2743 dst_model = gtk_tree_view_get_model(tv);
2744 dst_treepath = g_object_get_data(G_OBJECT(tv), "dst-tree-path");
2745 drop_pos = (GtkTreeViewDropPosition)g_object_get_data(G_OBJECT(tv),
2746 "dst-drop-pos");
2747 g_object_set_data(G_OBJECT(tv), "dst-tree-path", NULL);
2748 if (dst_treepath == NULL)
2749 {
2750 return;
2751 }
2752
2753 GtkTreeView *src_widget;
2754 GtkTreeModel *src_model;
2755 GtkTreeSelection *select;
2756 hb_preset_index_t *dst_path, *src_path;
2757
2758 #if GTK_CHECK_VERSION(3, 90, 0)
2759 GdkDrag *drag_ctx = gdk_drop_get_drag(dc);
2760 src_widget = GTK_TREE_VIEW(gtk_drag_get_source_widget(drag_ctx));
2761 #else
2762 src_widget = GTK_TREE_VIEW(gtk_drag_get_source_widget(dc));
2763 #endif
2764 select = gtk_tree_view_get_selection (src_widget);
2765 gtk_tree_selection_get_selected(select, &src_model, &src_iter);
2766
2767 src_path = ghb_tree_get_index(src_model, &src_iter);
2768 src_ptype = preset_get_type(src_path);
2769 src_folder = preset_is_folder(src_path);
2770
2771 // Don't allow repositioning of builtin presets
2772 if (src_ptype != HB_PRESET_TYPE_CUSTOM)
2773 {
2774 free(src_path);
2775 gtk_tree_path_free(dst_treepath);
2776 return;
2777 }
2778
2779 dst_path = ghb_tree_path_get_index(dst_treepath);
2780 dst_folder = preset_is_folder(dst_path);
2781
2782 // Don't allow allow dropping source folders after/before non-folders
2783 if (src_folder && !dst_folder)
2784 {
2785 gtk_tree_path_up(dst_treepath);
2786 dst_path = ghb_tree_path_get_index(dst_treepath);
2787 dst_folder = preset_is_folder(dst_path);
2788
2789 drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2790 }
2791
2792 // Don't allow dropping folders into other folders, only before/after
2793 if (src_folder && dst_folder)
2794 {
2795 if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
2796 drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
2797 else if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
2798 drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2799 }
2800
2801 if (!src_folder && dst_folder)
2802 {
2803 // Don't allow dropping preset before a folder
2804 if (drop_pos == GTK_TREE_VIEW_DROP_BEFORE)
2805 {
2806 free(src_path);
2807 free(dst_path);
2808 gtk_tree_path_free(dst_treepath);
2809 return;
2810 }
2811 // Don't allow dropping preset after a folder, only into
2812 if (drop_pos == GTK_TREE_VIEW_DROP_AFTER)
2813 drop_pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
2814 }
2815
2816 // Don't allow dropping into non-folder items
2817 if (!dst_folder)
2818 {
2819 if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
2820 drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
2821 else if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
2822 drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2823 }
2824 free(dst_path);
2825 if (gtk_tree_model_get_iter(dst_model, &dst_iter, dst_treepath))
2826 {
2827 GtkTreeIter iter;
2828
2829 // Insert new empty row in UI preset list
2830 // This logic determines the final position of the preset,
2831 // i.e. before, after or inside the target entry.
2832 // So the dst_path to move the preset to must be computed
2833 // after moving the entry in the UI list
2834 switch (drop_pos)
2835 {
2836 case GTK_TREE_VIEW_DROP_BEFORE:
2837 gtk_tree_store_insert_before(GTK_TREE_STORE(dst_model),
2838 &iter, NULL, &dst_iter);
2839 break;
2840
2841 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
2842 gtk_tree_store_insert(GTK_TREE_STORE(dst_model),
2843 &iter, &dst_iter, 0);
2844 break;
2845
2846 case GTK_TREE_VIEW_DROP_AFTER:
2847 gtk_tree_store_insert_after(GTK_TREE_STORE(dst_model),
2848 &iter, NULL, &dst_iter);
2849 break;
2850
2851 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
2852 gtk_tree_store_insert_after(GTK_TREE_STORE(dst_model),
2853 &iter, &dst_iter, NULL);
2854 break;
2855
2856 default:
2857 break;
2858 }
2859
2860 // Move source preset at the desired location
2861 dst_path = ghb_tree_get_index(dst_model, &iter);
2862 hb_preset_move(src_path, dst_path);
2863 free(dst_path);
2864
2865 // Remove the old entry in the UI list
2866 gtk_tree_store_remove(GTK_TREE_STORE(src_model), &src_iter);
2867
2868 // UI elements were shuffled again. recompute dst_path
2869 dst_path = ghb_tree_get_index(dst_model, &iter);
2870 presets_list_update_item(ud, dst_path, TRUE);
2871 select_preset2(ud, dst_path);
2872 free(dst_path);
2873
2874 store_presets();
2875 ghb_presets_menu_reinit(ud);
2876 }
2877 gtk_tree_path_free(dst_treepath);
2878 free(src_path);
2879 }
2880
2881 void
presets_row_expanded_cb(GtkTreeView * treeview,GtkTreeIter * iter,GtkTreePath * treepath,signal_user_data_t * ud)2882 presets_row_expanded_cb(
2883 GtkTreeView *treeview,
2884 GtkTreeIter *iter,
2885 GtkTreePath *treepath,
2886 signal_user_data_t *ud)
2887 {
2888 hb_preset_index_t *path;
2889 gboolean expanded;
2890 GhbValue *dict;
2891
2892 expanded = gtk_tree_view_row_expanded(treeview, treepath);
2893 path = ghb_tree_path_get_index(treepath);
2894 dict = hb_preset_get(path);
2895 free(path);
2896
2897 // Sanity check
2898 if (!ghb_dict_get_bool(dict, "Folder"))
2899 {
2900 g_warning("presets_row_expand_cb: Desync between presets and list");
2901 return;
2902 }
2903 if (ghb_dict_get_bool(dict, "FolderOpen"))
2904 {
2905 if (expanded)
2906 {
2907 return;
2908 }
2909 }
2910 else if (!expanded)
2911 {
2912 return;
2913 }
2914 ghb_dict_set_bool(dict, "FolderOpen", expanded);
2915 store_presets();
2916 }
2917
2918 // Makes a copy of the preset and assigns "PresetFullName" which
2919 // is use to look up the preset in the preset list should the need
2920 // arrise, e.g. saving changes to the preset.
2921 GhbValue*
ghb_get_current_preset(signal_user_data_t * ud)2922 ghb_get_current_preset(signal_user_data_t *ud)
2923 {
2924 GtkTreeView *tv;
2925 GtkTreeModel *tm;
2926 GtkTreeSelection *ts;
2927 GtkTreeIter ti;
2928 GhbValue *preset = NULL;
2929
2930 tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
2931 ts = gtk_tree_view_get_selection(tv);
2932 if (gtk_tree_selection_get_selected(ts, &tm, &ti))
2933 {
2934 hb_preset_index_t *path;
2935
2936 path = ghb_tree_get_index(tm, &ti);
2937 preset = hb_preset_get(path);
2938 if (preset != NULL)
2939 {
2940 char *fullname;
2941
2942 preset = hb_value_dup(preset);
2943 fullname = preset_get_fullname(path, "/", FALSE);
2944 ghb_dict_set_string(preset, "PresetFullName", fullname);
2945 free(fullname);
2946 }
2947 free(path);
2948 }
2949 return preset;
2950 }
2951
2952 G_MODULE_EXPORT void
presets_list_selection_changed_cb(GtkTreeSelection * selection,signal_user_data_t * ud)2953 presets_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
2954 {
2955 hb_preset_index_t * path;
2956
2957 path = get_selected_path(ud);
2958 if (path != NULL)
2959 {
2960 GhbValue *dict = hb_preset_get(path);
2961 if (!ghb_dict_get_bool(dict, "Folder") &&
2962 !ghb_dict_get_bool(ud->settings, "preset_reload"))
2963 {
2964 ghb_preset_to_settings(ud->settings, dict);
2965 char *fullname = preset_get_fullname(path, "/", FALSE);
2966 ghb_dict_set_string(ud->settings, "PresetFullName", fullname);
2967 free(fullname);
2968 ghb_set_current_title_settings(ud);
2969 ghb_load_post_settings(ud);
2970 }
2971 if (!ghb_dict_get_bool(dict, "Folder"))
2972 {
2973 GSimpleAction * action;
2974 GtkWidget * widget;
2975
2976 set_preset_menu_button_label(ud, path);
2977 action = G_SIMPLE_ACTION(g_action_map_lookup_action(
2978 G_ACTION_MAP(ud->app), "preset-reload"));
2979 g_simple_action_set_enabled(action, FALSE);
2980 widget = GHB_WIDGET(ud->builder, "preset_selection_modified_label");
2981 gtk_widget_set_visible(widget, FALSE);
2982 widget = GHB_WIDGET(ud->builder, "preset_selection_reload");
2983 gtk_widget_set_visible(widget, FALSE);
2984 widget = GHB_WIDGET(ud->builder, "preset_save_new");
2985 gtk_widget_set_visible(widget, FALSE);
2986 }
2987 int type = preset_get_type(path);
2988 GSimpleAction * action;
2989
2990 action = G_SIMPLE_ACTION(g_action_map_lookup_action(
2991 G_ACTION_MAP(ud->app), "preset-rename"));
2992 g_simple_action_set_enabled(action, type == HB_PRESET_TYPE_CUSTOM);
2993 action = G_SIMPLE_ACTION(g_action_map_lookup_action(
2994 G_ACTION_MAP(ud->app), "preset-save"));
2995 g_simple_action_set_enabled(action, type == HB_PRESET_TYPE_CUSTOM);
2996 free(path);
2997 }
2998 }
2999
3000 void
ghb_clear_presets_selection(signal_user_data_t * ud)3001 ghb_clear_presets_selection(signal_user_data_t *ud)
3002 {
3003 GtkTreeView * treeview;
3004 GtkTreeSelection * selection;
3005 GSimpleAction * action;
3006 GtkWidget * widget;
3007
3008 if (ud->dont_clear_presets) return;
3009 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
3010 selection = gtk_tree_view_get_selection(treeview);
3011 gtk_tree_selection_unselect_all(selection);
3012 ghb_dict_set_bool(ud->settings, "preset_modified", TRUE);
3013
3014
3015 action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
3016 "preset-reload"));
3017 g_simple_action_set_enabled(action, TRUE);
3018 widget = GHB_WIDGET(ud->builder, "preset_selection_modified_label");
3019 gtk_widget_set_visible(widget, TRUE);
3020 widget = GHB_WIDGET(ud->builder, "preset_selection_reload");
3021 gtk_widget_set_visible(widget, TRUE);
3022 widget = GHB_WIDGET(ud->builder, "preset_save_new");
3023 gtk_widget_set_visible(widget, TRUE);
3024 }
3025
3026 G_MODULE_EXPORT void
preset_default_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)3027 preset_default_action_cb(GSimpleAction *action, GVariant *param,
3028 signal_user_data_t *ud)
3029 {
3030 hb_preset_index_t *path = get_selected_path(ud);
3031 if (path != NULL)
3032 {
3033 hb_value_t *dict = hb_preset_get(path);
3034 if (dict != NULL && !ghb_dict_get_bool(dict, "Folder"))
3035 {
3036 ghb_presets_list_clear_default(ud);
3037 hb_presets_clear_default();
3038 ghb_dict_set_bool(dict, "Default", 1);
3039 ghb_presets_list_show_default(ud);
3040 store_presets();
3041 }
3042 g_free(path);
3043 }
3044 }
3045
3046 G_MODULE_EXPORT void
preset_edited_cb(GtkCellRendererText * cell,gchar * treepath_s,gchar * text,signal_user_data_t * ud)3047 preset_edited_cb(
3048 GtkCellRendererText *cell,
3049 gchar *treepath_s,
3050 gchar *text,
3051 signal_user_data_t *ud)
3052 {
3053 GtkTreePath *treepath;
3054 GtkTreeStore *store;
3055 GtkTreeView *treeview;
3056 GtkTreeIter iter;
3057 GhbValue *dict;
3058 hb_preset_index_t *path;
3059
3060 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
3061 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
3062 treepath = gtk_tree_path_new_from_string(treepath_s);
3063 path = ghb_tree_path_get_index(treepath);
3064 if (path != NULL)
3065 {
3066 dict = hb_preset_get(path);
3067 if (dict != NULL)
3068 {
3069 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
3070 gtk_tree_store_set(store, &iter, 0, text, -1);
3071
3072 ghb_dict_set_string(dict, "PresetName", text);
3073 store_presets();
3074 }
3075 }
3076 gtk_tree_path_free(treepath);
3077 free(path);
3078 }
3079
3080 G_MODULE_EXPORT void
preset_widget_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3081 preset_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3082 {
3083 ghb_widget_to_setting(ud->settings, widget);
3084 ghb_check_dependency(ud, widget, NULL);
3085 }
3086
3087