OSDN Git Service

SunOS support fixed with new ffmpeg and x264
[handbrake-jp/handbrake-jp-git.git] / gtk / src / presets.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * presets.c
4  * Copyright (C) John Stebbins 2008 <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, as published by the Free Software
10  * Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  * 
13  */
14 #include <glib.h>
15 #include <glib-object.h>
16 #include <glib/gstdio.h>
17 #include <string.h>
18 #include <gtk/gtk.h>
19 #include "settings.h"
20 #include "callbacks.h"
21 #include "audiohandler.h"
22 #include "hb-backend.h"
23 #include "plist.h"
24 #include "resources.h"
25 #include "presets.h"
26 #include "values.h"
27
28 #define MAX_NESTED_PRESET 3
29
30 // These are flags.  One bit for each feature
31 enum
32 {
33         PRESETS_CUST = 0x01,
34         PRESETS_FOLDER = 0x02,
35 };
36
37 static GValue *presetsPlist = NULL;
38 static GValue *internalPlist = NULL;
39 static GValue *prefsPlist = NULL;
40
41 static const GValue* preset_dict_get_value(GValue *dict, const gchar *key);
42 static void store_plist(GValue *plist, const gchar *name);
43
44 // This only handle limited depth
45 GtkTreePath*
46 ghb_tree_path_new_from_indices(gint *indices, gint len)
47 {
48         switch (len)
49         {
50                 case 1:
51                         return gtk_tree_path_new_from_indices(
52                                 indices[0], -1);
53                 case 2:
54                         return gtk_tree_path_new_from_indices(
55                                 indices[0], indices[1], -1);
56                 case 3:
57                         return gtk_tree_path_new_from_indices(
58                                 indices[0], indices[1], indices[2], -1);
59                 case 4:
60                         return gtk_tree_path_new_from_indices(
61                                 indices[0], indices[1], indices[2], indices[3], -1);
62                 case 5:
63                         return gtk_tree_path_new_from_indices(
64                                 indices[0], indices[1], indices[2], indices[3], indices[4], -1);
65                 default:
66                         return NULL;
67         }
68 }
69
70 GValue*
71 ghb_parse_preset_path(const gchar *path)
72 {
73         gchar **split;
74         GValue *preset;
75         gint ii;
76
77         preset = ghb_array_value_new(MAX_NESTED_PRESET);
78         split = g_strsplit(path, "#", MAX_NESTED_PRESET);
79         for (ii = 0; split[ii] != NULL; ii++)
80         {
81                 ghb_array_append(preset, ghb_string_value_new(split[ii]));
82         }
83         g_strfreev(split);
84         return preset;
85 }
86
87 static GValue*
88 preset_path_from_indices(GValue *presets, gint *indices, gint len)
89 {
90         gint ii;
91         GValue *path;
92
93         g_debug("preset_path_from_indices");
94         path = ghb_array_value_new(MAX_NESTED_PRESET);
95         for (ii = 0; ii < len; ii++)
96         {
97                 GValue *dict;
98                 gint count, ptype;
99                 const GValue *name;
100
101                 count = ghb_array_len(presets);
102                 if (indices[ii] >= count) break;
103                 dict = ghb_array_get_nth(presets, indices[ii]);
104                 name = ghb_dict_lookup(dict, "preset_name");
105                 ghb_array_append(path, ghb_value_dup(name));
106                 ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
107                 if (!(ptype & PRESETS_FOLDER))
108                         break;
109                 presets = ghb_dict_lookup(dict, "preset_folder");
110         }
111         return path;
112 }
113
114 gchar*
115 ghb_preset_path_string(const GValue *path)
116 {
117         gint count, ii;
118         GString *gstr;
119         GValue *val;
120         gchar *str;
121
122         gstr = g_string_new("");
123         if (path != NULL)
124         {
125                 count = ghb_array_len(path);
126                 for (ii = 0; ii < count; ii++)
127                 {
128                         val = ghb_array_get_nth(path, ii);
129                         str = ghb_value_string(val);
130                         g_string_append(gstr, str);
131                         if (ii < count-1)
132                                 g_string_append(gstr, "->");
133                         g_free(str);
134                 }
135         }
136         str = g_string_free(gstr, FALSE);
137         return str;
138 }
139
140 static void
141 debug_show_type(GType tp)
142 {
143         const gchar *str = "unknown";
144         if (tp == G_TYPE_STRING)
145         {
146                 str ="string";
147         }
148         else if (tp == G_TYPE_INT)
149         {
150                 str ="int";
151         }
152         else if (tp == G_TYPE_INT64)
153         {
154                 str ="int64";
155         }
156         else if (tp == G_TYPE_BOOLEAN)
157         {
158                 str ="bool";
159         }
160         else if (tp == ghb_array_get_type())
161         {
162                 str ="array";
163         }
164         else if (tp == ghb_dict_get_type())
165         {
166                 str ="dict";
167         }
168         g_message("Type: %s", str);
169 }
170
171 void
172 dump_preset_path(const gchar *msg, const GValue *path)
173 {
174         gchar *str;
175
176         if (path)
177                 debug_show_type (G_VALUE_TYPE(path));
178         str = ghb_preset_path_string(path);
179         g_message("%s path: (%s)", msg, str);
180         g_free(str);
181 }
182
183 void
184 dump_preset_indices(const gchar *msg, gint *indices, gint len)
185 {
186         gint ii;
187
188         g_message("%s indices: len %d", msg, len);
189         for (ii = 0; ii < len; ii++)
190         {
191                 printf("%d ", indices[ii]);
192         }
193         printf("\n");
194 }
195
196 #if 0
197 static gint
198 preset_path_cmp(const GValue *path1, const GValue *path2)
199 {
200         gint count, ii;
201         GValue *val;
202         gchar *str1, *str2;
203         gint result;
204
205         count = ghb_array_len(path1);
206         ii = ghb_array_len(path2);
207         if (ii != count)
208                 return ii - count;
209         for (ii = 0; ii < count; ii++)
210         {
211                 val = ghb_array_get_nth(path1, ii);
212                 str1 = ghb_value_string(val);
213                 val = ghb_array_get_nth(path2, ii);
214                 str2 = ghb_value_string(val);
215                 result = strcmp(str1, str2);
216                 if (result != 0)
217                         return result;
218                 g_free(str1);
219                 g_free(str2);
220         }
221         return 0;
222 }
223 #endif
224
225 static GValue*
226 presets_get_dict(GValue *presets, gint *indices, gint len)
227 {
228         gint ii, count, ptype;
229         GValue *dict = NULL;
230
231         g_debug("presets_get_dict ()");
232         for (ii = 0; ii < len; ii++)
233         {
234                 count = ghb_array_len(presets);
235                 if (indices[ii] >= count) return NULL;
236                 dict = ghb_array_get_nth(presets, indices[ii]);
237                 if (ii < len-1)
238                 {
239                         ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
240                         if (!(ptype & PRESETS_FOLDER))
241                                 return NULL;
242                         presets = ghb_dict_lookup(dict, "preset_folder");
243                 }
244         }
245         if (ii < len)
246                 return NULL;
247         return dict;
248 }
249
250 static GValue*
251 presets_get_folder(GValue *presets, gint *indices, gint len)
252 {
253         gint ii, count, ptype;
254         GValue *dict;
255
256         g_debug("presets_get_folder ()");
257         for (ii = 0; ii < len; ii++)
258         {
259                 count = ghb_array_len(presets);
260                 if (indices[ii] >= count) return NULL;
261                 dict = ghb_array_get_nth(presets, indices[ii]);
262                 ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
263                 if (!(ptype & PRESETS_FOLDER))
264                         break;
265                 presets = ghb_dict_lookup(dict, "preset_folder");
266         }
267         if (ii < len)
268                 return NULL;
269         return presets;
270 }
271
272 static GValue*
273 plist_get_dict(GValue *presets, const gchar *name)
274 {
275         if (presets == NULL || name == NULL) return NULL;
276         return ghb_dict_lookup(presets, name);
277 }
278
279 static const gchar*
280 preset_get_name(GValue *dict)
281 {
282         return g_value_get_string(ghb_dict_lookup(dict, "preset_name"));
283 }
284
285 gint
286 ghb_preset_flags(GValue *dict)
287 {
288         const GValue *gval;
289         gint ptype = 0;
290
291         gval = preset_dict_get_value(dict, "preset_type");
292         if (gval)
293         {
294                 ptype = ghb_value_int(gval);
295         }
296         return ptype;
297 }
298
299 static void
300 presets_remove_nth(GValue *presets, gint pos)
301 {
302         GValue *dict;
303         gint count;
304         
305         if (presets == NULL || pos < 0) return;
306         count = ghb_array_len(presets);
307         if (pos >= count) return;
308         dict = ghb_array_get_nth(presets, pos);
309         ghb_array_remove(presets, pos);
310         ghb_value_free(dict);
311 }
312
313 gboolean
314 ghb_presets_remove(
315         GValue *presets, 
316         gint *indices,
317         gint len)
318 {
319         GValue *folder = NULL;
320
321         folder = presets_get_folder(presets, indices, len-1);
322         if (folder)
323                 presets_remove_nth(folder, indices[len-1]);
324         else
325         {
326                 g_warning("ghb_presets_remove ()");
327                 g_warning("internal preset lookup error");
328                 return FALSE;
329         }
330         return TRUE;
331 }
332
333 static void
334 ghb_presets_replace(
335         GValue *presets, 
336         GValue *dict,
337         gint *indices,
338         gint len)
339 {
340         GValue *folder = NULL;
341
342         folder = presets_get_folder(presets, indices, len-1);
343         if (folder)
344                 ghb_array_replace(folder, indices[len-1], dict);
345         else
346         {
347                 g_warning("ghb_presets_replace ()");
348                 g_warning("internal preset lookup error");
349         }
350 }
351
352 static void
353 ghb_presets_insert(
354         GValue *presets, 
355         GValue *dict,
356         gint *indices,
357         gint len)
358 {
359         GValue *folder = NULL;
360
361         folder = presets_get_folder(presets, indices, len-1);
362         if (folder)
363                 ghb_array_insert(folder, indices[len-1], dict);
364         else
365         {
366                 g_warning("ghb_presets_insert ()");
367                 g_warning("internal preset lookup error");
368         }
369 }
370
371 static gint
372 presets_find_element(GValue *presets, const gchar *name)
373 {
374         GValue *dict;
375         gint count, ii;
376         
377         g_debug("presets_find_element () (%s)", name);
378         if (presets == NULL || name == NULL) return -1;
379         count = ghb_array_len(presets);
380         for (ii = 0; ii < count; ii++)
381         {
382                 const gchar *str;
383                 dict = ghb_array_get_nth(presets, ii);
384                 str = preset_get_name(dict);
385                 if (strcmp(name, str) == 0)
386                 {
387                         return ii;
388                 }
389         }
390         return -1;
391 }
392
393 static gint
394 single_find_pos(GValue *presets, const gchar *name, gint type)
395 {
396         GValue *dict;
397         gint count, ii, ptype, last;
398         
399         if (presets == NULL || name == NULL) return -1;
400         last = count = ghb_array_len(presets);
401         for (ii = 0; ii < count; ii++)
402         {
403                 const gchar *str;
404                 dict = ghb_array_get_nth(presets, ii);
405                 str = preset_get_name(dict);
406                 ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
407                 if (strcasecmp(name, str) <= 0 && ptype == type)
408                 {
409                         return ii;
410                 }
411                 if (ptype == type)
412                         last = ii+1;
413         }
414         return last;
415 }
416
417 static gint*
418 presets_find_pos(const GValue *path, gint type, gint *len)
419 {
420         GValue *nested;
421         GValue *val;
422         gint count, ii, ptype;
423         gint *indices = NULL;
424         const gchar *name;
425         GValue *dict;
426
427         g_debug("presets_find_pos () ");
428         nested = presetsPlist;
429         count = ghb_array_len(path);
430         indices = g_malloc(MAX_NESTED_PRESET * sizeof(gint));
431         for (ii = 0; ii < count-1; ii++)
432         {
433                 val = ghb_array_get_nth(path, ii);
434                 name = g_value_get_string(val);
435                 indices[ii] = presets_find_element(nested, name);
436                 if (indices[ii] == -1) return NULL;
437                 dict = ghb_array_get_nth(nested, indices[ii]);
438                 ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
439                 nested = NULL;
440                 if (!(ptype & PRESETS_FOLDER))
441                         break;
442                 nested = ghb_dict_lookup(dict, "preset_folder");
443         }
444         if (nested)
445         {
446                 const gchar *name;
447
448                 name = g_value_get_string(ghb_array_get_nth(path, count-1));
449                 indices[ii] = single_find_pos(nested, name, type);
450                 ii++;
451         }
452         *len = ii;
453         return indices;
454 }
455
456 static gint
457 preset_tree_depth(GValue *dict)
458 {
459         gint ptype;
460
461         ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
462         if (ptype & PRESETS_FOLDER)
463         {
464                 gint depth = 0;
465                 gint count, ii;
466                 GValue *presets;
467
468                 presets = ghb_dict_lookup(dict, "preset_folder");
469                 count = ghb_array_len(presets);
470                 for (ii = 0; ii < count; ii++)
471                 {
472                         gint tmp;
473
474                         dict = ghb_array_get_nth(presets, ii);
475                         tmp = preset_tree_depth(dict);
476                         depth = MAX(depth, tmp);
477                 }
478                 return depth + 1;
479         }
480         else
481         {
482                 return 1;
483         }
484 }
485
486 static gboolean
487 preset_is_default(GValue *dict)
488 {
489         const GValue *val;
490
491         val = preset_dict_get_value(dict, "Default");
492         return g_value_get_boolean(val);
493 }
494
495 static gint*
496 presets_find_default2(GValue *presets, gint *len)
497 {
498         gint count, ii;
499         gint *indices;
500
501         count = ghb_array_len(presets);
502         for (ii = 0; ii < count; ii++)
503         {
504                 GValue *dict;
505                 gint ptype;
506
507                 dict = ghb_array_get_nth(presets, ii);
508                 ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
509                 if (ptype & PRESETS_FOLDER)
510                 {
511                         GValue *nested;
512                         gint pos = *len;
513
514                         nested = ghb_dict_lookup(dict, "preset_folder");
515                         (*len)++;
516                         indices = presets_find_default2(nested, len);
517                         if (indices)
518                         {
519                                 indices[pos] = ii;
520                                 return indices;
521                         }
522                         else
523                                 *len = pos;
524                 }
525                 else
526                 {
527                         if (preset_is_default(dict))
528                         {
529                                 indices = malloc(MAX_NESTED_PRESET * sizeof(gint));
530                                 indices[*len] = ii;
531                                 (*len)++;
532                                 return indices;
533                         }
534                 }
535         }
536         return NULL;
537 }
538
539 static gint*
540 presets_find_default(gint *len)
541 {
542         *len = 0;
543         return presets_find_default2(presetsPlist, len);
544 }
545
546 gint*
547 ghb_preset_indices_from_path(
548         GValue *presets, 
549         const GValue *path,
550         gint *len)
551 {
552         GValue *nested;
553         GValue *val;
554         gint count, ii, ptype;
555         gint *indices = NULL;
556         const gchar *name;
557         GValue *dict;
558
559         g_debug("ghb_preset_indices_from_path () ");
560         nested = presets;
561         count = ghb_array_len(path);
562         if (count)
563                 indices = g_malloc(MAX_NESTED_PRESET * sizeof(gint));
564         *len = 0;
565         for (ii = 0; ii < count; ii++)
566         {
567                 val = ghb_array_get_nth(path, ii);
568                 name = g_value_get_string(val);
569                 indices[ii] = presets_find_element(nested, name);
570                 if (indices[ii] == -1)
571                 {
572                         g_free(indices);
573                         return NULL;
574                 }
575                 if (ii < count-1)
576                 {
577                         dict = ghb_array_get_nth(nested, indices[ii]);
578                         ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
579                         if (!(ptype & PRESETS_FOLDER))
580                         {
581                                 g_free(indices);
582                                 return NULL;
583                         }
584                         nested = ghb_dict_lookup(dict, "preset_folder");
585                 }
586         }
587         *len = ii;
588         return indices;
589 }
590
591 static gint
592 ghb_presets_get_type(
593         GValue *presets, 
594         gint *indices,
595         gint len)
596 {
597         GValue *dict;
598         gint flags = 0;
599
600         dict = presets_get_dict(presets, indices, len);
601         if (dict)
602         {
603                 flags = ghb_preset_flags(dict);
604         }
605         else
606         {
607                 g_warning("ghb_presets_get_type ()");
608                 g_warning("internal preset lookup error");
609         }
610         return flags;
611 }
612
613 void
614 presets_set_default(gint *indices, gint len)
615 {
616         GValue *dict;
617         gint *curr_indices, curr_len;
618         
619         g_debug("presets_set_default ()");
620         curr_indices = presets_find_default(&curr_len);
621         if (curr_indices)
622         {
623                 dict = presets_get_dict(presetsPlist, curr_indices, curr_len);
624                 if (dict)
625                 {
626                         ghb_dict_insert(dict, g_strdup("Default"), 
627                                                         ghb_boolean_value_new(FALSE));
628                 }
629         }
630         dict = presets_get_dict(presetsPlist, indices, len);
631         if (dict)
632         {
633                 ghb_dict_insert(dict, g_strdup("Default"), ghb_boolean_value_new(TRUE));
634         }
635         store_plist(presetsPlist, "presets");
636 }
637
638 // Used for sorting dictionaries.
639 gint
640 key_cmp(gconstpointer a, gconstpointer b)
641 {
642         gchar *stra = (gchar*)a;
643         gchar *strb = (gchar*)b;
644
645         return strcmp(stra, strb);
646 }
647
648 static const GValue*
649 preset_dict_get_value(GValue *dict, const gchar *key)
650 {
651         const GValue *gval = NULL;
652
653         if (dict)
654         {
655                 gval = ghb_dict_lookup(dict, key);
656         }
657         if (internalPlist == NULL) return NULL;
658         if (gval == NULL)
659         {
660                 dict = plist_get_dict(internalPlist, "Presets");
661                 if (dict == NULL) return NULL;
662                 gval = ghb_dict_lookup(dict, key);
663         }
664         return gval;
665 }
666
667 const gchar*
668 ghb_presets_get_description(GValue *pdict)
669 {
670         const gchar *desc;
671
672         if (pdict == NULL) return NULL;
673         desc = g_value_get_string(
674                         preset_dict_get_value(pdict, "preset_description"));
675         if (desc[0] == 0) return NULL;
676         return desc;
677 }
678
679
680 static const GValue*
681 preset_get_value(const GValue *preset, const gchar *key)
682 {
683         GValue *dict = NULL;
684         gint *indices, len;
685
686
687         indices = ghb_preset_indices_from_path(presetsPlist, preset, &len);
688         if (indices)
689         {
690                 dict = presets_get_dict(presetsPlist, indices, len);
691                 g_free(indices);
692         }
693         return preset_dict_get_value(dict, key);
694 }
695
696 static void init_settings_from_dict(
697         GValue *dest, GValue *internal, GValue *dict);
698
699 static void
700 init_settings_from_array(
701         GValue *dest, 
702         GValue *internal,
703         GValue *array)
704 {
705         GValue *gval, *val;
706         gint count, ii;
707         
708         count = ghb_array_len(array);
709         // The first element of the internal version is always the 
710         // template for the allowed values
711         gval = ghb_array_get_nth(internal, 0);
712         for (ii = 0; ii < count; ii++)
713         {
714                 val = NULL;
715                 val = ghb_array_get_nth(array, ii);
716                 if (val == NULL)
717                         val = gval;
718                 if (G_VALUE_TYPE(gval) == ghb_dict_get_type())
719                 {
720                         GValue *new_dict;
721                         new_dict = ghb_dict_value_new();
722                         ghb_array_append(dest, new_dict);
723                         if (G_VALUE_TYPE(val) == ghb_dict_get_type())
724                                 init_settings_from_dict(new_dict, gval, val);
725                         else
726                                 init_settings_from_dict(new_dict, gval, gval);
727                 }
728                 else if (G_VALUE_TYPE(gval) == ghb_array_get_type())
729                 {
730                         GValue *new_array;
731                         new_array = ghb_array_value_new(8);
732                         ghb_array_append(dest, new_array);
733                         if (G_VALUE_TYPE(val) == ghb_array_get_type())
734                                 init_settings_from_array(new_array, gval, val);
735                         else
736                                 init_settings_from_array(new_array, gval, gval);
737                 }
738                 else
739                 {
740                         ghb_array_append(dest, val);
741                 }
742         }
743 }
744
745 static void
746 init_settings_from_dict(
747         GValue *dest, 
748         GValue *internal,
749         GValue *dict)
750 {
751         GHashTableIter iter;
752         gchar *key;
753         GValue *gval, *val;
754         
755         ghb_dict_iter_init(&iter, internal);
756         // middle (void*) cast prevents gcc warning "defreferencing type-punned
757         // pointer will break strict-aliasing rules"
758         while (g_hash_table_iter_next(
759                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
760         {
761                 val = NULL;
762                 if (dict)
763                         val = ghb_dict_lookup(dict, key);
764                 if (val == NULL)
765                         val = gval;
766                 if (G_VALUE_TYPE(gval) == ghb_dict_get_type())
767                 {
768                         GValue *new_dict;
769                         new_dict = ghb_dict_value_new();
770                         ghb_settings_take_value(dest, key, new_dict);
771                         if (G_VALUE_TYPE(val) == ghb_dict_get_type())
772                                 init_settings_from_dict(new_dict, gval, val);
773                         else
774                                 init_settings_from_dict(new_dict, gval, gval);
775                 }
776                 else if (G_VALUE_TYPE(gval) == ghb_array_get_type())
777                 {
778                         GValue *new_array;
779                         new_array = ghb_array_value_new(8);
780                         ghb_settings_take_value(dest, key, new_array);
781                         if (G_VALUE_TYPE(val) == ghb_array_get_type())
782                                 init_settings_from_array(new_array, gval, val);
783                         else
784                                 init_settings_from_array(new_array, gval, gval);
785         
786                 }
787                 else
788                 {
789                         ghb_settings_set_value(dest, key, val);
790                 }
791         }
792 }
793
794 void
795 init_ui_from_dict(
796         signal_user_data_t *ud, 
797         GValue *internal,
798         GValue *dict)
799 {
800         GHashTableIter iter;
801         gchar *key;
802         GValue *gval, *val;
803         
804         ghb_dict_iter_init(&iter, internal);
805         // middle (void*) cast prevents gcc warning "defreferencing type-punned
806         // pointer will break strict-aliasing rules"
807         while (g_hash_table_iter_next(
808                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
809         {
810                 val = NULL;
811                 if (dict)
812                         val = ghb_dict_lookup(dict, key);
813                 if (val == NULL)
814                         val = gval;
815                 ghb_ui_update(ud, key, val);
816         }
817 }
818
819 static void
820 preset_to_ui(signal_user_data_t *ud, GValue *dict)
821 {
822         g_debug("preset_to_ui()\n");
823         // Initialize the ui from presets file.
824         GValue *internal;
825
826         // Get key list from internal default presets.  This way we do not
827         // load any unknown keys.
828         if (internalPlist == NULL) return;
829         internal = plist_get_dict(internalPlist, "Presets");
830         // Setting a ui widget will cause the corresponding setting
831         // to be set, but it also triggers a callback that can 
832         // have the side effect of using other settings values
833         // that have not yet been set.  So set *all* settings first
834         // then update the ui.
835         init_settings_from_dict(ud->settings, internal, dict);
836         init_ui_from_dict(ud, internal, dict);
837
838         if (ghb_settings_get_boolean(ud->settings, "allow_tweaks"))
839         {
840                 const GValue *gval;
841                 gval = preset_dict_get_value(dict, "deinterlace");
842                 if (gval)
843                 {
844                         ghb_ui_update(ud, "tweak_deinterlace", gval);
845                 }
846                 gval = preset_dict_get_value(dict, "denoise");
847                 if (gval)
848                 {
849                         ghb_ui_update(ud, "tweak_denoise", gval);
850                 }
851         }
852 }
853
854 void
855 ghb_settings_to_ui(signal_user_data_t *ud, GValue *dict)
856 {
857         init_ui_from_dict(ud, dict, dict);
858 }
859
860 void
861 ghb_set_preset_from_indices(signal_user_data_t *ud, gint *indices, gint len)
862 {
863         GValue *dict = NULL;
864         gint fallback[2] = {0, -1};
865
866         if (indices)
867                 dict = presets_get_dict(presetsPlist, indices, len);
868         if (dict == NULL)
869         {
870                 indices = fallback;
871                 len = 1;
872                 dict = presets_get_dict(presetsPlist, indices, len);
873         }
874         if (dict == NULL)
875         {
876                 preset_to_ui(ud, NULL);
877         }
878         else
879         {
880                 gint ptype;
881                 GValue *path;
882
883                 ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
884                 if (ptype & PRESETS_FOLDER)
885                         preset_to_ui(ud, NULL);
886                 else
887                         preset_to_ui(ud, dict);
888                 path = preset_path_from_indices(presetsPlist, indices, len);
889                 ghb_settings_set_value(ud->settings, "preset", path);
890                 ghb_value_free(path);
891         }
892 }
893
894 void
895 ghb_set_preset(signal_user_data_t *ud, const GValue *path)
896 {
897         gint *indices, len;
898         
899         g_debug("ghb_set_preset()");
900         indices = ghb_preset_indices_from_path(presetsPlist, path, &len);
901         ghb_set_preset_from_indices(ud, indices, len);
902         if (indices) g_free(indices);
903 }
904
905 void
906 ghb_update_from_preset(
907         signal_user_data_t *ud, 
908         const GValue *preset, 
909         const gchar *key)
910 {
911         const GValue *gval;
912         
913         g_debug("ghb_update_from_preset() %s", key);
914         if (preset == NULL) return;
915         gval = preset_get_value(preset, key);
916         if (gval != NULL)
917         {
918                 ghb_ui_update(ud, key, gval);
919         }
920 }
921
922 gchar*
923 ghb_get_user_config_dir()
924 {
925         const gchar *dir;
926         gchar *config;
927
928         dir = g_get_user_config_dir();
929         if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
930         {
931                 dir = g_get_home_dir();
932                 config = g_strdup_printf ("%s/.ghb", dir);
933                 if (!g_file_test(config, G_FILE_TEST_IS_DIR))
934                         g_mkdir (config, 0755);
935         }
936         else
937         {
938                 config = g_strdup_printf ("%s/ghb", dir);
939                 if (!g_file_test(config, G_FILE_TEST_IS_DIR))
940                         g_mkdir (config, 0755);
941         }
942         return config;
943 }
944
945 static void
946 store_plist(GValue *plist, const gchar *name)
947 {
948         gchar *config, *path;
949         FILE *file;
950
951         config = ghb_get_user_config_dir();
952         path = g_strdup_printf ("%s/%s", config, name);
953         file = g_fopen(path, "w");
954         g_free(config);
955         g_free(path);
956         ghb_plist_write(file, plist);
957         fclose(file);
958 }
959
960 static GValue*
961 load_plist(const gchar *name)
962 {
963         gchar *config, *path;
964         GValue *plist = NULL;
965
966         config = ghb_get_user_config_dir();
967         path = g_strdup_printf ("%s/%s", config, name);
968         if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
969         {
970                 plist = ghb_plist_parse_file(path);
971         }
972         g_free(config);
973         g_free(path);
974         return plist;
975 }
976
977 static void
978 remove_plist(const gchar *name)
979 {
980         gchar *config, *path;
981
982         config = ghb_get_user_config_dir();
983         path = g_strdup_printf ("%s/%s", config, name);
984         if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
985         {
986                 g_unlink(path);
987         }
988         g_free(path);
989         g_free(config);
990 }
991
992 static gboolean prefs_initializing = FALSE;
993
994 void
995 ghb_prefs_to_ui(signal_user_data_t *ud)
996 {
997         const GValue *gval;
998         gchar *key;
999         gchar *str;
1000         GValue *internal, *dict;
1001         GHashTableIter iter;
1002         
1003
1004         g_debug("ghb_prefs_to_ui");
1005         prefs_initializing = TRUE;
1006
1007         // Setting a ui widget will cause the corresponding setting
1008         // to be set, but it also triggers a callback that can 
1009         // have the side effect of using other settings values
1010         // that have not yet been set.  So set *all* settings first
1011         // then update the ui.
1012         internal = plist_get_dict(internalPlist, "Initialization");
1013         ghb_dict_iter_init(&iter, internal);
1014         // middle (void*) cast prevents gcc warning "defreferencing type-punned
1015         // pointer will break strict-aliasing rules"
1016         while (g_hash_table_iter_next(
1017                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
1018         {
1019                 ghb_ui_update(ud, key, gval);
1020         }
1021
1022         dict = plist_get_dict(prefsPlist, "Preferences");
1023         internal = plist_get_dict(internalPlist, "Preferences");
1024         ghb_dict_iter_init(&iter, internal);
1025         // middle (void*) cast prevents gcc warning "defreferencing type-punned
1026         // pointer will break strict-aliasing rules"
1027         while (g_hash_table_iter_next(
1028                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
1029     {
1030                 const GValue *value = NULL;
1031                 if (dict)
1032                         value = ghb_dict_lookup(dict, key);
1033                 if (value == NULL)
1034                         value = gval;
1035                 ghb_settings_set_value(ud->settings, key, value);
1036     }
1037         internal = plist_get_dict(internalPlist, "Preferences");
1038         ghb_dict_iter_init(&iter, internal);
1039         // middle (void*) cast prevents gcc warning "defreferencing type-punned
1040         // pointer will break strict-aliasing rules"
1041         while (g_hash_table_iter_next(
1042                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
1043         {
1044                 const GValue *value = NULL;
1045                 if (dict)
1046                         value = ghb_dict_lookup(dict, key);
1047                 if (value == NULL)
1048                         value = gval;
1049                 ghb_ui_update(ud, key, value);
1050         }
1051         const GValue *val;
1052         val = ghb_settings_get_value(ud->settings, "show_presets");
1053         ghb_ui_update(ud, "show_presets", val);
1054         if (ghb_settings_get_boolean(ud->settings, "hbfd_feature"))
1055         {
1056                 GtkAction *action;
1057                 val = ghb_settings_get_value(ud->settings, "hbfd");
1058                 ghb_ui_update(ud, "hbfd", val);
1059                 action = GHB_ACTION (ud->builder, "hbfd");
1060                 gtk_action_set_visible(action, TRUE);
1061         }
1062         else
1063         {
1064                 ghb_ui_update(ud, "hbfd", ghb_int64_value(0));
1065         }
1066         gval = ghb_settings_get_value(ud->settings, "default_source");
1067         ghb_settings_set_value (ud->settings, "source", gval);
1068         str = ghb_settings_get_string(ud->settings, "destination_dir");
1069
1070         gchar *path = g_strdup_printf ("%s/new_video.mp4", str);
1071         ghb_ui_update(ud, "destination", ghb_string_value(path));
1072         g_free(str);
1073         g_free(path);
1074
1075         prefs_initializing = FALSE;
1076 }
1077
1078 void
1079 ghb_prefs_save(GValue *settings)
1080 {
1081         GValue *dict;
1082         GValue *pref_dict;
1083         GHashTableIter iter;
1084         gchar *key;
1085         const GValue *value;
1086         
1087         if (prefs_initializing) return;
1088         dict = plist_get_dict(internalPlist, "Preferences");
1089         if (dict == NULL) return;
1090         pref_dict = plist_get_dict(prefsPlist, "Preferences");
1091         if (pref_dict == NULL) return;
1092         ghb_dict_iter_init(&iter, dict);
1093         // middle (void*) cast prevents gcc warning "defreferencing type-punned
1094         // pointer will break strict-aliasing rules"
1095         while (g_hash_table_iter_next(
1096                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&value))
1097     {
1098             value = ghb_settings_get_value(settings, key);
1099             if (value != NULL)
1100             {
1101                         ghb_dict_insert(pref_dict, g_strdup(key), ghb_value_dup(value));
1102             }
1103         }
1104     store_plist(prefsPlist, "preferences");
1105 }
1106
1107 void
1108 ghb_pref_save(GValue *settings, const gchar *key)
1109 {
1110         const GValue *value;
1111         
1112         if (prefs_initializing) return;
1113         value = ghb_settings_get_value(settings, key);
1114         if (value != NULL)
1115         {
1116                 GValue *dict;
1117                 dict = plist_get_dict(prefsPlist, "Preferences");
1118                 if (dict == NULL) return;
1119                 ghb_dict_insert(dict, g_strdup(key), ghb_value_dup(value));
1120                 store_plist(prefsPlist, "preferences");
1121         }
1122 }
1123
1124 void
1125 ghb_settings_init(signal_user_data_t *ud)
1126 {
1127         GValue *internal;
1128         GHashTableIter iter;
1129         gchar *key;
1130         GValue *gval;
1131
1132
1133         g_debug("ghb_settings_init");
1134         prefs_initializing = TRUE;
1135
1136         internalPlist = ghb_resource_get("internal-defaults");
1137         // Setting a ui widget will cause the corresponding setting
1138         // to be set, but it also triggers a callback that can 
1139         // have the side effect of using other settings values
1140         // that have not yet been set.  So set *all* settings first
1141         // then update the ui.
1142         internal = plist_get_dict(internalPlist, "Initialization");
1143         ghb_dict_iter_init(&iter, internal);
1144         // middle (void*) cast prevents gcc warning "defreferencing type-punned
1145         // pointer will break strict-aliasing rules"
1146         while (g_hash_table_iter_next(
1147                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
1148         {
1149                 ghb_settings_set_value(ud->settings, key, gval);
1150         }
1151
1152         internal = plist_get_dict(internalPlist, "Presets");
1153         ghb_dict_iter_init(&iter, internal);
1154         // middle (void*) cast prevents gcc warning "defreferencing type-punned
1155         // pointer will break strict-aliasing rules"
1156         while (g_hash_table_iter_next(
1157                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
1158         {
1159                 ghb_settings_set_value(ud->settings, key, gval);
1160         }
1161
1162         internal = plist_get_dict(internalPlist, "Preferences");
1163         ghb_dict_iter_init(&iter, internal);
1164         // middle (void*) cast prevents gcc warning "defreferencing type-punned
1165         // pointer will break strict-aliasing rules"
1166         while (g_hash_table_iter_next(
1167                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
1168         {
1169                 ghb_settings_set_value(ud->settings, key, gval);
1170         }
1171         prefs_initializing = FALSE;
1172 }
1173
1174 void
1175 ghb_settings_close()
1176 {
1177         if (internalPlist)
1178                 ghb_value_free(internalPlist);
1179         if (presetsPlist)
1180                 ghb_value_free(presetsPlist);
1181         if (prefsPlist)
1182                 ghb_value_free(prefsPlist);
1183 }
1184
1185 void
1186 ghb_prefs_load(signal_user_data_t *ud)
1187 {
1188         GValue *dict, *internal;
1189         GHashTableIter iter;
1190         gchar *key;
1191         GValue *gval, *path;
1192         
1193         g_debug("ghb_prefs_load");
1194         prefsPlist = load_plist("preferences");
1195         if (prefsPlist == NULL)
1196                 prefsPlist = ghb_dict_value_new();
1197         dict = plist_get_dict(prefsPlist, "Preferences");
1198         internal = plist_get_dict(internalPlist, "Preferences");
1199     if (dict == NULL && internal)
1200     {
1201                 dict = ghb_dict_value_new();
1202                 ghb_dict_insert(prefsPlist, g_strdup("Preferences"), dict);
1203
1204         // Get defaults from internal defaults 
1205                 ghb_dict_iter_init(&iter, internal);
1206                 // middle (void*) cast prevents gcc warning "defreferencing type-punned
1207                 // pointer will break strict-aliasing rules"
1208                 while (g_hash_table_iter_next(
1209                                 &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
1210         {
1211                         ghb_dict_insert(dict, g_strdup(key), ghb_value_dup(gval));
1212         }
1213                 const gchar *dir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS);
1214                 if (dir == NULL)
1215                 {
1216                         dir = ".";
1217                 }
1218                 ghb_dict_insert(dict, 
1219                         g_strdup("destination_dir"), ghb_value_dup(ghb_string_value(dir)));
1220                 store_plist(prefsPlist, "preferences");
1221     }
1222         // Read legacy default_preset preference and update accordingly
1223         path = ghb_dict_lookup(dict, "default_preset");
1224         if (path)
1225         {
1226                 gint *indices, len;
1227
1228                 if (G_VALUE_TYPE(path) == G_TYPE_STRING)
1229                 {
1230                         GValue *str = path;
1231
1232                         path = ghb_array_value_new(1);
1233                         ghb_array_append(path, ghb_value_dup(str));
1234                         indices = ghb_preset_indices_from_path(presetsPlist, path, &len);
1235                         ghb_value_free(path);
1236                 }
1237                 else
1238                         indices = ghb_preset_indices_from_path(presetsPlist, path, &len);
1239
1240                 if (indices)
1241                 {
1242                         presets_set_default(indices, len);
1243                         g_free(indices);
1244                 }
1245                 ghb_dict_remove(dict, "default_preset");
1246                 store_plist(prefsPlist, "preferences");
1247         }
1248 }
1249
1250 static const gchar*
1251 get_preset_color(gint flags)
1252 {
1253         const gchar *color;
1254
1255         if (flags & PRESETS_CUST)
1256         {
1257                 color = "DimGray";
1258                 if (flags & PRESETS_FOLDER)
1259                 {
1260                         color = "black";
1261                 }
1262         }
1263         else
1264         {
1265                 color = "blue";
1266                 if (flags & PRESETS_FOLDER)
1267                 {
1268                         color = "Navy";
1269                 }
1270         }
1271         return color;
1272 }
1273
1274 void
1275 ghb_presets_list_init(
1276         signal_user_data_t *ud, 
1277         gint *indices,
1278         gint len)
1279 {
1280         GtkTreeView *treeview;
1281         GtkTreeIter iter, titer, *piter;
1282         
1283         GtkTreeStore *store;
1284         const gchar *preset;
1285         GtkTreePath *parent_path;
1286         const gchar *description;
1287         gint flags;
1288         gboolean def;
1289         gint count, ii;
1290         GValue *dict;
1291         gint *more_indices;
1292         GValue *presets = NULL;
1293         
1294         g_debug("ghb_presets_list_init ()");
1295         more_indices = g_malloc((len+1)*sizeof(gint));
1296         memcpy(more_indices, indices, len*sizeof(gint));
1297         presets = presets_get_folder(presetsPlist, indices, len);
1298         if (presets == NULL)
1299         {
1300                 g_warning("Failed to find parent folder when adding child.");
1301                 return;
1302         }
1303         count = ghb_array_len(presets);
1304         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1305         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1306         parent_path = ghb_tree_path_new_from_indices(indices, len);
1307         if (parent_path)
1308         {
1309                 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &titer, parent_path);
1310                 piter = &titer;
1311                 gtk_tree_path_free(parent_path);
1312         }
1313         else
1314         {
1315                 piter = NULL;
1316         }
1317         for (ii = 0; ii < count; ii++)
1318         {
1319                 const gchar *color;
1320
1321                 // Additional settings, add row
1322                 dict = ghb_array_get_nth(presets, ii);
1323                 preset = preset_get_name(dict);
1324                 more_indices[len] = ii;
1325                 def = preset_is_default(dict);
1326
1327                 description = ghb_presets_get_description(dict);
1328                 gtk_tree_store_append(store, &iter, piter);
1329                 flags = ghb_preset_flags(dict);
1330                 color = get_preset_color(flags);
1331                 gtk_tree_store_set(store, &iter, 0, preset, 
1332                                                         1, def ? 800 : 400, 
1333                                                         2, def ? 2 : 0,
1334                                                         3, color, 
1335                                                         4, description,
1336                                                         -1);
1337                 if (def && piter)
1338                 {
1339                         GtkTreePath *path;
1340                         GtkTreeIter ppiter;
1341
1342                         if (gtk_tree_model_iter_parent(
1343                                 GTK_TREE_MODEL(store), &ppiter, piter))
1344                         {
1345                                 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &ppiter);
1346                                 gtk_tree_view_expand_row(treeview, path, FALSE);
1347                                 gtk_tree_path_free(path);
1348                         }
1349                         path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), piter);
1350                         gtk_tree_view_expand_row(treeview, path, FALSE);
1351                         gtk_tree_path_free(path);
1352                 }
1353                 if (flags & PRESETS_FOLDER)
1354                 {
1355                         ghb_presets_list_init(ud, more_indices, len+1);
1356                 }
1357         }
1358         g_free(more_indices);
1359 }
1360
1361 static void
1362 presets_list_update_item(
1363         signal_user_data_t *ud, 
1364         gint *indices,
1365         gint len)
1366 {
1367         GtkTreeView *treeview;
1368         GtkTreeStore *store;
1369         GtkTreeIter iter;
1370         GtkTreePath *treepath;
1371         const gchar *name;
1372         const gchar *description;
1373         gint flags;
1374         gboolean def;
1375         GValue *dict;
1376         const gchar *color;
1377         
1378         g_debug("presets_list_update_item ()");
1379         dict = presets_get_dict(presetsPlist, indices, len);
1380         if (dict == NULL)
1381                 return;
1382         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1383         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1384         treepath = ghb_tree_path_new_from_indices(indices, len);
1385         gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
1386         // Additional settings, add row
1387         name = preset_get_name(dict);
1388         def = preset_is_default(dict);
1389
1390         description = ghb_presets_get_description(dict);
1391         flags = ghb_preset_flags(dict);
1392         color = get_preset_color(flags);
1393         gtk_tree_store_set(store, &iter, 0, name, 
1394                                                 1, def ? 800 : 400, 
1395                                                 2, def ? 2 : 0,
1396                                                 3, color,
1397                                                 4, description,
1398                                                 -1);
1399         if (flags & PRESETS_FOLDER)
1400         {
1401                 ghb_presets_list_init(ud, indices, len);
1402         }
1403 }
1404
1405 static void
1406 presets_list_insert(
1407         signal_user_data_t *ud, 
1408         gint *indices,
1409         gint len)
1410 {
1411         GtkTreeView *treeview;
1412         GtkTreeIter iter, titer, *piter;
1413         GtkTreeStore *store;
1414         const gchar *preset;
1415         const gchar *description;
1416         gint flags;
1417         gboolean def;
1418         gint count;
1419         GValue *presets;
1420         GtkTreePath *parent_path;
1421         GValue *dict;
1422         const gchar *color;
1423         
1424         g_debug("presets_list_insert ()");
1425         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1426         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1427         presets = presets_get_folder(presetsPlist, indices, len-1);
1428         if (presets == NULL)
1429         {
1430                 g_warning("Failed to find parent folder while adding child.");
1431                 return;
1432         }
1433         parent_path = ghb_tree_path_new_from_indices(indices, len-1);
1434         if (parent_path)
1435         {
1436                 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &titer, parent_path);
1437                 piter = &titer;
1438                 gtk_tree_path_free(parent_path);
1439         }
1440         else
1441         {
1442                 piter = NULL;
1443         }
1444         count = ghb_array_len(presets);
1445         if (indices[len-1] >= count)
1446                 return;
1447         // Additional settings, add row
1448         dict = ghb_array_get_nth(presets, indices[len-1]);
1449         preset = preset_get_name(dict);
1450         def = preset_is_default(dict);
1451
1452         description = ghb_presets_get_description(dict);
1453         gtk_tree_store_insert(store, &iter, piter, indices[len-1]);
1454         flags = ghb_preset_flags(dict);
1455         color = get_preset_color(flags);
1456         gtk_tree_store_set(store, &iter, 0, preset, 
1457                                                 1, def ? 800 : 400, 
1458                                                 2, def ? 2 : 0,
1459                                                 3, color,
1460                                                 4, description,
1461                                                 -1);
1462         if (flags & PRESETS_FOLDER)
1463         {
1464                 ghb_presets_list_init(ud, indices, len);
1465         }
1466 }
1467
1468 static void
1469 presets_list_remove(
1470         signal_user_data_t *ud, 
1471         gint *indices,
1472         gint len)
1473 {
1474         GtkTreeView *treeview;
1475         GtkTreePath *treepath;
1476         GtkTreeIter iter;
1477         GtkTreeStore *store;
1478         
1479         g_debug("presets_list_remove ()");
1480         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1481         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1482         treepath = ghb_tree_path_new_from_indices(indices, len);
1483         if (treepath)
1484         {
1485                 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath))
1486                         gtk_tree_store_remove(store, &iter);
1487                 gtk_tree_path_free(treepath);
1488         }
1489 }
1490
1491 static void
1492 remove_std_presets(signal_user_data_t *ud)
1493 {
1494         gint count, ii;
1495         gint indices = 0;
1496
1497         count = ghb_array_len(presetsPlist);
1498         for (ii = count-1; ii >= 0; ii--)
1499         {
1500                 GValue *dict;
1501                 gint ptype;
1502
1503                 dict = ghb_array_get_nth(presetsPlist, ii);
1504                 ptype = ghb_value_int(preset_dict_get_value(dict, "preset_type"));
1505                 if (!(ptype & PRESETS_CUST))
1506                 {
1507                         if (ghb_presets_remove(presetsPlist, &indices, 1))
1508                         {
1509                                 presets_list_remove(ud, &indices, 1);
1510                         }
1511                 }
1512         }
1513 }
1514
1515 void
1516 ghb_presets_reload(signal_user_data_t *ud)
1517 {
1518         GValue *std_presets;
1519         gint count, ii;
1520
1521         g_debug("ghb_presets_reload()\n");
1522         std_presets = ghb_resource_get("standard-presets");
1523         if (std_presets == NULL) return;
1524
1525         remove_std_presets(ud);
1526         // Merge the keyfile contents into our presets
1527         count = ghb_array_len(std_presets);
1528         for (ii = count-1; ii >= 0; ii--)
1529         {
1530                 GValue *std_dict;
1531                 GValue *copy_dict;
1532                 GHashTableIter iter;
1533                 gchar *key;
1534                 GValue *value;
1535                 gint indices = 0;
1536
1537                 std_dict = ghb_array_get_nth(std_presets, ii);
1538                 copy_dict = ghb_dict_value_new();
1539                 ghb_presets_insert(presetsPlist, copy_dict, &indices, 1);
1540                 ghb_dict_iter_init(&iter, std_dict);
1541                 // middle (void*) cast prevents gcc warning "defreferencing 
1542                 // type-punned pointer will break strict-aliasing rules"
1543                 while (g_hash_table_iter_next(
1544                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&value))
1545                 {
1546                         ghb_dict_insert(copy_dict, g_strdup(key), ghb_value_dup(value));
1547                 }
1548                 presets_list_insert(ud, &indices, 1);
1549         }
1550         store_plist(presetsPlist, "presets");
1551 }
1552
1553 void
1554 ghb_save_queue(GValue *queue)
1555 {
1556         store_plist(queue, "queue");
1557 }
1558
1559 GValue*
1560 ghb_load_queue()
1561 {
1562         return load_plist("queue");
1563 }
1564
1565 void
1566 ghb_remove_queue_file()
1567 {
1568         remove_plist("queue");
1569 }
1570
1571 void
1572 ghb_presets_load()
1573 {
1574         presetsPlist = load_plist("presets");
1575         if (presetsPlist == NULL)
1576         {
1577                 presetsPlist = ghb_value_dup(ghb_resource_get("standard-presets"));
1578                 store_plist(presetsPlist, "presets");
1579         }
1580         if (G_VALUE_TYPE(presetsPlist) == ghb_dict_get_type())
1581         { // Presets is older dictionary format. Convert to array
1582                 GHashTableIter old_iter;
1583                 GValue *presets;
1584                 gchar *name;
1585                 GValue *orig_dict;
1586
1587                 presets = ghb_array_value_new(32);
1588                 ghb_dict_iter_init(&old_iter, presetsPlist);
1589                 // middle (void*) cast prevents gcc warning "defreferencing type-punned
1590                 // pointer will break strict-aliasing rules"
1591                 while (g_hash_table_iter_next(
1592                         &old_iter, (gpointer*)(void*)&name, (gpointer*)(void*)&orig_dict))
1593                 {
1594                         GHashTableIter iter;
1595                         gchar *key;
1596                         GValue *value, *dict;
1597
1598                         dict = ghb_dict_value_new();
1599                         ghb_dict_insert(dict, g_strdup("preset_name"), 
1600                                 ghb_string_value_new(name));
1601                         ghb_array_append(presets, dict);
1602                         ghb_dict_iter_init(&iter, orig_dict);
1603                         // middle (void*) cast prevents gcc warning "defreferencing 
1604                         // type-punned pointer will break strict-aliasing rules"
1605                         while (g_hash_table_iter_next(
1606                                 &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&value))
1607                         {
1608                                 ghb_dict_insert(dict, g_strdup(key), ghb_value_dup(value));
1609                         }
1610                 }
1611                 ghb_value_free(presetsPlist);
1612                 presetsPlist = presets;
1613                 store_plist(presetsPlist, "presets");
1614         }
1615 }
1616
1617 static void
1618 settings_save(signal_user_data_t *ud, const GValue *path)
1619 {
1620         GValue *dict, *internal;
1621         GHashTableIter iter;
1622         gchar *key;
1623         GValue *value;
1624         gboolean autoscale;
1625         gint *indices, len, count;
1626         const gchar *name;
1627         gboolean replace = FALSE;
1628
1629         g_debug("settings_save");
1630         if (internalPlist == NULL) return;
1631         count = ghb_array_len(path);
1632         name = g_value_get_string(ghb_array_get_nth(path, count-1));
1633         indices = ghb_preset_indices_from_path(presetsPlist, path, &len);
1634         if (indices)
1635         {
1636                 if (ghb_presets_get_type(presetsPlist, indices, len) & 
1637                         PRESETS_FOLDER)
1638                 {
1639                         gchar *message;
1640                         message = g_strdup_printf(
1641                                                 "%s: Folder already exists.\n"
1642                                                 "You can not replace it with a preset.",
1643                                                 name);
1644                         ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
1645                         g_free(message);
1646                         return;
1647                 }
1648                 dict = ghb_dict_value_new();
1649                 ghb_presets_replace(presetsPlist, dict, indices, len);
1650                 replace = TRUE;
1651         }
1652         else
1653         {
1654                 indices = presets_find_pos(path, PRESETS_CUST, &len);
1655                 if (indices)
1656                 {
1657                         dict = ghb_dict_value_new();
1658                         ghb_presets_insert(presetsPlist, dict, indices, len);
1659                 }
1660                 else
1661                 {
1662                         g_warning("failed to find insert path");
1663                         return;
1664                 }
1665         }
1666         ghb_dict_insert(dict, g_strdup("preset_name"), ghb_string_value_new(name));
1667
1668         if (ghb_settings_get_boolean(ud->settings, "allow_tweaks"))
1669         {
1670                 gchar *str;
1671                 str = ghb_settings_get_string(ud->settings, "tweak_deinterlace");
1672                 if (str)
1673                 {
1674                         ghb_settings_set_string(ud->settings, "deinterlace", str);
1675                         g_free(str);
1676                 }
1677                 str = ghb_settings_get_string(ud->settings, "tweak_denoise");
1678                 if (str)
1679                 {
1680                         ghb_settings_set_string(ud->settings, "denoise", str);
1681                         g_free(str);
1682                 }
1683         }
1684         autoscale = ghb_settings_get_boolean(ud->settings, "autoscale");
1685         ghb_settings_set_int64(ud->settings, "preset_type", PRESETS_CUST);
1686
1687         internal = plist_get_dict(internalPlist, "Presets");
1688         ghb_dict_iter_init(&iter, internal);
1689         // middle (void*) cast prevents gcc warning "defreferencing type-punned
1690         // pointer will break strict-aliasing rules"
1691         while (g_hash_table_iter_next(
1692                         &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&value))
1693         {
1694                 const GValue *gval;
1695                 gchar *key2;
1696
1697                 key2 = key;
1698                 if (!autoscale)
1699                 {
1700                         if (strcmp(key, "max_width") == 0)
1701                         {
1702                                 key2 = "scale_width";
1703                         }
1704                         else if (strcmp(key, "max_height") == 0)
1705                         {
1706                                 key2 = "scale_height";
1707                         }
1708                 }
1709                 gval = ghb_settings_get_value(ud->settings, key2);
1710                 if (gval == NULL)
1711                 {
1712                         g_debug("Setting (%s) is not in defaults\n", (gchar*)key);
1713                         continue;
1714                 }
1715                 if (ghb_value_cmp(gval, value) != 0)
1716                 {
1717                         // Differs from default value.  Store it.
1718                         ghb_dict_insert(dict, g_strdup(key), ghb_value_dup(gval));
1719                 }
1720         }
1721         if (replace)
1722                 presets_list_update_item(ud, indices, len);
1723         else
1724         {
1725                 ghb_dict_insert(dict, g_strdup("Default"), 
1726                                                 ghb_boolean_value_new(FALSE));
1727                 presets_list_insert(ud, indices, len);
1728         }
1729         g_free(indices);
1730         store_plist(presetsPlist, "presets");
1731         ud->dont_clear_presets = TRUE;
1732         ghb_set_preset(ud, path);
1733         ud->dont_clear_presets = FALSE;
1734         return;
1735 }
1736
1737 static void
1738 folder_save(signal_user_data_t *ud, const GValue *path)
1739 {
1740         GValue *dict, *folder;
1741         gint *indices, len, count;
1742         const gchar *name;
1743
1744         count = ghb_array_len(path);
1745         name = g_value_get_string(ghb_array_get_nth(path, count-1));
1746         indices = ghb_preset_indices_from_path(presetsPlist, path, &len);
1747         if (indices)
1748         {
1749                 if (!(ghb_presets_get_type(presetsPlist, indices, len) & 
1750                         PRESETS_FOLDER))
1751                 {
1752                         gchar *message;
1753                         message = g_strdup_printf(
1754                                                 "%s: Preset already exists.\n"
1755                                                 "You can not replace it with a folder.",
1756                                                 name);
1757                         ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
1758                         g_free(message);
1759                         g_free(indices);
1760                         return;
1761                 }
1762                 // Already exists, update its description
1763                 dict = presets_get_dict(presetsPlist, indices, len);
1764                 ghb_dict_insert(dict, g_strdup("preset_description"), 
1765                         ghb_value_dup(preset_dict_get_value(
1766                                 ud->settings, "preset_description")));
1767                 g_free(indices);
1768                 return;
1769         }
1770         else
1771         {
1772                 indices = presets_find_pos(path, PRESETS_CUST, &len);
1773                 if (indices)
1774                 {
1775                         dict = ghb_dict_value_new();
1776                         ghb_presets_insert(presetsPlist, dict, indices, len);
1777                 }
1778                 else
1779                 {
1780                         g_warning("failed to find insert path");
1781                         return;
1782                 }
1783         }
1784         ghb_dict_insert(dict, g_strdup("preset_description"), 
1785                 ghb_value_dup(preset_dict_get_value(
1786                         ud->settings, "preset_description")));
1787         ghb_dict_insert(dict, g_strdup("preset_name"), ghb_string_value_new(name));
1788         folder = ghb_array_value_new(8);
1789         ghb_dict_insert(dict, g_strdup("preset_folder"), folder);
1790         ghb_dict_insert(dict, g_strdup("preset_type"),
1791                                                         ghb_int64_value_new(PRESETS_FOLDER|PRESETS_CUST));
1792
1793         presets_list_insert(ud, indices, len);
1794         g_free(indices);
1795         store_plist(presetsPlist, "presets");
1796         ud->dont_clear_presets = TRUE;
1797         ghb_set_preset(ud, path);
1798         ud->dont_clear_presets = FALSE;
1799         return;
1800 }
1801
1802 void
1803 ghb_presets_list_default(signal_user_data_t *ud)
1804 {
1805         GtkTreeView *treeview;
1806         GtkTreePath *treepath;
1807         GtkTreeIter iter;
1808         GtkTreeStore *store;
1809         gint *indices, len;
1810         
1811         g_debug("ghb_presets_list_default ()");
1812         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1813         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1814         indices = presets_find_default(&len);
1815         if (indices == NULL) return;
1816         treepath = ghb_tree_path_new_from_indices(indices, len);
1817         if (treepath)
1818         {
1819                 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath))
1820                 {
1821                         gtk_tree_store_set(store, &iter, 
1822                                                 1, 800, 
1823                                                 2, 2 ,
1824                                                 -1);
1825                 }
1826                 gtk_tree_path_free(treepath);
1827         }
1828         g_free(indices);
1829 }
1830
1831 void
1832 ghb_presets_list_clear_default(signal_user_data_t *ud)
1833 {
1834         GtkTreeView *treeview;
1835         GtkTreePath *treepath;
1836         GtkTreeIter iter;
1837         GtkTreeStore *store;
1838         gint *indices, len;
1839         
1840         g_debug("ghb_presets_list_clear_default ()");
1841         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1842         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1843         indices = presets_find_default(&len);
1844         if (indices == NULL) return;
1845         treepath = ghb_tree_path_new_from_indices(indices, len);
1846         if (treepath)
1847         {
1848                 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath))
1849                 {
1850                         gtk_tree_store_set(store, &iter, 
1851                                                 1, 400, 
1852                                                 2, 0 ,
1853                                                 -1);
1854                 }
1855                 gtk_tree_path_free(treepath);
1856         }
1857         g_free(indices);
1858 }
1859
1860 static void
1861 ghb_select_preset2(
1862         GtkBuilder *builder, 
1863         gint *indices, 
1864         gint len)
1865 {
1866         GtkTreeView *treeview;
1867         GtkTreeSelection *selection;
1868         GtkTreeModel *store;
1869         GtkTreeIter iter;
1870         GtkTreePath *path;
1871         
1872         g_debug("ghb_select_preset2()");
1873         treeview = GTK_TREE_VIEW(GHB_WIDGET(builder, "presets_list"));
1874         selection = gtk_tree_view_get_selection (treeview);
1875         store = gtk_tree_view_get_model (treeview);
1876         path = ghb_tree_path_new_from_indices(indices, len);
1877         if (path)
1878         {
1879                 if (gtk_tree_model_get_iter(store, &iter, path))
1880                 {
1881                         gtk_tree_selection_select_iter (selection, &iter);
1882                 }
1883                 else
1884                 {
1885                         if (gtk_tree_model_get_iter_first(store, &iter))
1886                                 gtk_tree_selection_select_iter (selection, &iter);
1887                 }
1888                 gtk_tree_path_free(path);
1889         }
1890 }
1891
1892 void
1893 ghb_select_preset(GtkBuilder *builder, const GValue *path)
1894 {
1895         gint *indices, len;
1896
1897         g_debug("ghb_select_preset()");
1898         indices = ghb_preset_indices_from_path(presetsPlist, path, &len);
1899         if (indices)
1900         {
1901                 ghb_select_preset2(builder, indices, len);
1902                 g_free(indices);
1903         }
1904 }
1905
1906 void
1907 ghb_select_default_preset(GtkBuilder *builder)
1908 {
1909         gint *indices, len;
1910
1911         g_debug("ghb_select_default_preset()");
1912         indices = presets_find_default(&len);
1913         if (indices)
1914         {
1915                 ghb_select_preset2(builder, indices, len);
1916                 g_free(indices);
1917         }
1918 }
1919
1920 static void
1921 update_audio_presets(signal_user_data_t *ud)
1922 {
1923         g_debug("update_audio_presets");
1924         const GValue *audio_list;
1925
1926         audio_list = ghb_settings_get_value(ud->settings, "audio_list");
1927         ghb_settings_set_value(ud->settings, "pref_audio_list", audio_list);
1928 }
1929
1930 void
1931 enforce_preset_type(signal_user_data_t *ud, const GValue *path)
1932 {
1933         gint *indices, len;
1934         GtkWidget *normal, *folder;
1935         gint ptype;
1936
1937         normal = GHB_WIDGET(ud->builder, "preset_type_normal");
1938         folder = GHB_WIDGET(ud->builder, "preset_type_folder");
1939         indices = ghb_preset_indices_from_path(presetsPlist, path, &len);
1940         if (indices)
1941         {
1942                 ptype = ghb_presets_get_type(presetsPlist, indices, len);
1943                 if (ptype & PRESETS_FOLDER)
1944                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(folder), 
1945                                                                         TRUE);
1946                 else
1947                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(normal), 
1948                                                                         TRUE);
1949                 gtk_widget_set_sensitive(folder,  ptype & PRESETS_FOLDER);
1950                 gtk_widget_set_sensitive(normal,  !(ptype & PRESETS_FOLDER));
1951                 g_free(indices);
1952         }
1953         else
1954         {
1955                 gtk_widget_set_sensitive(folder, TRUE);
1956                 gtk_widget_set_sensitive(normal, TRUE);
1957         }
1958 }
1959
1960 void
1961 presets_save_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1962 {
1963         GtkWidget *dialog;
1964         GtkEntry *entry;
1965         GtkTextView *desc;
1966         GtkResponseType response;
1967         GValue *preset;
1968         const gchar *name = "";
1969         gint count, *indices, len;
1970
1971         g_debug("presets_save_clicked_cb ()");
1972         preset = ghb_settings_get_value (ud->settings, "preset_selection");
1973
1974         count = ghb_array_len(preset);
1975         if (count > 0)
1976                 name = g_value_get_string(ghb_array_get_nth(preset, count-1));
1977         else
1978                 count = 1;
1979         // Clear the description
1980         desc = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "preset_description"));
1981         dialog = GHB_WIDGET(ud->builder, "preset_save_dialog");
1982         entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "preset_name"));
1983         gtk_entry_set_text(entry, name);
1984         enforce_preset_type(ud, preset);
1985         response = gtk_dialog_run(GTK_DIALOG(dialog));
1986         gtk_widget_hide(dialog);
1987         if (response == GTK_RESPONSE_OK)
1988         {
1989                 // save the preset
1990                 const gchar *name = gtk_entry_get_text(entry);
1991                 GValue *dest;
1992
1993                 if (ghb_settings_get_boolean(ud->settings, "preset_type_folder"))
1994                 {
1995                         if (count > MAX_NESTED_PRESET-1)
1996                         {
1997                                 count = MAX_NESTED_PRESET-1;
1998                         }
1999                 }
2000                 dest = ghb_array_value_new(MAX_NESTED_PRESET);
2001                 indices = ghb_preset_indices_from_path(presetsPlist, preset, &len);
2002                 if (indices)
2003                 {
2004                         gint ptype;
2005
2006                         ptype = ghb_presets_get_type(presetsPlist, indices, len);
2007                         if (ptype & PRESETS_CUST)
2008                         {
2009                                 ghb_array_copy(dest, preset, count-1);
2010                         }
2011                 }
2012                 ghb_array_append(dest, ghb_string_value_new(name));
2013
2014                 ghb_widget_to_setting(ud->settings, GTK_WIDGET(desc));
2015                 if (ghb_settings_get_boolean(ud->settings, "preset_type_folder"))
2016                 {
2017                         folder_save(ud, dest);
2018                 }
2019                 else
2020                 {
2021                         // Construct the audio settings presets from the current audio list
2022                         update_audio_presets(ud);
2023                         settings_save(ud, dest);
2024                 }
2025                 // Make the new preset the selected item
2026                 ghb_select_preset(ud->builder, dest);
2027                 ghb_value_free(dest);
2028         }
2029 }
2030
2031 void
2032 preset_type_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2033 {
2034         ghb_widget_to_setting(ud->settings, widget);
2035 }
2036
2037 void
2038 preset_name_changed_cb(GtkWidget *entry, signal_user_data_t *ud)
2039 {
2040         gchar *name;
2041         GValue *preset, *dest;
2042         gint count;
2043
2044         preset = ghb_settings_get_value (ud->settings, "preset_selection");
2045         name = ghb_widget_string(entry);
2046         dest = ghb_value_dup(preset);
2047         count = ghb_array_len(dest);
2048         ghb_array_replace(dest, count-1, ghb_string_value_new(name));
2049         enforce_preset_type(ud, dest);
2050         ghb_value_free(dest);
2051 }
2052
2053 void
2054 presets_restore_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2055 {
2056         GValue *preset;
2057
2058         g_debug("presets_restore_clicked_cb ()");
2059         // Reload only the standard presets
2060         ghb_presets_reload(ud);
2061         // Updating the presets list shuffles things around
2062         // need to make sure the proper preset is selected
2063         preset = ghb_settings_get_value (ud->settings, "preset");
2064         ghb_select_preset(ud->builder, preset);
2065 }
2066
2067 void
2068 presets_remove_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2069 {
2070         GtkTreeView *treeview;
2071         GtkTreeSelection *selection;
2072         GtkTreeModel *store;
2073         GtkTreeIter iter;
2074         gchar *preset;
2075         GtkResponseType response;
2076
2077         g_debug("presets_remove_clicked_cb ()");
2078         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
2079         selection = gtk_tree_view_get_selection (treeview);
2080         if (gtk_tree_selection_get_selected(selection, &store, &iter))
2081         {
2082                 GtkWidget *dialog;
2083                 GtkTreePath *path;
2084                 gint *indices, len;
2085                 gint ptype;
2086
2087                 gtk_tree_model_get(store, &iter, 0, &preset, -1);
2088                 path = gtk_tree_model_get_path(store, &iter);
2089                 indices = gtk_tree_path_get_indices(path);
2090                 len = gtk_tree_path_get_depth(path);
2091
2092                 ptype = ghb_presets_get_type(presetsPlist, indices, len);
2093                 dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2094                                                         GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
2095                                                         "Confirm deletion of %s:\n\n%s", 
2096                                                         (ptype & PRESETS_FOLDER) ? "folder" : "preset",
2097                                                         preset);
2098                 response = gtk_dialog_run(GTK_DIALOG(dialog));
2099                 gtk_widget_destroy (dialog);
2100                 if (response == GTK_RESPONSE_YES)
2101                 {
2102                         GtkTreeIter nextIter = iter;
2103                         gboolean valid = TRUE;
2104                         if (!gtk_tree_model_iter_next(store, &nextIter))
2105                         {
2106                                 if (!gtk_tree_model_iter_parent(store, &nextIter, &iter))
2107                                 {
2108                                         valid = FALSE;
2109                                 }
2110                         }
2111                         // Remove the selected item
2112                         // First unselect it so that selecting the new item works properly
2113                         gtk_tree_selection_unselect_iter (selection, &iter);
2114                         if (ghb_presets_remove(presetsPlist, indices, len))
2115                         {
2116                                 store_plist(presetsPlist, "presets");
2117                                 presets_list_remove(ud, indices, len);
2118                         }
2119                         if (!valid)
2120                                 valid = gtk_tree_model_get_iter_first(store, &nextIter);
2121                         if (valid)
2122                         {
2123                                 gtk_tree_path_free(path);
2124                                 path = gtk_tree_model_get_path(store, &nextIter);
2125                                 indices = gtk_tree_path_get_indices(path);
2126                                 len = gtk_tree_path_get_depth(path);
2127                                 ghb_select_preset2(ud->builder, indices, len);
2128                         }
2129                 }
2130                 g_free(preset);
2131                 gtk_tree_path_free(path);
2132         }
2133 }
2134
2135 // controls where valid drop locations are
2136 gboolean
2137 presets_drag_motion_cb(
2138         GtkTreeView *tv,
2139         GdkDragContext *ctx,
2140         gint x,
2141         gint y,
2142         guint time,
2143         signal_user_data_t *ud)
2144 {
2145         GtkTreePath *path = NULL;
2146         GtkTreeViewDropPosition drop_pos;
2147         gint *indices, len;
2148         GtkTreeIter iter;
2149         GtkTreeView *srctv;
2150         GtkTreeModel *model;
2151         GtkTreeSelection *select;
2152         gint src_ptype, dst_ptype;
2153         GValue *preset;
2154         gint tree_depth, ii;
2155
2156         // Get the type of the object being dragged
2157         srctv = GTK_TREE_VIEW(gtk_drag_get_source_widget(ctx));
2158         select = gtk_tree_view_get_selection (srctv);
2159         gtk_tree_selection_get_selected (select, &model, &iter);
2160         path = gtk_tree_model_get_path (model, &iter);
2161         indices = gtk_tree_path_get_indices(path);
2162         len = gtk_tree_path_get_depth(path);
2163
2164         preset = presets_get_dict(presetsPlist, indices, len);
2165         tree_depth = preset_tree_depth(preset);
2166
2167         src_ptype = ghb_presets_get_type(presetsPlist, indices, len);
2168         gtk_tree_path_free(path);
2169
2170         if ((src_ptype & PRESETS_FOLDER) && tree_depth == 1)
2171                 tree_depth = 2;
2172
2173         // The rest checks that the destination is a valid position
2174         // in the list.
2175         gtk_tree_view_get_dest_row_at_pos (tv, x, y, &path, &drop_pos);
2176         if (path == NULL)
2177         {
2178                 gdk_drag_status(ctx, 0, time);
2179                 return TRUE;
2180         }
2181         // Don't allow repositioning of builtin presets
2182         if (!(src_ptype & PRESETS_CUST))
2183         {
2184                 gdk_drag_status(ctx, 0, time);
2185                 return TRUE;
2186         }
2187
2188         len = gtk_tree_path_get_depth(path);
2189         if (len+tree_depth-1 >= MAX_NESTED_PRESET)
2190         {
2191                 if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
2192                         drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
2193                 if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
2194                         drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2195         }
2196         for (ii = len+tree_depth-1; ii > MAX_NESTED_PRESET; ii--)
2197                 gtk_tree_path_up(path);
2198         indices = gtk_tree_path_get_indices(path);
2199         len = gtk_tree_path_get_depth(path);
2200         dst_ptype = ghb_presets_get_type(presetsPlist, indices, len);
2201         // Don't allow mixing custom presets in the builtins
2202         if (!(dst_ptype & PRESETS_CUST))
2203         {
2204                 gdk_drag_status(ctx, 0, time);
2205                 return TRUE;
2206         }
2207
2208         // Only allow *drop into* for folders
2209         if (!(dst_ptype & PRESETS_FOLDER))
2210         { 
2211                 if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
2212                         drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
2213                 if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
2214                         drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2215         }
2216
2217         len = gtk_tree_path_get_depth(path);
2218         gtk_tree_view_set_drag_dest_row(tv, path, drop_pos);
2219         gtk_tree_path_free(path);
2220         gdk_drag_status(ctx, GDK_ACTION_MOVE, time);
2221         return TRUE;
2222 }
2223
2224 void 
2225 presets_drag_cb(
2226         GtkTreeView *dstwidget, 
2227         GdkDragContext *dc, 
2228         gint x, gint y, 
2229         GtkSelectionData *selection_data, 
2230         guint info, guint t, 
2231         signal_user_data_t *ud)
2232 {
2233         GtkTreePath *path = NULL;
2234         GtkTreeViewDropPosition drop_pos;
2235         GtkTreeIter dstiter, srciter;
2236         gint *dst_indices, dst_len, *src_indices, src_len;
2237         gint src_ptype, dst_ptype;
2238         
2239         GtkTreeModel *dstmodel = gtk_tree_view_get_model(dstwidget);
2240                         
2241         g_debug("preset_drag_cb ()");
2242         // This doesn't work here for some reason...
2243         // gtk_tree_view_get_drag_dest_row(dstwidget, &path, &drop_pos);
2244         gtk_tree_view_get_dest_row_at_pos (dstwidget, x, y, &path, &drop_pos);
2245         // This little hack is needed because attempting to drop after
2246         // the last item gives us no path or drop_pos.
2247         if (path == NULL)
2248         {
2249                 gint n_children;
2250
2251                 n_children = gtk_tree_model_iter_n_children(dstmodel, NULL);
2252                 if (n_children)
2253                 {
2254                         drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2255                         path = gtk_tree_path_new_from_indices(n_children-1, -1);
2256                 }
2257                 else
2258                 {
2259                         drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
2260                         path = gtk_tree_path_new_from_indices(0, -1);
2261                 }
2262         }
2263         if (path)
2264         {
2265                 GtkTreeView *srcwidget;
2266                 GtkTreeModel *srcmodel;
2267                 GtkTreeSelection *select;
2268                 GtkTreePath *srcpath = NULL;
2269                 GValue *preset;
2270                 gint tree_depth, ii;
2271
2272                 srcwidget = GTK_TREE_VIEW(gtk_drag_get_source_widget(dc));
2273                 select = gtk_tree_view_get_selection (srcwidget);
2274                 gtk_tree_selection_get_selected (select, &srcmodel, &srciter);
2275
2276                 srcpath = gtk_tree_model_get_path (srcmodel, &srciter);
2277                 src_indices = gtk_tree_path_get_indices(srcpath);
2278                 src_len = gtk_tree_path_get_depth(srcpath);
2279                 src_ptype = ghb_presets_get_type(presetsPlist, src_indices, src_len);
2280                 preset = ghb_value_dup(
2281                                         presets_get_dict(presetsPlist, src_indices, src_len));
2282                 gtk_tree_path_free(srcpath);
2283
2284                 // Don't allow repositioning of builtin presets
2285                 if (!(src_ptype & PRESETS_CUST))
2286                         return;
2287
2288                 tree_depth = preset_tree_depth(preset);
2289                 if ((src_ptype & PRESETS_FOLDER) && tree_depth == 1)
2290                         tree_depth = 2;
2291
2292                 dst_len = gtk_tree_path_get_depth(path);
2293                 if (dst_len+tree_depth-1 >= MAX_NESTED_PRESET)
2294                 {
2295                         if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
2296                                 drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
2297                         if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
2298                                 drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2299                 }
2300
2301                 for (ii = dst_len+tree_depth-1; ii > MAX_NESTED_PRESET; ii--)
2302                         gtk_tree_path_up(path);
2303                 dst_indices = gtk_tree_path_get_indices(path);
2304                 dst_len = gtk_tree_path_get_depth(path);
2305                 dst_ptype = ghb_presets_get_type(presetsPlist, dst_indices, dst_len);
2306                 // Only allow *drop into* for folders
2307                 if (!(dst_ptype & PRESETS_FOLDER))
2308                 { 
2309                         if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
2310                                 drop_pos = GTK_TREE_VIEW_DROP_BEFORE;
2311                         if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
2312                                 drop_pos = GTK_TREE_VIEW_DROP_AFTER;
2313                 }
2314                 if (gtk_tree_model_get_iter (dstmodel, &dstiter, path))
2315                 {
2316                         GtkTreeIter iter;
2317                         GtkTreePath *dstpath = NULL;
2318
2319                         switch (drop_pos)
2320                         {
2321                                 case GTK_TREE_VIEW_DROP_BEFORE:
2322                                         gtk_tree_store_insert_before(GTK_TREE_STORE (dstmodel), 
2323                                                                                                 &iter, NULL, &dstiter);
2324                                         break;
2325
2326                                 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
2327                                         gtk_tree_store_insert(GTK_TREE_STORE (dstmodel), 
2328                                                                                                 &iter, &dstiter, 0);
2329                                         break;
2330
2331                                 case GTK_TREE_VIEW_DROP_AFTER:
2332                                         gtk_tree_store_insert_after(GTK_TREE_STORE (dstmodel), 
2333                                                                                                 &iter, NULL, &dstiter);
2334                                         break;
2335
2336                                 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
2337                                         gtk_tree_store_insert_after(GTK_TREE_STORE (dstmodel), 
2338                                                                                                 &iter, &dstiter, 0);
2339                                         break;
2340
2341                                 default:
2342                                         break;
2343                         }
2344
2345                         dstpath = gtk_tree_model_get_path (dstmodel, &iter);
2346                         dst_indices = gtk_tree_path_get_indices(dstpath);
2347                         dst_len = gtk_tree_path_get_depth(dstpath);
2348                         ghb_presets_insert(presetsPlist, preset, dst_indices, dst_len);
2349                         gtk_tree_path_free(dstpath);
2350
2351                         srcpath = gtk_tree_model_get_path (srcmodel, &srciter);
2352                         src_indices = gtk_tree_path_get_indices(srcpath);
2353                         src_len = gtk_tree_path_get_depth(srcpath);
2354                         ghb_presets_remove(presetsPlist, src_indices, src_len);
2355                         gtk_tree_path_free(srcpath);
2356
2357                         gtk_tree_store_remove (GTK_TREE_STORE (srcmodel), &srciter);
2358
2359                         dstpath = gtk_tree_model_get_path (dstmodel, &iter);
2360                         dst_indices = gtk_tree_path_get_indices(dstpath);
2361                         dst_len = gtk_tree_path_get_depth(dstpath);
2362                         presets_list_update_item(ud, dst_indices, dst_len);
2363                         gtk_tree_path_free(dstpath);
2364
2365                         store_plist(presetsPlist, "presets");
2366                 }
2367                 gtk_tree_path_free(path);
2368         }
2369 }
2370
2371 static void
2372 preset_update_title_deps(signal_user_data_t *ud, ghb_title_info_t *tinfo)
2373 {
2374         GtkWidget *widget;
2375
2376         ghb_ui_update(ud, "scale_width", 
2377                         ghb_int64_value(tinfo->width - tinfo->crop[2] - tinfo->crop[3]));
2378         // If anamorphic or keep_aspect, the hight will be automatically calculated
2379         gboolean keep_aspect, anamorphic;
2380         keep_aspect = ghb_settings_get_boolean(ud->settings, "keep_aspect");
2381         anamorphic = ghb_settings_get_boolean(ud->settings, "anamorphic");
2382         if (!(keep_aspect || anamorphic))
2383         {
2384                 ghb_ui_update(ud, "scale_height", 
2385                         ghb_int64_value(tinfo->height - tinfo->crop[0] - tinfo->crop[1]));
2386         }
2387
2388         // Set the limits of cropping.  hb_set_anamorphic_size crashes if
2389         // you pass it a cropped width or height == 0.
2390         gint bound;
2391         bound = tinfo->height / 2 - 2;
2392         widget = GHB_WIDGET (ud->builder, "crop_top");
2393         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
2394         widget = GHB_WIDGET (ud->builder, "crop_bottom");
2395         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
2396         bound = tinfo->width / 2 - 2;
2397         widget = GHB_WIDGET (ud->builder, "crop_left");
2398         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
2399         widget = GHB_WIDGET (ud->builder, "crop_right");
2400         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
2401         if (ghb_settings_get_boolean(ud->settings, "autocrop"))
2402         {
2403                 ghb_ui_update(ud, "crop_top", ghb_int64_value(tinfo->crop[0]));
2404                 ghb_ui_update(ud, "crop_bottom", ghb_int64_value(tinfo->crop[1]));
2405                 ghb_ui_update(ud, "crop_left", ghb_int64_value(tinfo->crop[2]));
2406                 ghb_ui_update(ud, "crop_right", ghb_int64_value(tinfo->crop[3]));
2407         }
2408         else
2409         {
2410                 ghb_ui_update(ud, "crop_top", ghb_int64_value(0));
2411                 ghb_ui_update(ud, "crop_bottom", ghb_int64_value(0));
2412                 ghb_ui_update(ud, "crop_left", ghb_int64_value(0));
2413                 ghb_ui_update(ud, "crop_right", ghb_int64_value(0));
2414         }
2415 }
2416
2417 void
2418 presets_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
2419 {
2420         GtkTreeModel *store;
2421         GtkTreeIter iter;
2422         ghb_title_info_t tinfo;
2423         GtkWidget *widget;
2424         
2425         g_debug("presets_list_selection_changed_cb ()");
2426         widget = GHB_WIDGET (ud->builder, "presets_remove");
2427         if (gtk_tree_selection_get_selected(selection, &store, &iter))
2428         {
2429                 GtkTreePath *treepath;
2430                 gint *indices, len, ptype;
2431                 GValue *path;
2432
2433                 treepath = gtk_tree_model_get_path(store, &iter);
2434                 indices = gtk_tree_path_get_indices(treepath);
2435                 len = gtk_tree_path_get_depth(treepath);
2436
2437                 path = preset_path_from_indices(presetsPlist, indices, len);
2438                 ghb_settings_take_value(ud->settings, "preset_selection", path);
2439
2440                 ptype = ghb_presets_get_type(presetsPlist, indices, len);
2441                 if (!(ptype & PRESETS_FOLDER))
2442                 {
2443                         ud->dont_clear_presets = TRUE;
2444                         // Temporarily set the video_quality range to (0,100)
2445                         // This is needed so the video_quality value does not get
2446                         // truncated when set.  The range will be readjusted below
2447                         GtkWidget *qp = GHB_WIDGET(ud->builder, "video_quality");
2448                         gtk_range_set_range (GTK_RANGE(qp), 0, 100);
2449                         // Clear the audio list prior to changing the preset.  Existing 
2450                         // audio can cause the container extension to be automatically 
2451                         // changed when it shouldn't be
2452                         ghb_clear_audio_list(ud);
2453                         ghb_set_preset_from_indices(ud, indices, len);
2454                         gtk_tree_path_free(treepath);
2455                         gint titleindex;
2456                         titleindex = ghb_settings_combo_int(ud->settings, "title");
2457                         ghb_set_pref_audio(titleindex, ud);
2458                         ghb_settings_set_boolean(ud->settings, "preset_modified", FALSE);
2459                         ud->dont_clear_presets = FALSE;
2460                         if (ghb_get_title_info (&tinfo, titleindex))
2461                         {
2462                                 preset_update_title_deps(ud, &tinfo);
2463                         }
2464                         ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
2465
2466                         gint vqmin, vqmax;
2467                         ghb_vquality_range(ud, &vqmin, &vqmax);
2468                         gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
2469                         gtk_widget_set_sensitive(widget, TRUE);
2470                 }
2471         }
2472         else
2473         {
2474                 g_debug("No selection???  Perhaps unselected.");
2475                 gtk_widget_set_sensitive(widget, FALSE);
2476         }
2477 }
2478
2479 void
2480 ghb_clear_presets_selection(signal_user_data_t *ud)
2481 {
2482         GtkTreeView *treeview;
2483         GtkTreeSelection *selection;
2484         
2485         if (ud->dont_clear_presets) return;
2486         g_debug("ghb_clear_presets_selection()");
2487         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
2488         selection = gtk_tree_view_get_selection (treeview);
2489         gtk_tree_selection_unselect_all (selection);
2490         ghb_settings_set_boolean(ud->settings, "preset_modified", TRUE);
2491 }
2492
2493 void
2494 presets_frame_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
2495 {
2496         GtkTreeView *treeview;
2497         GtkTreeSelection *selection;
2498         GtkTreeModel *store;
2499         GtkTreeIter iter;
2500         
2501         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
2502         selection = gtk_tree_view_get_selection(treeview);
2503         if (gtk_tree_selection_get_selected(selection, &store, &iter))
2504         {
2505                 GtkTreePath *path;
2506                 path = gtk_tree_model_get_path (store, &iter);
2507                 // Make the parent visible in scroll window if it is not.
2508                 gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0);
2509                 gtk_tree_path_free(path);
2510         }
2511 }
2512
2513 void
2514 presets_default_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2515 {
2516         GValue *preset;
2517         gint *indices, len;
2518
2519         g_debug("presets_default_clicked_cb ()");
2520         preset = ghb_settings_get_value(ud->settings, "preset_selection");
2521         indices = ghb_preset_indices_from_path(presetsPlist, preset, &len);
2522         if (indices)
2523         {
2524                 if (!(ghb_presets_get_type(presetsPlist, indices, len) & 
2525                         PRESETS_FOLDER))
2526                 {
2527                         ghb_presets_list_clear_default(ud);
2528                         presets_set_default(indices, len);
2529                         ghb_presets_list_default(ud);
2530                 }
2531                 g_free(indices);
2532         }
2533 }
2534