1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * queuehandler.c
4  * Copyright (C) John Stebbins 2008-2021 <stebbins@stebbins>
5  *
6  * queuehandler.c is free software.
7  *
8  * You may redistribute it and/or modify it under the terms of the
9  * GNU General Public License version 2, as published by the Free Software
10  * Foundation.
11  *
12  * queuehandler.c is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15  * See the GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with main.c.  If not, write to:
19  *  The Free Software Foundation, Inc.,
20  *  51 Franklin Street, Fifth Floor
21  *  Boston, MA  02110-1301, USA.
22  */
23 
24 #include "ghbcompat.h"
25 #include <glib/gstdio.h>
26 #include <glib/gi18n.h>
27 #include <gio/gio.h>
28 #include "handbrake/handbrake.h"
29 #include "settings.h"
30 #include "jobdict.h"
31 #include "titledict.h"
32 #include "hb-backend.h"
33 #include "values.h"
34 #include "callbacks.h"
35 #include "presets.h"
36 #include "audiohandler.h"
37 #include "subtitlehandler.h"
38 #include "ghb-dvd.h"
39 #include "plist.h"
40 #include "queuehandler.h"
41 
42 void ghb_queue_buttons_grey(signal_user_data_t *ud);
43 
44 // Callbacks
45 G_MODULE_EXPORT void
46 queue_remove_clicked_cb(GtkWidget *widget, signal_user_data_t *ud);
47 
48 #if GTK_CHECK_VERSION(3, 90, 0)
49 G_MODULE_EXPORT void
50 queue_drag_begin_cb(GtkWidget * widget, GdkDrag * context,
51                     signal_user_data_t * ud);
52 G_MODULE_EXPORT void
53 queue_drag_end_cb(GtkWidget * widget, GdkDrag * context,
54                   signal_user_data_t * ud);
55 G_MODULE_EXPORT void
56 queue_drag_data_get_cb(GtkWidget * widget, GdkDrag * context,
57                        GtkSelectionData * selection_data,
58                        signal_user_data_t * ud);
59 
60 G_MODULE_EXPORT gboolean
61 queue_row_key_cb(GtkEventControllerKey * keycon, guint keyval,
62                  guint keycode, GdkModifierType state,
63                  signal_user_data_t * ud);
64 #else
65 G_MODULE_EXPORT void
66 queue_drag_begin_cb(GtkWidget * widget, GdkDragContext * context,
67                     signal_user_data_t * ud);
68 G_MODULE_EXPORT void
69 queue_drag_end_cb(GtkWidget * widget, GdkDragContext * context,
70                   signal_user_data_t * ud);
71 G_MODULE_EXPORT void
72 queue_drag_data_get_cb(GtkWidget * widget, GdkDragContext * context,
73                        GtkSelectionData * selection_data,
74                        guint info, guint time, signal_user_data_t * ud);
75 #endif
76 G_MODULE_EXPORT void
77 title_selected_cb(GtkWidget *widget, signal_user_data_t *ud);
78 G_MODULE_EXPORT void
79 title_dest_file_cb(GtkWidget *widget, signal_user_data_t *ud);
80 G_MODULE_EXPORT void
81 title_dest_dir_cb(GtkWidget *widget, signal_user_data_t *ud);
82 
83 #if GTK_CHECK_VERSION(3, 90, 0)
84 static const char * queue_drag_entries[] = {
85     "application/queue-list-row-drop"
86 };
87 
88 void
ghb_queue_drag_n_drop_init(signal_user_data_t * ud)89 ghb_queue_drag_n_drop_init(signal_user_data_t * ud)
90 {
91     GtkWidget * widget;
92     GdkContentFormats * targets;
93 
94     widget = GHB_WIDGET(ud->builder, "queue_list");
95     targets = gdk_content_formats_new(queue_drag_entries,
96                                       G_N_ELEMENTS(queue_drag_entries));
97     gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_MOTION|GTK_DEST_DEFAULT_DROP,
98                       targets, GDK_ACTION_MOVE);
99     gdk_content_formats_unref(targets);
100 }
101 #else
102 static GtkTargetEntry queue_drag_entries[] = {
103    { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_APP, 0 }
104 };
105 
106 void
ghb_queue_drag_n_drop_init(signal_user_data_t * ud)107 ghb_queue_drag_n_drop_init(signal_user_data_t * ud)
108 {
109     GtkWidget * widget;
110 
111     widget = GHB_WIDGET(ud->builder, "queue_list");
112     gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_MOTION|GTK_DEST_DEFAULT_DROP,
113                       queue_drag_entries, 1, GDK_ACTION_MOVE);
114 }
115 #endif
116 
find_widget(GtkWidget * widget,gchar * name)117 static GtkWidget *find_widget(GtkWidget *widget, gchar *name)
118 {
119     const char *wname;
120     GtkWidget *result = NULL;
121 
122     if (widget == NULL || name == NULL)
123         return NULL;
124 
125     wname = gtk_widget_get_name(widget);
126     if (wname != NULL && !strncmp(wname, name, 80))
127     {
128         return widget;
129     }
130     if (GTK_IS_CONTAINER(widget))
131     {
132         GList *list, *link;
133         link = list = gtk_container_get_children(GTK_CONTAINER(widget));
134         while (link)
135         {
136             result = find_widget(GTK_WIDGET(link->data), name);
137             if (result != NULL)
138                 break;
139             link = link->next;
140         }
141         g_list_free(list);
142     }
143     return result;
144 }
145 
146 static void
queue_update_summary(GhbValue * queueDict,signal_user_data_t * ud)147 queue_update_summary(GhbValue * queueDict, signal_user_data_t *ud)
148 {
149     GString            * str;
150     char               * text;
151     const char         * ctext;
152     const char         * sep;
153     GtkWidget          * widget;
154     GhbValue           * uiDict     = NULL;
155     GhbValue           * jobDict    = NULL;
156     GhbValue           * sourceDict = NULL;
157     GhbValue           * rangeDict  = NULL;
158     GhbValue           * titleDict  = NULL;
159 
160     if (queueDict != NULL)
161     {
162         uiDict    = ghb_dict_get(queueDict, "uiSettings");
163         jobDict   = ghb_dict_get(queueDict, "Job");
164         titleDict = ghb_dict_get(queueDict, "Title");
165     }
166     if (titleDict == NULL)
167     {
168         // No title, clear summary
169         widget = GHB_WIDGET(ud->builder, "queue_summary_preset");
170         gtk_label_set_text(GTK_LABEL(widget), "");
171         widget = GHB_WIDGET(ud->builder, "queue_summary_source");
172         gtk_label_set_text(GTK_LABEL(widget), "");
173         widget = GHB_WIDGET(ud->builder, "queue_summary_dest");
174         gtk_label_set_text(GTK_LABEL(widget), "");
175         widget = GHB_WIDGET(ud->builder, "queue_summary_dimensions");
176         gtk_label_set_text(GTK_LABEL(widget), "");
177         widget = GHB_WIDGET(ud->builder, "queue_summary_video");
178         gtk_label_set_text(GTK_LABEL(widget), "");
179         widget = GHB_WIDGET(ud->builder, "queue_summary_audio");
180         gtk_label_set_text(GTK_LABEL(widget), "");
181         widget = GHB_WIDGET(ud->builder, "queue_summary_subtitle");
182         gtk_label_set_text(GTK_LABEL(widget), "");
183         return;
184     }
185 
186     // Preset: PresetName
187     const char * name;
188     gboolean preset_modified;
189 
190     preset_modified = ghb_dict_get_bool(uiDict, "preset_modified");
191     name = ghb_dict_get_string(uiDict, "PresetFullName");
192 
193     str = g_string_new("");
194     if (preset_modified)
195     {
196         g_string_append_printf(str, _("%s (Modified)"), name);
197     }
198     else
199     {
200         g_string_append_printf(str, "%s", name);
201     }
202 
203     widget = GHB_WIDGET(ud->builder, "queue_summary_preset");
204     text = g_string_free(str, FALSE);
205     gtk_label_set_text(GTK_LABEL(widget), text);
206     g_free(text);
207 
208     // Source
209     sourceDict = ghb_dict_get(jobDict, "Source");
210     ctext = ghb_dict_get_string(sourceDict, "Path");
211     widget = GHB_WIDGET(ud->builder, "queue_summary_source");
212     gtk_label_set_text(GTK_LABEL(widget), ctext);
213 
214     // Title
215     const char * rangeType;
216     int titleID;
217     int64_t rangeStart, rangeEnd;
218 
219     titleID       = ghb_dict_get_int(sourceDict, "Title");
220     rangeDict     = ghb_dict_get(sourceDict, "Range");
221     rangeType     = ghb_dict_get_string(rangeDict, "Type");
222     rangeStart    = ghb_dict_get_int(rangeDict, "Start");
223     rangeEnd      = ghb_dict_get_int(rangeDict, "End");
224 
225     str = g_string_new("");
226     g_string_append_printf(str, "%-8d", titleID);
227     if (!strcmp(rangeType, "chapter"))
228     {
229         g_string_append_printf(str, "Chapters: %ld to %ld",
230                                rangeStart, rangeEnd);
231     }
232     else if (!strcmp(rangeType, "time"))
233     {
234         int start_hh, start_mm;
235         double start_ss;
236         int end_hh, end_mm;
237         double end_ss;
238 
239         ghb_break_pts_duration(rangeStart, &start_hh, &start_mm, &start_ss);
240         ghb_break_pts_duration(rangeEnd, &end_hh, &end_mm, &end_ss);
241         g_string_append_printf(str,
242                                "Time: %02d:%02d:%05.2f to %02d:%02d:%05.2f",
243                                 start_hh, start_mm, start_ss,
244                                 end_hh, end_mm, end_ss);
245     }
246     else if (!strcmp(rangeType, "frame"))
247     {
248         g_string_append_printf(str, "Frames: %ld to %ld",
249                                rangeStart, rangeEnd);
250     }
251     text = g_string_free(str, FALSE);
252     widget = GHB_WIDGET(ud->builder, "queue_summary_title");
253     gtk_label_set_text(GTK_LABEL(widget), text);
254     g_free(text);
255 
256 
257     // Destination
258     ctext = ghb_dict_get_string(uiDict, "destination");
259     str = g_string_new(ctext);
260 
261     const char           * mux_name;
262     const hb_container_t * container;
263 
264     mux_name  = ghb_dict_get_string(uiDict, "FileFormat");
265     container = ghb_lookup_container_by_name(mux_name);
266     g_string_append_printf(str, "\n%s", container->name);
267 
268     gboolean markers, av_align, ipod = FALSE, http = FALSE;
269     markers  = ghb_dict_get_bool(uiDict, "ChapterMarkers");
270     av_align = ghb_dict_get_bool(uiDict, "AlignAVStart");
271     if (container->format & HB_MUX_MASK_MP4)
272     {
273         ipod = ghb_dict_get_bool(uiDict, "Mp4iPodCompatible");
274         http = ghb_dict_get_bool(uiDict, "Mp4HttpOptimize");
275     }
276 
277     sep = "\n";
278     if (markers)
279     {
280         g_string_append_printf(str, "%s%s", sep, _("Chapter Markers"));
281         sep = ", ";
282     }
283     if (av_align)
284     {
285         g_string_append_printf(str, "%s%s", sep, _("Align A/V"));
286         sep = ", ";
287     }
288     if (http)
289     {
290         g_string_append_printf(str, "%s%s", sep, _("Web Optimized"));
291         sep = ", ";
292     }
293     if (ipod)
294     {
295         g_string_append_printf(str, "%s%s", sep, _("iPod 5G"));
296         sep = ", ";
297     }
298 
299     text = g_string_free(str, FALSE);
300     widget = GHB_WIDGET(ud->builder, "queue_summary_dest");
301     gtk_label_set_text(GTK_LABEL(widget), text);
302     g_free(text);
303 
304     // Dimensions
305     double display_width;
306     int    width, height, display_height, par_width, par_height;
307     int    crop[4];
308     char * display_aspect;
309 
310     width          = ghb_dict_get_int(uiDict, "scale_width");
311     height         = ghb_dict_get_int(uiDict, "scale_height");
312     display_width  = ghb_dict_get_int(uiDict, "PictureDARWidth");
313     display_height = ghb_dict_get_int(uiDict, "DisplayHeight");
314     par_width      = ghb_dict_get_int(uiDict, "PicturePARWidth");
315     par_height     = ghb_dict_get_int(uiDict, "PicturePARHeight");
316     crop[0]        = ghb_dict_get_int(uiDict, "PictureTopCrop");
317     crop[1]        = ghb_dict_get_int(uiDict, "PictureBottomCrop");
318     crop[2]        = ghb_dict_get_int(uiDict, "PictureLeftCrop");
319     crop[3]        = ghb_dict_get_int(uiDict, "PictureRightCrop");
320 
321 
322     display_width = (double)width * par_width / par_height;
323     display_aspect = ghb_get_display_aspect_string(display_width,
324                                                    display_height);
325 
326     display_width  = ghb_dict_get_int(uiDict, "PictureDARWidth");
327     text = g_strdup_printf(_("%d:%d:%d:%d Crop\n"
328                              "%dx%d storage, %dx%d display\n"
329                              "%d:%d Pixel Aspect Ratio\n"
330                              "%s Display Aspect Ratio"),
331                            crop[0], crop[1], crop[2], crop[3],
332                            width, height, (int)display_width, display_height,
333                            par_width, par_height, display_aspect);
334     widget = GHB_WIDGET(ud->builder, "queue_summary_dimensions");
335     gtk_label_set_text(GTK_LABEL(widget), text);
336 
337     g_free(text);
338     g_free(display_aspect);
339 
340     // Video Track
341     const hb_encoder_t * video_encoder;
342     const hb_rate_t    * fps;
343     hb_rational_t        vrate;
344     char               * rate_str;
345     gboolean             two_pass, vqtype;
346 
347     str = g_string_new("");
348     video_encoder = ghb_settings_video_encoder(uiDict, "VideoEncoder");
349     vqtype = ghb_dict_get_bool(uiDict, "vquality_type_constant");
350     two_pass = ghb_dict_get_bool(uiDict, "VideoTwoPass");
351 
352     if (!vqtype)
353     {
354         // ABR
355         int br = ghb_dict_get_int(uiDict, "VideoAvgBitrate");
356         if (!two_pass)
357         {
358             g_string_append_printf(str, _("%s, Bitrate %dkbps"),
359                                    video_encoder->name, br);
360         }
361         else
362         {
363             g_string_append_printf(str, _("%s, Bitrate %dkbps (2 Pass)"),
364                                    video_encoder->name, br);
365         }
366     }
367     else
368     {
369         gdouble quality = ghb_dict_get_double(uiDict, "VideoQualitySlider");
370         g_string_append_printf(str, _("%s, Constant Quality %.4g(%s)"),
371                                video_encoder->name, quality,
372                                hb_video_quality_get_name(video_encoder->codec));
373     }
374     const char * enc_preset  = NULL;
375     const char * enc_tune    = NULL;
376     const char * enc_level   = NULL;
377     const char * enc_profile = NULL;
378 
379     if (hb_video_encoder_get_presets(video_encoder->codec) != NULL)
380     {
381         // The encoder supports presets
382         enc_preset  = ghb_dict_get_string(uiDict, "VideoPreset");
383     }
384     if (hb_video_encoder_get_tunes(video_encoder->codec) != NULL)
385     {
386         // The encoder supports tunes
387         enc_tune    = ghb_dict_get_string(uiDict, "VideoTune");
388     }
389     if (hb_video_encoder_get_profiles(video_encoder->codec) != NULL)
390     {
391         // The encoder supports profiles
392         enc_profile = ghb_dict_get_string(uiDict, "VideoProfile");
393     }
394     if (hb_video_encoder_get_levels(video_encoder->codec) != NULL)
395     {
396         // The encoder supports levels
397         enc_level   = ghb_dict_get_string(uiDict, "VideoLevel");
398     }
399 
400     sep = "\n";
401     if (enc_preset != NULL)
402     {
403         g_string_append_printf(str, _("%sPreset %s"), sep, enc_preset);
404         sep = ", ";
405     }
406     if (enc_tune != NULL)
407     {
408         g_string_append_printf(str, _("%sTune %s"), sep, enc_tune);
409         sep = ", ";
410     }
411     if (enc_profile != NULL)
412     {
413         g_string_append_printf(str, _("%sProfile %s"), sep, enc_profile);
414         sep = ", ";
415     }
416     if (enc_level != NULL)
417     {
418         g_string_append_printf(str, _("%sLevel %s"), sep, enc_level);
419         sep = ", ";
420     }
421 
422     fps = ghb_settings_video_framerate(uiDict, "VideoFramerate");
423     if (fps->rate == 0)
424     {
425         hb_dict_extract_rational(&vrate, titleDict, "FrameRate");
426     }
427     else
428     {
429         vrate.num = 27000000;
430         vrate.den = fps->rate;
431     }
432     rate_str = g_strdup_printf("%.6g", (gdouble)vrate.num / vrate.den);
433     if (ghb_dict_get_bool(uiDict, "VideoFramerateCFR"))
434     {
435         g_string_append_printf(str, _("\nConstant Framerate %s fps"), rate_str);
436     }
437     else if (ghb_dict_get_bool(uiDict, "VideoFrameratePFR"))
438     {
439         g_string_append_printf(str, _("\nPeak Framerate %s fps (may be lower)"),
440                                rate_str);
441     }
442     else if (ghb_dict_get_bool(uiDict, "VideoFramerateVFR"))
443     {
444         g_string_append_printf(str, _("\nVariable Framerate %s fps"), rate_str);
445     }
446     g_free(rate_str);
447 
448     // Append Filters to video summary
449     gboolean     detel, comb_detect, deint, decomb, deblock, nlmeans, denoise;
450     gboolean     unsharp, lapsharp, hflip, rot, gray, colorspace, chroma_smooth;
451 
452     ctext       = ghb_dict_get_string(uiDict, "PictureDetelecine");
453     detel       = ctext != NULL && !!strcasecmp(ctext, "off");
454     ctext       = ghb_dict_get_string(uiDict, "PictureCombDetectPreset");
455     comb_detect = ctext != NULL && !!strcasecmp(ctext, "off");
456     ctext       = ghb_dict_get_string(uiDict, "PictureDeinterlaceFilter");
457     deint       = ctext != NULL && !strcasecmp(ctext, "deinterlace");
458     decomb      = ctext != NULL && !strcasecmp(ctext, "decomb");
459     ctext       = ghb_dict_get_string(uiDict, "PictureDeblockPreset");
460     deblock     = ctext != NULL && !!strcasecmp(ctext, "off");
461     ctext       = ghb_dict_get_string(uiDict, "PictureDenoiseFilter");
462     nlmeans     = ctext != NULL && !strcasecmp(ctext, "nlmeans");
463     denoise     = ctext != NULL && !strcasecmp(ctext, "hqdn3d");
464     ctext       = ghb_dict_get_string(uiDict, "PictureSharpenFilter");
465     unsharp     = ctext != NULL && !strcasecmp(ctext, "unsharp");
466     lapsharp    = ctext != NULL && !strcasecmp(ctext, "lapsharp");
467     hflip       = ghb_dict_get_bool(uiDict, "hflip");
468     ctext       = ghb_dict_get_string(uiDict, "rotate");
469     rot         = ctext != NULL && !!strcasecmp(ctext, "0");
470     gray        = ghb_dict_get_bool(uiDict, "VideoGrayScale");
471     ctext       = ghb_dict_get_string(uiDict, "PictureColorspacePreset");
472     colorspace  = ctext != NULL && !!strcasecmp(ctext, "off");
473     ctext       = ghb_dict_get_string(uiDict, "PictureChromaSmoothPreset");
474     chroma_smooth = ctext != NULL && !!strcasecmp(ctext, "off");
475 
476     sep = "\n";
477     if (detel)
478     {
479         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DETELECINE);
480         g_string_append_printf(str, "%s%s", sep, filter->name);
481         sep = ", ";
482     }
483     if (comb_detect)
484     {
485         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_COMB_DETECT);
486         g_string_append_printf(str, "%s%s", sep, filter->name);
487         sep = ", ";
488     }
489     if (deint)
490     {
491         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DEINTERLACE);
492         g_string_append_printf(str, "%s%s", sep, filter->name);
493         sep = ", ";
494     }
495     if (decomb)
496     {
497         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DECOMB);
498         g_string_append_printf(str, "%s%s", sep, filter->name);
499         sep = ", ";
500     }
501     if (deblock)
502     {
503         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DEBLOCK);
504         g_string_append_printf(str, "%s%s", sep, filter->name);
505         sep = ", ";
506     }
507     if (nlmeans)
508     {
509         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_NLMEANS);
510         g_string_append_printf(str, "%s%s", sep, filter->name);
511         sep = ", ";
512     }
513     if (denoise)
514     {
515         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DENOISE);
516         g_string_append_printf(str, "%s%s", sep, filter->name);
517         sep = ", ";
518     }
519     if (chroma_smooth)
520     {
521         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_CHROMA_SMOOTH);
522         g_string_append_printf(str, "%s%s", sep, filter->name);
523         sep = ", ";
524     }
525     if (unsharp)
526     {
527         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_UNSHARP);
528         g_string_append_printf(str, "%s%s", sep, filter->name);
529         sep = ", ";
530     }
531     if (lapsharp)
532     {
533         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_LAPSHARP);
534         g_string_append_printf(str, "%s%s", sep, filter->name);
535         sep = ", ";
536     }
537     if (rot || hflip)
538     {
539         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_ROTATE);
540         g_string_append_printf(str, "%s%s", sep, filter->name);
541         sep = ", ";
542     }
543     if (gray)
544     {
545         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_GRAYSCALE);
546         g_string_append_printf(str, "%s%s", sep, filter->name);
547         sep = ", ";
548     }
549     if (colorspace)
550     {
551         hb_filter_object_t * filter = hb_filter_get(HB_FILTER_COLORSPACE);
552         g_string_append_printf(str, "%s%s", sep, filter->name);
553         sep = ", ";
554     }
555 
556     text = g_string_free(str, FALSE);
557     widget = GHB_WIDGET(ud->builder, "queue_summary_video");
558     gtk_label_set_text(GTK_LABEL(widget), text);
559     g_free(text);
560 
561     GhbValue * audioList;
562     GhbValue * sourceAudioList;
563     int        ii, count;
564 
565     sep             = "";
566     str             = g_string_new("");
567     sourceAudioList = ghb_dict_get(titleDict, "AudioList");
568     audioList       = ghb_get_job_audio_list(queueDict);
569     count           = ghb_array_len(audioList);
570     for (ii = 0; ii < count; ii++)
571     {
572         GhbValue           * asettings, * asource;
573         const hb_mixdown_t * audio_mix;
574         const hb_encoder_t * audio_encoder;
575         const char         * lang;
576         int                  track;
577 
578         asettings     = ghb_array_get(audioList, ii);
579         track         = ghb_dict_get_int(asettings, "Track");
580         asource       = ghb_array_get(sourceAudioList, track);
581         lang          = ghb_dict_get_string(asource, "Language");
582         audio_encoder = ghb_settings_audio_encoder(asettings, "Encoder");
583         if (audio_encoder->codec & HB_ACODEC_PASS_FLAG)
584         {
585             g_string_append_printf(str, "%s%s, %s", sep, lang,
586                                    audio_encoder->name);
587         }
588         else
589         {
590             audio_mix = ghb_settings_mixdown(asettings, "Mixdown");
591             g_string_append_printf(str, "%s%s, %s, %s", sep, lang,
592                                    audio_encoder->name, audio_mix->name);
593         }
594         sep = "\n";
595     }
596 
597     text = g_string_free(str, FALSE);
598     widget = GHB_WIDGET(ud->builder, "queue_summary_audio");
599     gtk_label_set_text(GTK_LABEL(widget), text);
600     g_free(text);
601 
602     GhbValue * subtitleDict;
603     GhbValue * searchDict;
604     GhbValue * subtitleList;
605     GhbValue * sourceSubtitleList;
606     gboolean   search;
607 
608     sep                = "";
609     str                = g_string_new("");
610     sourceSubtitleList = ghb_dict_get(titleDict, "SubtitleList");
611     subtitleDict       = ghb_get_job_subtitle_settings(queueDict);
612     subtitleList       = ghb_dict_get(subtitleDict, "SubtitleList");
613     searchDict         = ghb_dict_get(subtitleDict, "Search");
614     search             = ghb_dict_get_bool(searchDict, "Enable");
615     count              = ghb_array_len(subtitleList);
616     if (search)
617     {
618         gboolean force, burn, def;
619 
620         force = ghb_dict_get_bool(searchDict, "Forced");
621         burn  = ghb_dict_get_bool(searchDict, "Burn");
622         def   = ghb_dict_get_bool(searchDict, "Default");
623 
624         g_string_append_printf(str, _("Foreign Audio Scan"));
625         if (force)
626         {
627             g_string_append_printf(str, _(", Forced Only"));
628         }
629         if (burn)
630         {
631             g_string_append_printf(str, _(", Burned"));
632         }
633         else if (def)
634         {
635             g_string_append_printf(str, _(", Default"));
636         }
637         sep = "\n";
638     }
639     for (ii = 0; ii < count; ii++)
640     {
641         GhbValue           * subsettings, * subsource;
642         int                  track;
643         char               * desc;
644         gboolean             force, burn, def;
645 
646         subsettings = ghb_array_get(subtitleList, ii);
647         track       = ghb_dict_get_int(subsettings, "Track");
648         subsource   = ghb_array_get(sourceSubtitleList, track);
649         desc        = ghb_subtitle_short_description(subsource, subsettings);
650         force       = ghb_dict_get_bool(subsettings, "Forced");
651         burn        = ghb_dict_get_bool(subsettings, "Burn");
652         def         = ghb_dict_get_bool(subsettings, "Default");
653 
654         g_string_append_printf(str, "%s%s", sep, desc);
655         free(desc);
656         if (force)
657         {
658             g_string_append_printf(str, _(", Forced Only"));
659         }
660         if (burn)
661         {
662             g_string_append_printf(str, _(", Burned"));
663         }
664         else if (def)
665         {
666             g_string_append_printf(str, _(", Default"));
667         }
668         sep = "\n";
669     }
670 
671     text = g_string_free(str, FALSE);
672     widget = GHB_WIDGET(ud->builder, "queue_summary_subtitle");
673     gtk_label_set_text(GTK_LABEL(widget), text);
674     g_free(text);
675 }
676 
677 void
queue_update_stats(GhbValue * queueDict,signal_user_data_t * ud)678 queue_update_stats(GhbValue * queueDict, signal_user_data_t *ud)
679 {
680     GhbValue * uiDict;
681     GtkLabel * label;
682 
683     uiDict = ghb_dict_get(queueDict, "uiSettings");
684     if (uiDict == NULL) // should never happen
685     {
686         return;
687     }
688 
689     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_pass_label"));
690     gtk_widget_set_visible(GTK_WIDGET(label), FALSE);
691     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_pass"));
692     gtk_widget_set_visible(GTK_WIDGET(label), FALSE);
693 
694     const char * result = "";
695     int status = ghb_dict_get_int(uiDict, "job_status");
696 
697     if (status == GHB_QUEUE_PENDING)
698     {
699         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_start_time"));
700         gtk_label_set_text(label, "");
701         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_finish_time"));
702         gtk_label_set_text(label, "");
703         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_paused"));
704         gtk_label_set_text(label, "");
705         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_encode"));
706         gtk_label_set_text(label, "");
707         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_file_size"));
708         gtk_label_set_text(label, "");
709         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_result"));
710         gtk_label_set_text(label, "Pending");
711         return;
712     }
713 
714     switch (status)
715     {
716         case GHB_QUEUE_RUNNING:
717             // This job is running.
718             // ghb_queue_update_live_stats() will update stats
719             return;
720 
721         case GHB_QUEUE_DONE:
722             result = _("Completed");
723             break;
724 
725         case GHB_QUEUE_CANCELED:
726             result = _("Canceled");
727             break;
728 
729         case GHB_QUEUE_FAIL:
730             result = _("Failed");
731             break;
732 
733         case GHB_QUEUE_PENDING:
734         default:
735             result = _("Pending");
736             break;
737     }
738 
739     struct tm  * tm;
740     char         date[40] = "";
741     char       * str;
742     time_t       start, finish, paused, duration;
743 
744     start  = ghb_dict_get_int(uiDict, "job_start_time");
745     finish = ghb_dict_get_int(uiDict, "job_finish_time");
746     paused = ghb_dict_get_int(uiDict, "job_pause_time_ms") / 1000;
747 
748     tm     = localtime( &start );
749     strftime(date, 40, "%c", tm);
750     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_start_time"));
751     gtk_label_set_text(label, date);
752 
753     tm  = localtime( &finish );
754     strftime(date, 40, "%c", tm);
755     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_finish_time"));
756     gtk_label_set_text(label, date);
757 
758     int dd = 0, hh, mm, ss;
759     ghb_break_duration(paused, &hh, &mm, &ss);
760     if (hh >= 24)
761     {
762         dd = hh / 24;
763         hh = hh - dd * 24;
764     }
765     switch (dd)
766     {
767         case 0:
768             str = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
769             break;
770         case 1:
771             str = g_strdup_printf(_("%d Day %02d:%02d:%02d"), dd, hh, mm, ss);
772             break;
773         default:
774             str = g_strdup_printf(_("%d Days %02d:%02d:%02d"), dd, hh, mm, ss);
775             break;
776     }
777     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_paused"));
778     gtk_label_set_text(label, str);
779     g_free(str);
780 
781     duration = finish - start;
782     if (duration < 0)
783     {
784         duration = 0;
785     }
786     dd = 0;
787     ghb_break_duration(duration, &hh, &mm, &ss);
788     if (hh >= 24)
789     {
790         dd = hh / 24;
791         hh = hh - dd * 24;
792     }
793     switch (dd)
794     {
795         case 0:
796             str = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
797             break;
798         case 1:
799             str = g_strdup_printf(_("%d Day %02d:%02d:%02d"), dd, hh, mm, ss);
800             break;
801         default:
802             str = g_strdup_printf(_("%d Days %02d:%02d:%02d"), dd, hh, mm, ss);
803             break;
804     }
805     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_encode"));
806     gtk_label_set_text(label, str);
807     g_free(str);
808 
809     const char  * path;
810     struct stat   stbuf;
811 
812     path = ghb_dict_get_string(uiDict, "destination");
813     if (g_stat(path, &stbuf) == 0)
814     {
815         const char * units = _("B");
816         double size = stbuf.st_size;
817         if (size > 1024)
818         {
819             units = _("KB");
820             size /= 1024.0;
821         }
822         if (size > 1024)
823         {
824             units = _("MB");
825             size /= 1024.0;
826         }
827         if (size > 1024)
828         {
829             units = _("GB");
830             size /= 1024.0;
831         }
832         str = g_strdup_printf("%.2f %s", size, units);
833         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_file_size"));
834         gtk_label_set_text(label, str);
835         g_free(str);
836     }
837     else
838     {
839         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_file_size"));
840         gtk_label_set_text(label, _("Not Available"));
841     }
842 
843     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_result"));
844     gtk_label_set_text(label, result);
845 }
846 
queue_update_current_stats(signal_user_data_t * ud)847 void queue_update_current_stats(signal_user_data_t * ud)
848 {
849     GtkListBox    * lb;
850     GtkListBoxRow * row;
851     gint            index;
852     GhbValue      * queueDict;
853 
854     lb  = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
855     row = gtk_list_box_get_selected_row(lb);
856     if (row != NULL)
857     {
858         // There is a queue list row selected
859         index = gtk_list_box_row_get_index(row);
860         if (index < 0 || index >= ghb_array_len(ud->queue))
861         { // Should never happen
862             return;
863         }
864         queueDict = ghb_array_get(ud->queue, index);
865         queue_update_stats(queueDict, ud);
866     }
867 }
868 
869 #define ACTIVITY_MAX_READ_SZ (1024*1024)
read_log(signal_user_data_t * ud,const char * log_path)870 static void read_log(signal_user_data_t * ud, const char * log_path)
871 {
872     FILE        * f;
873     size_t        size, req_size;
874     GtkTextIter   iter;
875     char        * buf;
876 
877     if (ud->extra_activity_path != NULL &&
878         !strcmp(ud->extra_activity_path, log_path))
879     {
880         return;
881     }
882     g_free(ud->extra_activity_path);
883     ud->extra_activity_path = g_strdup(log_path);
884 
885     gtk_text_buffer_set_text(ud->extra_activity_buffer, "", 0);
886 
887     f = g_fopen(log_path, "r");
888     if (f == NULL)
889     {
890         return;
891     }
892     fseek(f, 0, SEEK_END);
893     req_size = ftell(f);
894     fseek(f, 0, SEEK_SET);
895     if (req_size > ACTIVITY_MAX_READ_SZ)
896     {
897         req_size = ACTIVITY_MAX_READ_SZ;
898     }
899     buf = g_malloc(req_size);
900     while (!feof(f))
901     {
902         size = fread(buf, 1, req_size, f);
903         if (size <= 0)
904         {
905             break;
906         }
907         gtk_text_buffer_get_end_iter(ud->extra_activity_buffer, &iter);
908         gtk_text_buffer_insert(ud->extra_activity_buffer, &iter, buf, size);
909     }
910     fclose(f);
911     g_free(buf);
912 }
913 
914 // Display/Hide queue activity log pane
915 // Choose buffer to display. Queue logs use 1 of 2 available buffers
916 //      queue_activity_buffer - live buffer, updated as encode proceeds
917 //      extra_activity_buffer - non-live buffer, populated from log file
918 // If showing non-live buffer, read log file into buffer
ghb_queue_select_log(signal_user_data_t * ud)919 void ghb_queue_select_log(signal_user_data_t * ud)
920 {
921     GtkListBox    * lb;
922     GtkListBoxRow * row;
923     GtkTextBuffer * current;
924     gint            index;
925     GhbValue      * queueDict, *uiDict;
926 
927     lb              = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
928     row             = gtk_list_box_get_selected_row(lb);
929     if (row != NULL)
930     {
931         // There is a queue list row selected
932         GtkTextView * tv;
933         int           status;
934         const char  * log_path;
935 
936         index = gtk_list_box_row_get_index(row);
937         if (index < 0 || index >= ghb_array_len(ud->queue))
938         { // Should never happen
939             return;
940         }
941         queueDict = ghb_array_get(ud->queue, index);
942         uiDict = ghb_dict_get(queueDict, "uiSettings");
943         // Get the current buffer that is displayed in the queue log
944         tv = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "queue_activity_view"));
945         current = gtk_text_view_get_buffer(tv);
946 
947         status = ghb_dict_get_int(uiDict, "job_status");
948         log_path = ghb_dict_get_string(uiDict, "ActivityFilename");
949         if (status != GHB_QUEUE_PENDING && log_path != NULL)
950         {
951             ghb_ui_update(ud, "queue_activity_location",
952                           ghb_string_value(log_path));
953         }
954         else
955         {
956             ghb_ui_update(ud, "queue_activity_location", ghb_string_value(""));
957         }
958         if (status == GHB_QUEUE_RUNNING)
959         {
960             // Selected encode is running, enable display of log and
961             // show the live buffer
962             if (ud->queue_activity_buffer != current)
963             {
964                 gtk_text_view_set_buffer(tv, ud->queue_activity_buffer);
965             }
966         }
967         else
968         {
969             // Selected encode is pending/finished/canceled/failed
970             // use non-live buffer (aka extra) to display log
971             if (ud->extra_activity_buffer != current)
972             {
973                 gtk_text_view_set_buffer(tv, ud->extra_activity_buffer);
974             }
975             log_path = ghb_dict_get_string(uiDict, "ActivityFilename");
976             if (status != GHB_QUEUE_PENDING && log_path != NULL)
977             {
978                 // enable display of log and read log into display buffer
979                 read_log(ud, log_path);
980             }
981             else
982             {
983                 // No log file, encode is pending
984                 // disable display of log
985                 g_free(ud->extra_activity_path);
986                 ud->extra_activity_path = NULL;
987                 gtk_text_buffer_set_text(ud->extra_activity_buffer, "", 0);
988             }
989         }
990     }
991 }
992 
993 void
ghb_queue_selection_init(signal_user_data_t * ud)994 ghb_queue_selection_init(signal_user_data_t * ud)
995 {
996     GtkListBox    * lb;
997     GtkListBoxRow * row;
998 
999     lb            = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
1000     row           = gtk_list_box_get_selected_row(lb);
1001     if (row == NULL)
1002     {
1003         row = gtk_list_box_get_row_at_index(lb, 0);
1004         if (row != NULL)
1005         {
1006             gtk_list_box_select_row(lb, row);
1007         }
1008     }
1009 }
1010 
1011 char *
ghb_subtitle_short_description(const GhbValue * subsource,const GhbValue * subsettings)1012 ghb_subtitle_short_description(const GhbValue *subsource,
1013                                const GhbValue *subsettings)
1014 {
1015     GhbValue *import;
1016     char *desc = NULL;
1017 
1018     import = ghb_dict_get(subsettings, "Import");
1019     if (import != NULL)
1020     {
1021         const gchar * format = "SRT";
1022         const gchar * code;
1023         const gchar * lang;
1024         int           source = IMPORTSRT;
1025         const iso639_lang_t *iso;
1026 
1027         format = ghb_dict_get_string(import, "Format");
1028         lang   = ghb_dict_get_string(import, "Language");
1029         code   = ghb_dict_get_string(import, "Codeset");
1030 
1031         if (format != NULL && !strcasecmp(format, "SSA"))
1032         {
1033             source = IMPORTSSA;
1034         }
1035         iso = lang_lookup(lang);
1036         if (iso != NULL)
1037         {
1038             if (iso->native_name != NULL)
1039                 lang = iso->native_name;
1040             else
1041                 lang = iso->eng_name;
1042         }
1043 
1044         if (source == IMPORTSRT)
1045         {
1046             desc = g_strdup_printf("%s (%s)(%s)", lang, code, format);
1047         }
1048         else
1049         {
1050             desc = g_strdup_printf("%s (%s)", lang, format);
1051         }
1052     }
1053     else if (subsource == NULL)
1054     {
1055         desc = g_strdup(_("Foreign Audio Scan"));
1056     }
1057     else
1058     {
1059         const char * lang = ghb_dict_get_string(subsource, "Language");
1060         desc = g_strdup_printf("%s", lang);
1061     }
1062 
1063     return desc;
1064 }
1065 
1066 void
ghb_queue_progress_set_visible(signal_user_data_t * ud,int index,gboolean visible)1067 ghb_queue_progress_set_visible(signal_user_data_t *ud, int index,
1068                                gboolean visible)
1069 {
1070     GtkListBox    * lb;
1071     GtkListBoxRow * row;
1072     GtkWidget     * progress;
1073 
1074     int count = ghb_array_len(ud->queue);
1075     if (index < 0 || index >= count)
1076     {
1077         // invalid index
1078         return;
1079     }
1080 
1081     lb = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
1082     row = gtk_list_box_get_row_at_index(lb, index);
1083     if (row == NULL)
1084     {
1085         return;
1086     }
1087     progress = find_widget(GTK_WIDGET(row), "queue_item_progress");
1088     gtk_widget_set_visible(progress, visible);
1089 }
1090 
1091 void
ghb_queue_progress_set_fraction(signal_user_data_t * ud,int index,gdouble frac)1092 ghb_queue_progress_set_fraction(signal_user_data_t *ud, int index, gdouble frac)
1093 {
1094     GtkListBox     * lb;
1095     GtkListBoxRow  * row;
1096     GtkProgressBar * progress;
1097 
1098     int count = ghb_array_len(ud->queue);
1099     if (index < 0 || index >= count)
1100     {
1101         // invalid index
1102         return;
1103     }
1104 
1105     lb = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
1106     row = gtk_list_box_get_row_at_index(lb, index);
1107     if (row == NULL)
1108     {
1109         return;
1110     }
1111     progress = GTK_PROGRESS_BAR(find_widget(GTK_WIDGET(row),
1112                                             "queue_item_progress"));
1113     gtk_progress_bar_set_fraction(progress, frac);
1114 }
1115 
1116 void
ghb_queue_update_live_stats(signal_user_data_t * ud,int index,ghb_instance_status_t * status)1117 ghb_queue_update_live_stats(signal_user_data_t * ud, int index, ghb_instance_status_t * status)
1118 {
1119     int count = ghb_array_len(ud->queue);
1120     if (index < 0 || index >= count)
1121     {
1122         // invalid index
1123         return;
1124     }
1125 
1126     GtkListBox    * lb;
1127     GtkListBoxRow * row;
1128 
1129     lb = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
1130     row = gtk_list_box_get_selected_row(lb);
1131     if (row == NULL || index != gtk_list_box_row_get_index(row))
1132     {
1133         return;
1134     }
1135 
1136     GhbValue * queueDict, * uiDict;
1137     queueDict = ghb_array_get(ud->queue, index);
1138     if (queueDict == NULL) // should never happen
1139     {
1140         return;
1141     }
1142     uiDict    = ghb_dict_get(queueDict, "uiSettings");
1143     if (uiDict == NULL) // should never happen
1144     {
1145         return;
1146     }
1147 
1148     GString    * gstr = NULL;
1149     GtkLabel   * label;
1150     struct tm  * tm;
1151     char         date[40] = "";
1152     char       * str;
1153     const char * pass = "";
1154     const char * result = "";
1155     time_t       start, finish, paused, duration;
1156 
1157     start  = ghb_dict_get_int(uiDict, "job_start_time");
1158     finish = time(NULL);
1159     if (status->state & GHB_STATE_WORKING)
1160     {
1161         finish += status->eta_seconds;
1162     }
1163     paused = status->paused / 1000;
1164     if ((status->state & GHB_STATE_WORKING) && status->pass_count > 1)
1165     {
1166         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_pass_label"));
1167         gtk_widget_set_visible(GTK_WIDGET(label), TRUE);
1168         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_pass"));
1169         gtk_widget_set_visible(GTK_WIDGET(label), TRUE);
1170         switch (status->pass_id)
1171         {
1172             case HB_PASS_SUBTITLE:
1173                 pass = _("Foreign Audio Search");
1174                 break;
1175 
1176             case HB_PASS_ENCODE:
1177                 pass = _("Encode");
1178                 break;
1179 
1180             case HB_PASS_ENCODE_1ST:
1181                 pass = _("Encode First Pass (Analysis)");
1182                 break;
1183 
1184             case HB_PASS_ENCODE_2ND:
1185                 pass = _("Encode Second Pass (Final)");
1186                 break;
1187 
1188             default:
1189                 // Should never happen
1190                 pass = _("Error");
1191                 break;
1192         }
1193         gstr = g_string_new(NULL);
1194         g_string_append_printf(gstr, _("pass %d of %d\n%s"), status->pass, status->pass_count, pass);
1195     }
1196     else
1197     {
1198         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_pass_label"));
1199         gtk_widget_set_visible(GTK_WIDGET(label), FALSE);
1200         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_pass"));
1201         gtk_widget_set_visible(GTK_WIDGET(label), FALSE);
1202     }
1203 
1204     if (status->state & GHB_STATE_SCANNING)
1205     {
1206         result = _("Scanning Title");
1207     }
1208     else if (status->state & GHB_STATE_PAUSED)
1209     {
1210         result = _("Encoding Paused");
1211     }
1212     else if (status->state & GHB_STATE_WORKING)
1213     {
1214         result = _("Encoding In Progress");
1215     }
1216     else if (status->state & GHB_STATE_WORKDONE)
1217     {
1218         switch (status->error)
1219         {
1220             case GHB_ERROR_NONE:
1221                 result = _("Completed");
1222                 break;
1223 
1224             case GHB_ERROR_CANCELED:
1225                 result = _("Canceled");
1226                 break;
1227 
1228             case GHB_ERROR_FAIL:
1229                 result = _("Failed");
1230                 break;
1231 
1232             default:
1233                 // Should never happen
1234                 result = _("Unknown");
1235                 break;
1236         }
1237     }
1238 
1239     if (gstr != NULL)
1240     {
1241         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_pass"));
1242         gtk_label_set_text(label, gstr->str);
1243         g_string_free(gstr, TRUE);
1244     }
1245 
1246     tm     = localtime( &start );
1247     strftime(date, 40, "%c", tm);
1248     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_start_time"));
1249     gtk_label_set_text(label, date);
1250 
1251     tm  = localtime( &finish );
1252     strftime(date, 40, "%c", tm);
1253     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_finish_time"));
1254     gtk_label_set_text(label, date);
1255 
1256     int dd = 0, hh, mm, ss;
1257     ghb_break_duration(paused, &hh, &mm, &ss);
1258     if (hh >= 24)
1259     {
1260         dd = hh / 24;
1261         hh = hh - dd * 24;
1262     }
1263     switch (dd)
1264     {
1265         case 0:
1266             str = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
1267             break;
1268         case 1:
1269             str = g_strdup_printf(_("%d Day %02d:%02d:%02d"), dd, hh, mm, ss);
1270             break;
1271         default:
1272             str = g_strdup_printf(_("%d Days %02d:%02d:%02d"), dd, hh, mm, ss);
1273             break;
1274     }
1275     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_paused"));
1276     gtk_label_set_text(label, str);
1277     g_free(str);
1278 
1279     duration = finish - start;
1280     if (duration < 0)
1281     {
1282         duration = 0;
1283     }
1284     dd = 0;
1285     ghb_break_duration(duration, &hh, &mm, &ss);
1286     if (hh >= 24)
1287     {
1288         dd = hh / 24;
1289         hh = hh - dd * 24;
1290     }
1291     switch (dd)
1292     {
1293         case 0:
1294             str = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
1295             break;
1296         case 1:
1297             str = g_strdup_printf(_("%d Day %02d:%02d:%02d"), dd, hh, mm, ss);
1298             break;
1299         default:
1300             str = g_strdup_printf(_("%d Days %02d:%02d:%02d"), dd, hh, mm, ss);
1301             break;
1302     }
1303     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_encode"));
1304     gtk_label_set_text(label, str);
1305     g_free(str);
1306 
1307     const char  * path;
1308     struct stat   stbuf;
1309 
1310     path = ghb_dict_get_string(uiDict, "destination");
1311     if (g_stat(path, &stbuf) == 0)
1312     {
1313         const char * units = _("B");
1314         double size = stbuf.st_size;
1315         if (size > 1024)
1316         {
1317             units = _("KB");
1318             size /= 1024.0;
1319         }
1320         if (size > 1024)
1321         {
1322             units = _("MB");
1323             size /= 1024.0;
1324         }
1325         if (size > 1024)
1326         {
1327             units = _("GB");
1328             size /= 1024.0;
1329         }
1330         str = g_strdup_printf("%.2f %s", size, units);
1331         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_file_size"));
1332         gtk_label_set_text(label, str);
1333         g_free(str);
1334     }
1335     else
1336     {
1337         label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_file_size"));
1338         gtk_label_set_text(label, _("Not Available"));
1339     }
1340 
1341     label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_stats_result"));
1342     gtk_label_set_text(label, result);
1343 }
1344 
1345 void
ghb_queue_update_status_icon(signal_user_data_t * ud,int index)1346 ghb_queue_update_status_icon(signal_user_data_t *ud, int index)
1347 {
1348     int count = ghb_array_len(ud->queue);
1349     if (index < 0 || index >= count)
1350     {
1351         // invalid index
1352         return;
1353     }
1354 
1355     GhbValue * queueDict, * uiDict;
1356     queueDict = ghb_array_get(ud->queue, index);
1357     if (queueDict == NULL) // should never happen
1358     {
1359         return;
1360     }
1361     uiDict    = ghb_dict_get(queueDict, "uiSettings");
1362     if (uiDict == NULL) // should never happen
1363     {
1364         return;
1365     }
1366 
1367     int status = ghb_dict_get_int(uiDict, "job_status");
1368 
1369     // Now update the UI
1370     const char * icon_name;
1371     switch (status)
1372     {
1373         case GHB_QUEUE_RUNNING:
1374              icon_name = "hb-start";
1375             break;
1376         case GHB_QUEUE_PENDING:
1377              icon_name = "hb-source";
1378             break;
1379         case GHB_QUEUE_FAIL:
1380         case GHB_QUEUE_CANCELED:
1381              icon_name = "hb-stop";
1382             break;
1383         case GHB_QUEUE_DONE:
1384              icon_name = "hb-complete";
1385             break;
1386         default:
1387              icon_name = "hb-source";
1388             break;
1389     }
1390     GtkListBox    * lb;
1391     GtkListBoxRow * row;
1392     GtkImage      * status_icon;
1393 
1394     lb = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
1395     row = gtk_list_box_get_row_at_index(lb, index);
1396     if (row == NULL) // should never happen
1397     {
1398         return;
1399     }
1400     status_icon = GTK_IMAGE(find_widget(GTK_WIDGET(row), "queue_item_status"));
1401     if (status_icon == NULL) // should never happen
1402     {
1403         return;
1404     }
1405     ghb_image_set_from_icon_name(status_icon, icon_name,
1406                                  GHB_ICON_SIZE_BUTTON);
1407 }
1408 
1409 void
ghb_queue_update_status(signal_user_data_t * ud,int index,int status)1410 ghb_queue_update_status(signal_user_data_t *ud, int index, int status)
1411 {
1412     int count = ghb_array_len(ud->queue);
1413     if (index < 0 || index >= count)
1414     {
1415         // invalid index
1416         return;
1417     }
1418 
1419     GhbValue * queueDict, * uiDict;
1420     queueDict = ghb_array_get(ud->queue, index);
1421     if (queueDict == NULL) // should never happen
1422     {
1423         return;
1424     }
1425     uiDict    = ghb_dict_get(queueDict, "uiSettings");
1426     if (uiDict == NULL) // should never happen
1427     {
1428         return;
1429     }
1430 
1431     if (ghb_dict_get_int(uiDict, "job_status") == GHB_QUEUE_RUNNING)
1432     {
1433         return; // Never change the status of currently running jobs
1434     }
1435 
1436     if (status == GHB_QUEUE_PENDING)
1437     {
1438         ghb_queue_progress_set_visible(ud, index, FALSE);
1439     }
1440     ghb_dict_set_int(uiDict, "job_status", status);
1441     ghb_queue_update_status_icon(ud, index);
1442 }
1443 
1444 void
ghb_update_all_status(signal_user_data_t * ud,int status)1445 ghb_update_all_status(signal_user_data_t *ud, int status)
1446 {
1447     int count, ii;
1448 
1449     count = ghb_array_len(ud->queue);
1450     for (ii = 0; ii < count; ii++)
1451     {
1452         ghb_queue_update_status(ud, ii, status);
1453     }
1454 }
1455 
1456 static void
save_queue_file(signal_user_data_t * ud)1457 save_queue_file(signal_user_data_t *ud)
1458 {
1459     int ii, count;
1460     GhbValue *queue = ghb_value_dup(ud->queue);
1461 
1462     count = ghb_array_len(queue);
1463     for (ii = 0; ii < count; ii++)
1464     {
1465         GhbValue *queueDict, *uiDict;
1466 
1467         queueDict = ghb_array_get(ud->queue, ii);
1468         uiDict = ghb_dict_get(queueDict, "uiSettings");
1469         if (uiDict == NULL)
1470             continue;
1471         ghb_dict_set_int(uiDict, "job_status", GHB_QUEUE_PENDING);
1472     }
1473 
1474     GtkWidget *dialog;
1475     GtkWindow *hb_window;
1476 
1477     hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
1478     dialog = gtk_file_chooser_dialog_new("Queue Destination",
1479                       hb_window,
1480                       GTK_FILE_CHOOSER_ACTION_SAVE,
1481                       GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1482                       GHB_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1483                       NULL);
1484     gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "queue.json");
1485     if (gtk_dialog_run(GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
1486     {
1487         ghb_value_free(&queue);
1488         gtk_widget_destroy(dialog);
1489         return;
1490     }
1491 
1492     char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog));
1493     gtk_widget_destroy(dialog);
1494 
1495     ghb_write_settings_file(filename, queue);
1496     g_free (filename);
1497     ghb_value_free(&queue);
1498 }
1499 
1500 static void
add_to_queue_list(signal_user_data_t * ud,GhbValue * queueDict)1501 add_to_queue_list(signal_user_data_t *ud, GhbValue *queueDict)
1502 {
1503     GtkListBox * lb;
1504     GtkWidget  * row;
1505     GtkBox     * hbox, * vbox, * dbox;
1506     GtkWidget  * ebox;
1507     GtkWidget  * status_icon;
1508     GtkWidget  * dest_label;
1509     GtkWidget  * delete_button;
1510     GtkWidget  * progress;
1511     GhbValue   * uiDict;
1512     const char * dest;
1513     gchar      * basename;
1514 
1515     lb     = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
1516     uiDict = ghb_dict_get(queueDict, "uiSettings");
1517 
1518     row  = gtk_list_box_row_new();
1519     vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 6));
1520     gtk_widget_set_margin_start(GTK_WIDGET(vbox), 12);
1521     hbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6));
1522     dbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6));
1523 #if GTK_CHECK_VERSION(3, 90, 0)
1524     ebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1525 #else
1526     ebox = gtk_event_box_new();
1527 #endif
1528 
1529     status_icon = ghb_image_new_from_icon_name("hb-source",
1530                                                GHB_ICON_SIZE_BUTTON);
1531 
1532     gtk_widget_set_name(status_icon, "queue_item_status");
1533     gtk_image_set_pixel_size(GTK_IMAGE(status_icon), 16);
1534     gtk_widget_set_hexpand(status_icon, FALSE);
1535 
1536     dest       = ghb_dict_get_string(uiDict, "destination");
1537     basename   = g_path_get_basename(dest);
1538     dest_label = gtk_label_new(basename);
1539     g_free(basename);
1540     gtk_widget_set_name(dest_label, "queue_item_dest");
1541     gtk_widget_set_hexpand(dest_label, TRUE);
1542     gtk_widget_set_halign(dest_label, GTK_ALIGN_FILL);
1543     gtk_label_set_justify(GTK_LABEL(dest_label), GTK_JUSTIFY_LEFT);
1544     gtk_label_set_xalign(GTK_LABEL(dest_label), 0.0);
1545     gtk_label_set_width_chars(GTK_LABEL(dest_label), 50);
1546     gtk_label_set_ellipsize(GTK_LABEL(dest_label), PANGO_ELLIPSIZE_END);
1547 
1548     delete_button = ghb_button_new_from_icon_name("hb-remove");
1549     gtk_button_set_relief(GTK_BUTTON(delete_button), GTK_RELIEF_NONE);
1550     g_signal_connect(delete_button, "clicked",
1551                      (GCallback)queue_remove_clicked_cb, ud);
1552     gtk_widget_set_hexpand(delete_button, FALSE);
1553 
1554     progress = gtk_progress_bar_new();
1555     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.0);
1556     gtk_widget_set_name(progress, "queue_item_progress");
1557     gtk_widget_set_visible(progress, FALSE);
1558 
1559     ghb_box_append_child(dbox, GTK_WIDGET(status_icon));
1560     ghb_box_append_child(dbox, GTK_WIDGET(dest_label));
1561     gtk_container_add(GTK_CONTAINER(ebox), GTK_WIDGET(dbox));
1562     ghb_box_append_child(hbox, GTK_WIDGET(ebox));
1563     ghb_box_append_child(hbox, GTK_WIDGET(delete_button));
1564 
1565     ghb_box_append_child(vbox, GTK_WIDGET(hbox));
1566     ghb_box_append_child(vbox, GTK_WIDGET(progress));
1567     gtk_container_add(GTK_CONTAINER(row), GTK_WIDGET(vbox));
1568 
1569     gtk_widget_show(GTK_WIDGET(row));
1570     gtk_widget_show(GTK_WIDGET(vbox));
1571     gtk_widget_show(GTK_WIDGET(hbox));
1572     gtk_widget_show(GTK_WIDGET(dbox));
1573     gtk_widget_show(GTK_WIDGET(ebox));
1574     gtk_widget_show(status_icon);
1575     gtk_widget_show(dest_label);
1576     gtk_widget_show(delete_button);
1577     gtk_list_box_insert(lb, row, -1);
1578 
1579     // style class for CSS settings
1580     gtk_style_context_add_class(gtk_widget_get_style_context(row), "row");
1581     // set row as a source for drag & drop
1582 #if GTK_CHECK_VERSION(3, 90, 0)
1583     GdkContentFormats * targets;
1584 
1585     targets = gdk_content_formats_new(queue_drag_entries,
1586                                       G_N_ELEMENTS(queue_drag_entries));
1587     gtk_drag_source_set(ebox, GDK_BUTTON1_MASK, targets, GDK_ACTION_MOVE);
1588     gdk_content_formats_unref(targets);
1589 #else
1590     gtk_drag_source_set(ebox, GDK_BUTTON1_MASK, queue_drag_entries, 1,
1591                         GDK_ACTION_MOVE);
1592 #endif
1593     g_signal_connect(ebox, "drag-begin", G_CALLBACK(queue_drag_begin_cb), NULL);
1594     g_signal_connect(ebox, "drag-end", G_CALLBACK(queue_drag_end_cb), NULL);
1595     g_signal_connect(ebox, "drag-data-get",
1596                     G_CALLBACK(queue_drag_data_get_cb), NULL);
1597 
1598 #if GTK_CHECK_VERSION(3, 90, 0)
1599     // connect key event controller to capture "delete" key press on row
1600     GtkEventController * econ = gtk_event_controller_key_new();
1601     gtk_widget_add_controller(row, econ);
1602     g_signal_connect(econ, "key-pressed", G_CALLBACK(queue_row_key_cb), ud);
1603 #endif
1604 }
1605 
1606 static void
open_queue_file(signal_user_data_t * ud)1607 open_queue_file(signal_user_data_t *ud)
1608 {
1609     GtkWidget *dialog;
1610     GtkWindow *hb_window;
1611 
1612     hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
1613     dialog = gtk_file_chooser_dialog_new("Queue Destination",
1614                       hb_window,
1615                       GTK_FILE_CHOOSER_ACTION_OPEN,
1616                       GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1617                       GHB_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1618                       NULL);
1619 
1620     // Add filters
1621     GtkFileFilter *filter;
1622     GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
1623     filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "QueueFilterAll"));
1624     gtk_file_filter_set_name(filter, _("All"));
1625     gtk_file_filter_add_pattern(filter, "*");
1626     gtk_file_chooser_add_filter(chooser, filter);
1627     filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "QueueFilterJSON"));
1628     gtk_file_filter_set_name(filter, "JSON");
1629     gtk_file_filter_add_pattern(filter, "*.JSON");
1630     gtk_file_filter_add_pattern(filter, "*.json");
1631     gtk_file_chooser_add_filter(chooser, filter);
1632 
1633     if (gtk_dialog_run(GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
1634     {
1635         gtk_widget_destroy(dialog);
1636         return;
1637     }
1638 
1639     GhbValue *queue;
1640     char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1641     gtk_widget_destroy(dialog);
1642 
1643     queue = ghb_read_settings_file(filename);
1644     if (queue != NULL)
1645     {
1646         int ii, count;
1647         count = ghb_array_len(queue);
1648         for (ii = 0; ii < count; ii++)
1649         {
1650             GhbValue *queueDict, *uiDict;
1651 
1652             queueDict = ghb_array_get(queue, ii);
1653             uiDict = ghb_dict_get(queueDict, "uiSettings");
1654             ghb_value_incref(queueDict);
1655             ghb_dict_set_int(uiDict, "job_status", GHB_QUEUE_PENDING);
1656             ghb_dict_set_int(uiDict, "job_unique_id", 0);
1657 
1658             if (ud->queue == NULL)
1659                 ud->queue = ghb_array_new();
1660             ghb_array_append(ud->queue, queueDict);
1661             add_to_queue_list(ud, queueDict);
1662         }
1663         ghb_queue_buttons_grey(ud);
1664         ghb_save_queue(ud->queue);
1665         ghb_value_free(&queue);
1666     }
1667     g_free (filename);
1668 }
1669 
1670 gint64
ghb_dest_free_space(GhbValue * settings)1671 ghb_dest_free_space(GhbValue *settings)
1672 {
1673     GFile       *gfile;
1674     GFileInfo   *info;
1675     guint64      size = -1;
1676     const gchar *dest     = ghb_dict_get_string(settings, "destination");
1677     gchar       *destdir  = g_path_get_dirname(dest);
1678     gchar       *resolved = ghb_resolve_symlink(destdir);
1679 
1680     gfile = g_file_new_for_path(resolved);
1681     info  = g_file_query_filesystem_info(gfile,
1682                                 G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, NULL);
1683     if (info != NULL)
1684     {
1685         if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE))
1686         {
1687             size = g_file_info_get_attribute_uint64(info,
1688                                     G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
1689         }
1690         g_object_unref(info);
1691     }
1692     g_object_unref(gfile);
1693     g_free(resolved);
1694     g_free(destdir);
1695 
1696     return size;
1697 }
1698 
1699 gint
ghb_find_queue_job(GhbValue * queue,gint unique_id,GhbValue ** job)1700 ghb_find_queue_job(GhbValue *queue, gint unique_id, GhbValue **job)
1701 {
1702     GhbValue *queueDict, *uiDict;
1703     gint ii, count;
1704     gint job_unique_id;
1705 
1706     if (job != NULL)
1707     {
1708         *job = NULL;
1709     }
1710     if (unique_id == 0)  // Invalid Id
1711         return -1;
1712 
1713     count = ghb_array_len(queue);
1714     for (ii = 0; ii < count; ii++)
1715     {
1716         queueDict = ghb_array_get(queue, ii);
1717         uiDict = ghb_dict_get(queueDict, "uiSettings");
1718         job_unique_id = ghb_dict_get_int(uiDict, "job_unique_id");
1719         if (job_unique_id == unique_id)
1720         {
1721             if (job != NULL)
1722             {
1723                 *job = queueDict;
1724             }
1725             return ii;
1726         }
1727     }
1728     return -1;
1729 }
1730 
1731 void
ghb_low_disk_check(signal_user_data_t * ud)1732 ghb_low_disk_check(signal_user_data_t *ud)
1733 {
1734     GtkWindow       *hb_window;
1735     GtkWidget       *dialog;
1736     GtkResponseType  response;
1737     ghb_status_t     status;
1738     const char      *paused_msg = "";
1739     const char      *dest;
1740     gint64           free_size;
1741     gint64           free_limit;
1742     GhbValue        *qDict;
1743     GhbValue        *settings;
1744 
1745     if (ghb_dict_get_bool(ud->globals, "SkipDiskFreeCheck") ||
1746         !ghb_dict_get_bool(ud->prefs, "DiskFreeCheck"))
1747     {
1748         return;
1749     }
1750 
1751     ghb_get_status(&status);
1752     if (status.queue.unique_id <= 0)
1753     {
1754         // No current job
1755         return;
1756     }
1757     ghb_find_queue_job(ud->queue, status.queue.unique_id, &qDict);
1758     if (qDict == NULL)
1759     {
1760         // Failed to find queue setting!
1761         return;
1762     }
1763     settings = ghb_dict_get(qDict, "uiSettings");
1764     free_size = ghb_dest_free_space(settings);
1765     if (free_size < 0)
1766     {
1767         // Failed to read free space
1768         return;
1769     }
1770     // limit is in MB
1771     free_limit = ghb_dict_get_int(ud->prefs, "DiskFreeLimit") * 1024 * 1024;
1772     if (free_size > free_limit)
1773     {
1774         return;
1775     }
1776 
1777     if ((status.queue.state & GHB_STATE_WORKING) &&
1778         !(status.queue.state & GHB_STATE_PAUSED))
1779     {
1780         paused_msg = "Encoding has been paused.\n\n";
1781         ghb_pause_queue();
1782     }
1783     dest      = ghb_dict_get_string(settings, "destination");
1784     hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
1785     dialog    = gtk_message_dialog_new(hb_window, GTK_DIALOG_MODAL,
1786             GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
1787             _("%sThe destination filesystem is almost full: %"PRId64" MB free.\n"
1788               "Destination: %s\n"
1789               "Encode may be incomplete if you proceed.\n"),
1790             paused_msg, free_size / (1024 * 1024), dest);
1791     gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1792                            _("Resume, I've fixed the problem"), 1,
1793                            _("Resume, Don't tell me again"), 2,
1794                            _("Cancel Current and Stop"), 3,
1795                            NULL);
1796     response = gtk_dialog_run(GTK_DIALOG(dialog));
1797     gtk_widget_destroy(dialog);
1798     switch ((int)response)
1799     {
1800         case 1:
1801             ghb_resume_queue();
1802             break;
1803         case 2:
1804             ghb_dict_set_bool(ud->globals, "SkipDiskFreeCheck", TRUE);
1805             ghb_resume_queue();
1806             break;
1807         case 3:
1808             ghb_stop_queue();
1809             ud->cancel_encode = GHB_CANCEL_ALL;
1810             break;
1811         default:
1812             ghb_resume_queue();
1813             break;
1814     }
1815 }
1816 
1817 static gboolean
validate_settings(signal_user_data_t * ud,GhbValue * settings,gint batch)1818 validate_settings(signal_user_data_t *ud, GhbValue *settings, gint batch)
1819 {
1820     // Check to see if the dest file exists or is
1821     // already in the queue
1822     gchar *message;
1823     const gchar *dest;
1824     gint count, ii;
1825     gint title_id, titleindex;
1826     const hb_title_t *title;
1827     GtkWindow *hb_window;
1828 
1829     hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
1830 
1831     title_id = ghb_dict_get_int(settings, "title");
1832     title = ghb_lookup_title(title_id, &titleindex);
1833     if (title == NULL) return FALSE;
1834     dest = ghb_dict_get_string(settings, "destination");
1835     count = ghb_array_len(ud->queue);
1836     for (ii = 0; ii < count; ii++)
1837     {
1838         GhbValue *queueDict, *uiDict;
1839         const gchar *filename;
1840 
1841         queueDict = ghb_array_get(ud->queue, ii);
1842         uiDict = ghb_dict_get(queueDict, "uiSettings");
1843         filename = ghb_dict_get_string(uiDict, "destination");
1844         if (strcmp(dest, filename) == 0)
1845         {
1846             message = g_strdup_printf(
1847                         _("Destination: %s\n\n"
1848                         "Another queued job has specified the same destination.\n"
1849                         "Do you want to overwrite?"),
1850                         dest);
1851             if (!ghb_message_dialog(hb_window, GTK_MESSAGE_QUESTION,
1852                                     message, _("Cancel"), _("Overwrite")))
1853             {
1854                 g_free(message);
1855                 return FALSE;
1856             }
1857             g_free(message);
1858             break;
1859         }
1860     }
1861     gchar *destdir = g_path_get_dirname(dest);
1862     if (!g_file_test(destdir, G_FILE_TEST_IS_DIR))
1863     {
1864         message = g_strdup_printf(
1865                     _("Destination: %s\n\n"
1866                     "This is not a valid directory."),
1867                     destdir);
1868         ghb_message_dialog(hb_window, GTK_MESSAGE_ERROR,
1869                            message, _("Cancel"), NULL);
1870         g_free(message);
1871         g_free(destdir);
1872         return FALSE;
1873     }
1874 #if !defined(_WIN32)
1875     // This doesn't work properly on windows
1876     if (g_access(destdir, R_OK|W_OK) != 0)
1877     {
1878         message = g_strdup_printf(
1879                     _("Destination: %s\n\n"
1880                     "Can not read or write the directory."),
1881                     destdir);
1882         ghb_message_dialog(hb_window, GTK_MESSAGE_ERROR,
1883                            message, _("Cancel"), NULL);
1884         g_free(message);
1885         g_free(destdir);
1886         return FALSE;
1887     }
1888 #endif
1889     g_free(destdir);
1890     if (g_file_test(dest, G_FILE_TEST_EXISTS))
1891     {
1892         message = g_strdup_printf(
1893                     _("Destination: %s\n\n"
1894                     "File already exists.\n"
1895                     "Do you want to overwrite?"),
1896                     dest);
1897         if (!ghb_message_dialog(hb_window, GTK_MESSAGE_QUESTION,
1898                                 message, _("Cancel"), _("Overwrite")))
1899         {
1900             g_free(message);
1901             return FALSE;
1902         }
1903         g_free(message);
1904         g_unlink(dest);
1905     }
1906     // Validate audio settings
1907     if (!ghb_validate_audio(settings, hb_window))
1908     {
1909         return FALSE;
1910     }
1911     // Validate audio settings
1912     if (!ghb_validate_subtitles(settings, hb_window))
1913     {
1914         return FALSE;
1915     }
1916     // Validate video settings
1917     if (!ghb_validate_video(settings, hb_window))
1918     {
1919         return FALSE;
1920     }
1921     // Validate filter settings
1922     if (!ghb_validate_filters(settings, hb_window))
1923     {
1924         return FALSE;
1925     }
1926     return TRUE;
1927 }
1928 
ghb_finalize_job(GhbValue * settings)1929 void ghb_finalize_job(GhbValue *settings)
1930 {
1931     GhbValue *preset, *job;
1932 
1933     preset = ghb_settings_to_preset(settings);
1934     job    = ghb_dict_get(settings, "Job");
1935 
1936     // Add scale filter since the above does not
1937     GhbValue *filter_list, *filter_dict;
1938     int width, height, crop[4];
1939 
1940     filter_list = ghb_get_job_filter_list(settings);
1941     width = ghb_dict_get_int(settings, "scale_width");
1942     height = ghb_dict_get_int(settings, "scale_height");
1943 
1944     crop[0] = ghb_dict_get_int(settings, "PictureTopCrop");
1945     crop[1] = ghb_dict_get_int(settings, "PictureBottomCrop");
1946     crop[2] = ghb_dict_get_int(settings, "PictureLeftCrop");
1947     crop[3] = ghb_dict_get_int(settings, "PictureRightCrop");
1948 
1949     hb_dict_t * dict = ghb_dict_new();
1950     ghb_dict_set_int(dict, "width", width);
1951     ghb_dict_set_int(dict, "height", height);
1952     ghb_dict_set_int(dict, "crop-top", crop[0]);
1953     ghb_dict_set_int(dict, "crop-bottom", crop[1]);
1954     ghb_dict_set_int(dict, "crop-left", crop[2]);
1955     ghb_dict_set_int(dict, "crop-right", crop[3]);
1956 
1957     filter_dict = ghb_dict_new();
1958     ghb_dict_set_int(filter_dict, "ID", HB_FILTER_CROP_SCALE);
1959     ghb_dict_set(filter_dict, "Settings", dict);
1960     hb_add_filter2(filter_list, filter_dict);
1961 
1962     // Apply selected preset settings
1963     hb_preset_apply_mux(preset, job);
1964     hb_preset_apply_video(preset, job);
1965     hb_preset_apply_filters(preset, job);
1966 
1967     ghb_value_free(&preset);
1968 }
1969 
1970 static gboolean
queue_add(signal_user_data_t * ud,GhbValue * settings,gint batch)1971 queue_add(signal_user_data_t *ud, GhbValue *settings, gint batch)
1972 {
1973     // Add settings to the queue
1974     if (!validate_settings(ud, settings, batch))
1975     {
1976         return FALSE;
1977     }
1978 
1979     if (ud->queue == NULL)
1980         ud->queue = ghb_array_new();
1981 
1982     ghb_finalize_job(settings);
1983 
1984     GhbValue *titleDict  = ghb_get_title_settings(settings);
1985     GhbValue *jobDict    = ghb_get_job_settings(settings);
1986     GhbValue *uiDict     = ghb_value_dup(settings);
1987 
1988     ghb_dict_remove(uiDict, "Job");
1989     ghb_dict_remove(uiDict, "Title");
1990 
1991     GhbValue *queueDict  = ghb_dict_new();
1992     ghb_dict_set(queueDict, "uiSettings", uiDict);
1993     ghb_dict_set(queueDict, "Job", ghb_value_dup(jobDict));
1994     ghb_dict_set(queueDict, "Title", ghb_value_dup(titleDict));
1995 
1996     // Copy current prefs into settings
1997     // The job should run with the preferences that existed
1998     // when the job was added to the queue.
1999     ghb_dict_set(uiDict, "Preferences", ghb_value_dup(ud->prefs));
2000 
2001     // Make a copy of current settings to be used for the new job
2002     ghb_dict_set_int(uiDict, "job_status", GHB_QUEUE_PENDING);
2003     ghb_dict_set_int(uiDict, "job_unique_id", 0);
2004 
2005     ghb_array_append(ud->queue, queueDict);
2006     add_to_queue_list(ud, queueDict);
2007     ghb_save_queue(ud->queue);
2008     ghb_update_pending(ud);
2009     ghb_queue_buttons_grey(ud);
2010 
2011     return TRUE;
2012 }
2013 
2014 static gboolean
title_multiple_can_select(GhbValue * settings_array,int index)2015 title_multiple_can_select(GhbValue *settings_array, int index)
2016 {
2017     gint count, ii;
2018     GhbValue *settings, *gdest;
2019     const char *dest;
2020 
2021     settings = ghb_array_get(settings_array, index);
2022     gdest = ghb_dict_get_value(settings, "destination");
2023     dest = ghb_value_get_string(gdest);
2024     if (dest == NULL)
2025         return FALSE;
2026 
2027     count = ghb_array_len(settings_array);
2028     count = count < index ? count : index;
2029     for (ii = 0; ii < count; ii++)
2030     {
2031         const char *tmp;
2032 
2033         settings = ghb_array_get(settings_array, ii);
2034         gdest = ghb_dict_get_value(settings, "destination");
2035         tmp = ghb_value_get_string(gdest);
2036         if (tmp != NULL && !strncmp(dest, tmp, PATH_MAX))
2037             return FALSE;
2038     }
2039     return TRUE;
2040 }
2041 
2042 static PangoAttrList *default_title_attrs;
2043 
2044 static void
title_add_multiple_set_sensitive(GtkWidget * row,gboolean sensitive)2045 title_add_multiple_set_sensitive(GtkWidget *row, gboolean sensitive)
2046 {
2047     GtkWidget *widget;
2048     widget = find_widget(row, "title_selected");
2049     gtk_widget_set_sensitive(widget, sensitive);
2050 
2051     widget = find_widget(row, "title_label");
2052     if (!sensitive)
2053     {
2054         PangoAttrList *pal;
2055         PangoAttribute *bg;
2056         bg = pango_attr_background_new(0xFFFF, 0xFFFF, 0xA000);
2057         pal = pango_attr_list_new();
2058         pango_attr_list_insert(pal, bg);
2059         gtk_label_set_attributes(GTK_LABEL(widget), pal);
2060         gtk_widget_set_has_tooltip(widget, TRUE);
2061     }
2062     else
2063     {
2064         gtk_label_set_attributes(GTK_LABEL(widget), default_title_attrs);
2065         gtk_widget_set_has_tooltip(widget, FALSE);
2066     }
2067 }
2068 
2069 static gboolean
title_add_multiple_are_conflicts(signal_user_data_t * ud)2070 title_add_multiple_are_conflicts(signal_user_data_t *ud)
2071 {
2072     GtkListBox *list;
2073     GtkWidget *row;
2074     gint count, ii;
2075 
2076     list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list"));
2077     count = ghb_array_len(ud->settings_array);
2078     for (ii = 0; ii < count; ii++)
2079     {
2080         row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii));
2081         if (!title_multiple_can_select(ud->settings_array, ii))
2082         {
2083             title_add_multiple_set_sensitive(GTK_WIDGET(row), FALSE);
2084             return TRUE;
2085         }
2086         title_add_multiple_set_sensitive(GTK_WIDGET(row), TRUE);
2087     }
2088     return FALSE;
2089 }
2090 
2091 static void
title_add_multiple_set_conflict_label(signal_user_data_t * ud,gboolean are_conflicts)2092 title_add_multiple_set_conflict_label(
2093     signal_user_data_t *ud,
2094     gboolean are_conflicts)
2095 {
2096     const gchar *msg;
2097     static gboolean conflict_showing = FALSE;
2098     GtkMessageType msg_type;
2099 
2100     if (are_conflicts)
2101     {
2102         msg =
2103             "<span foreground='black' weight='bold'>"
2104             "Duplicate destination files detected.\n"
2105             "Duplicates will not be added to the queue."
2106             "</span>";
2107             msg_type = GTK_MESSAGE_WARNING;
2108     }
2109     else
2110     {
2111         msg =
2112             "Destination files OK.  No duplicates detected.";
2113             msg_type = GTK_MESSAGE_INFO;
2114     }
2115     if (are_conflicts ^ conflict_showing)
2116     {
2117         GtkInfoBar *info;
2118         GtkContainer *content_area;
2119         GList *list;
2120         GtkLabel *label;
2121 
2122         info = GTK_INFO_BAR(GHB_WIDGET(ud->builder,
2123                                        "title_add_multiple_infobar"));
2124         content_area = GTK_CONTAINER(gtk_info_bar_get_content_area(info));
2125         list = gtk_container_get_children(content_area);
2126         // Label is first in list
2127         label = GTK_LABEL(list->data);
2128         gtk_label_set_markup(label, msg);
2129         gtk_info_bar_set_message_type(info, msg_type);
2130         conflict_showing = are_conflicts;
2131     }
2132 }
2133 
2134 static void
title_add_multiple_check_conflicts(signal_user_data_t * ud)2135 title_add_multiple_check_conflicts(signal_user_data_t *ud)
2136 {
2137     gint count, ii;
2138     GhbValue *settings;
2139     GtkWidget *row;
2140     GtkListBox *list;
2141     GtkToggleButton *selected;
2142     gboolean can_select;
2143     gboolean are_conflicts = FALSE;
2144 
2145     list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list"));
2146     count = ghb_array_len(ud->settings_array);
2147     for (ii = 0; ii < count; ii++)
2148     {
2149         row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii));
2150         selected = GTK_TOGGLE_BUTTON(find_widget(row, "title_selected"));
2151 
2152         settings = ghb_array_get(ud->settings_array, ii);
2153         can_select = title_multiple_can_select(ud->settings_array, ii);
2154         ghb_dict_set_bool(settings, "title_selected", FALSE);
2155         gtk_toggle_button_set_active(selected, FALSE);
2156         title_add_multiple_set_sensitive(GTK_WIDGET(row), can_select);
2157         are_conflicts |= !can_select;
2158     }
2159     title_add_multiple_set_conflict_label(ud, are_conflicts);
2160 }
2161 
2162 static void
add_multiple_titles(signal_user_data_t * ud)2163 add_multiple_titles(signal_user_data_t *ud)
2164 {
2165     gint count, ii;
2166 
2167     count = ghb_array_len(ud->settings_array);
2168     for (ii = 0; ii < count; ii++)
2169     {
2170         GhbValue *settings;
2171 
2172         settings = ghb_array_get(ud->settings_array, ii);
2173         if (ghb_dict_get_bool(settings, "title_selected"))
2174         {
2175             queue_add(ud, settings, 1);
2176         }
2177     }
2178     ghb_queue_selection_init(ud);
2179 }
2180 
2181 static GtkListBoxRow*
list_box_get_row(GtkWidget * widget)2182 list_box_get_row(GtkWidget *widget)
2183 {
2184     while (widget != NULL && G_OBJECT_TYPE(widget) != GTK_TYPE_LIST_BOX_ROW)
2185     {
2186         widget = gtk_widget_get_parent(widget);
2187     }
2188     return GTK_LIST_BOX_ROW(widget);
2189 }
2190 
title_create_row(signal_user_data_t * ud)2191 GtkWidget * title_create_row(signal_user_data_t *ud)
2192 {
2193     GtkBox *hbox, *vbox_dest;
2194     GtkCheckButton *selected;
2195     GtkLabel *title;
2196     GtkEntry *dest_file;
2197     GtkFileChooserButton *dest_dir;
2198 
2199     hbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
2200     gtk_box_set_spacing(hbox, 6);
2201     gtk_widget_show(GTK_WIDGET(hbox));
2202 
2203     // Select checkbox
2204     selected = GTK_CHECK_BUTTON(gtk_check_button_new());
2205     gtk_widget_set_tooltip_markup(GTK_WIDGET(selected),
2206       _("Select this title for adding to the queue.\n"));
2207     gtk_widget_set_valign(GTK_WIDGET(selected), GTK_ALIGN_CENTER);
2208     gtk_widget_set_name(GTK_WIDGET(selected), "title_selected");
2209     gtk_widget_show(GTK_WIDGET(selected));
2210     g_signal_connect(selected, "toggled", (GCallback)title_selected_cb, ud);
2211     ghb_box_append_child(hbox, GTK_WIDGET(selected));
2212 
2213     // Title label
2214     title = GTK_LABEL(gtk_label_new(_("No Title")));
2215     gtk_label_set_width_chars(title, 12);
2216     gtk_widget_set_halign(GTK_WIDGET(title), GTK_ALIGN_START);
2217     gtk_widget_set_valign(GTK_WIDGET(title), GTK_ALIGN_CENTER);
2218     gtk_widget_set_name(GTK_WIDGET(title), "title_label");
2219     gtk_widget_show(GTK_WIDGET(title));
2220     ghb_box_append_child(hbox, GTK_WIDGET(title));
2221 
2222     default_title_attrs = gtk_label_get_attributes(title);
2223     gtk_widget_set_tooltip_text(GTK_WIDGET(title),
2224         _("There is another title with the same destination file name.\n"
2225         "This title will not be added to the queue unless you change\n"
2226         "the output file name.\n"));
2227     gtk_widget_set_has_tooltip(GTK_WIDGET(title), FALSE);
2228 
2229     // Destination entry and file chooser
2230     vbox_dest = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
2231     gtk_widget_set_hexpand(GTK_WIDGET(vbox_dest), TRUE);
2232     gtk_widget_set_halign(GTK_WIDGET(vbox_dest), GTK_ALIGN_FILL);
2233     //gtk_widget_set_hexpand(GTK_WIDGET(vbox_dest), TRUE);
2234     dest_file = GTK_ENTRY(gtk_entry_new());
2235     ghb_entry_set_width_chars(dest_file, 40);
2236     gtk_widget_set_name(GTK_WIDGET(dest_file), "title_file");
2237     //gtk_widget_set_hexpand(GTK_WIDGET(dest_file), TRUE);
2238     gtk_widget_show(GTK_WIDGET(dest_file));
2239     g_signal_connect(dest_file, "changed", (GCallback)title_dest_file_cb, ud);
2240     ghb_box_append_child(vbox_dest, GTK_WIDGET(dest_file));
2241     dest_dir = GTK_FILE_CHOOSER_BUTTON(
2242             gtk_file_chooser_button_new(_("Destination Directory"),
2243                                         GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER));
2244     g_signal_connect(dest_dir, "selection-changed",
2245                     (GCallback)title_dest_dir_cb, ud);
2246     gtk_widget_set_name(GTK_WIDGET(dest_dir), "title_dir");
2247     gtk_widget_set_hexpand(GTK_WIDGET(dest_dir), TRUE);
2248     gtk_widget_show(GTK_WIDGET(dest_dir));
2249     ghb_box_append_child(vbox_dest, GTK_WIDGET(dest_dir));
2250     gtk_widget_show(GTK_WIDGET(vbox_dest));
2251     ghb_box_append_child(hbox, GTK_WIDGET(vbox_dest));
2252 
2253     return GTK_WIDGET(hbox);
2254 }
2255 
2256 static void
ghb_queue_remove_row_internal(signal_user_data_t * ud,int index)2257 ghb_queue_remove_row_internal(signal_user_data_t *ud, int index)
2258 {
2259     GtkListBox    * lb;
2260     GtkListBoxRow * row;
2261     GhbValue      * queueDict, * uiDict;
2262 
2263     if (index < 0 || index >= ghb_array_len(ud->queue))
2264     {
2265         return;
2266     }
2267 
2268     queueDict  = ghb_array_get(ud->queue, index);
2269     uiDict     = ghb_dict_get(queueDict, "uiSettings");
2270     int status = ghb_dict_get_int(uiDict, "job_status");
2271     if (status == GHB_QUEUE_RUNNING)
2272     {
2273         // Ask if wants to stop encode.
2274         if (!ghb_cancel_encode2(ud, NULL))
2275         {
2276             return;
2277         }
2278         int unique_id = ghb_dict_get_int(uiDict, "job_unique_id");
2279         ghb_remove_job(unique_id);
2280     }
2281     ghb_array_remove(ud->queue, index);
2282 
2283     // Update UI
2284     lb  = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
2285     row = gtk_list_box_get_row_at_index(lb, index);
2286     gtk_container_remove(GTK_CONTAINER(lb), GTK_WIDGET(row));
2287 }
2288 
2289 void
ghb_queue_remove_row(signal_user_data_t * ud,int index)2290 ghb_queue_remove_row(signal_user_data_t *ud, int index)
2291 {
2292     ghb_queue_remove_row_internal(ud, index);
2293     ghb_save_queue(ud->queue);
2294     ghb_queue_buttons_grey(ud);
2295 }
2296 
2297 void
ghb_queue_buttons_grey(signal_user_data_t * ud)2298 ghb_queue_buttons_grey(signal_user_data_t *ud)
2299 {
2300     GtkWidget        * widget;
2301     GSimpleAction    * action;
2302     gint               queue_count;
2303     gint               title_id, titleindex;
2304     const hb_title_t * title;
2305     gint               queue_state, scan_state;
2306     gboolean           allow_start, show_stop, allow_add, paused;
2307     GtkListBox       * lb;
2308     GtkListBoxRow    * row;
2309     gint               index, status = GHB_QUEUE_PENDING;
2310 
2311     lb = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
2312     row = gtk_list_box_get_selected_row(lb);
2313 
2314     if (row != NULL)
2315     {
2316         index = gtk_list_box_row_get_index(row);
2317         if (index >= 0 && index < ghb_array_len(ud->queue))
2318         {
2319             GhbValue * queueDict, *uiDict;
2320 
2321             queueDict = ghb_array_get(ud->queue, index);
2322             uiDict    = ghb_dict_get(queueDict, "uiSettings");
2323             status    = ghb_dict_get_int(uiDict, "job_status");
2324         }
2325     }
2326 
2327     queue_count = ghb_array_len(ud->queue);
2328     title_id = ghb_dict_get_int(ud->settings, "title");
2329     title = ghb_lookup_title(title_id, &titleindex);
2330 
2331     queue_state = ghb_get_queue_state();
2332     scan_state = ghb_get_scan_state();
2333 
2334     show_stop   = queue_state &
2335                   (GHB_STATE_WORKING | GHB_STATE_SEARCHING |
2336                    GHB_STATE_SCANNING | GHB_STATE_MUXING | GHB_STATE_PAUSED);
2337     allow_start = !(scan_state & GHB_STATE_SCANNING) &&
2338                     (title != NULL || queue_count > 0);
2339     allow_add   = !(scan_state & GHB_STATE_SCANNING) && title != NULL;
2340 
2341 
2342     paused = queue_state & GHB_STATE_PAUSED;
2343 
2344     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2345                                                         "queue-export"));
2346     g_simple_action_set_enabled(action, !!queue_count);
2347     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2348                                                         "queue-add"));
2349     g_simple_action_set_enabled(action, allow_add);
2350     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2351                                                         "queue-add-all"));
2352     g_simple_action_set_enabled(action, allow_add);
2353     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2354                                                         "queue-start"));
2355     g_simple_action_set_enabled(action, allow_start || show_stop);
2356     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2357                                                         "queue-pause"));
2358     g_simple_action_set_enabled(action, show_stop);
2359 
2360     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2361                                                         "queue-reset"));
2362     g_simple_action_set_enabled(action, row != NULL);
2363 
2364     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2365                                                         "queue-edit"));
2366     g_simple_action_set_enabled(action, row != NULL);
2367 
2368     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2369                                                         "queue-open-source"));
2370     g_simple_action_set_enabled(action, row != NULL);
2371 
2372     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2373                                                         "queue-open-dest"));
2374     g_simple_action_set_enabled(action, row != NULL);
2375 
2376     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2377                                                         "queue-open-log-dir"));
2378     g_simple_action_set_enabled(action, status != GHB_QUEUE_PENDING);
2379 
2380     action = G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(ud->app),
2381                                                         "queue-open-log"));
2382     g_simple_action_set_enabled(action, status != GHB_QUEUE_PENDING);
2383 
2384     widget = GHB_WIDGET (ud->builder, "queue_start");
2385     if (show_stop)
2386     {
2387         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop");
2388         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Stop"));
2389         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Stop Encoding"));
2390     }
2391     else
2392     {
2393         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-start");
2394         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Start"));
2395         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Start Encoding"));
2396     }
2397     widget = GHB_WIDGET (ud->builder, "queue_list_start");
2398     if (show_stop)
2399     {
2400         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop");
2401         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Stop"));
2402         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Stop Encoding"));
2403     }
2404     else
2405     {
2406         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-start");
2407         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Start"));
2408         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Start Encoding"));
2409     }
2410     widget = GHB_WIDGET (ud->builder, "queue_start_menu");
2411     if (show_stop)
2412     {
2413         gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _("S_top Encoding"));
2414         gtk_widget_set_tooltip_text(widget, _("Stop Encoding"));
2415     }
2416     else
2417     {
2418         gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _("_Start Encoding"));
2419         gtk_widget_set_tooltip_text(widget, _("Start Encoding"));
2420     }
2421 
2422     widget = GHB_WIDGET (ud->builder, "queue_pause");
2423     if (paused)
2424     {
2425         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-start");
2426         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Resume"));
2427         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Resume Encoding"));
2428     }
2429     else
2430     {
2431         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-pause");
2432         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Pause"));
2433         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Pause Encoding"));
2434     }
2435     widget = GHB_WIDGET (ud->builder, "queue_list_pause");
2436     if (paused)
2437     {
2438         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-start");
2439         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Resume"));
2440         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Resume Encoding"));
2441     }
2442     else
2443     {
2444         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-pause");
2445         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Pause"));
2446         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Pause Encoding"));
2447     }
2448     widget = GHB_WIDGET (ud->builder, "queue_pause_menu");
2449     if (paused)
2450     {
2451         gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _("_Resume Encoding"));
2452         gtk_widget_set_tooltip_text(widget, _("Resume Encoding"));
2453     }
2454     else
2455     {
2456         gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _("_Pause Encoding"));
2457         gtk_widget_set_tooltip_text(widget, _("Pause Encoding"));
2458     }
2459 }
2460 
2461 gboolean
ghb_reload_queue(signal_user_data_t * ud)2462 ghb_reload_queue(signal_user_data_t *ud)
2463 {
2464     GhbValue *queue;
2465     gint count, ii;
2466     gint pid;
2467     gint status;
2468     GhbValue *queueDict, *uiDict;
2469 
2470     g_debug("ghb_reload_queue");
2471 
2472 find_pid:
2473     pid = ghb_find_pid_file();
2474     if (pid < 0)
2475         goto done;
2476 
2477     queue = ghb_load_old_queue(pid);
2478     ghb_remove_old_queue_file(pid);
2479 
2480     // Look for unfinished entries
2481     count = ghb_array_len(queue);
2482     for (ii = count-1; ii >= 0; ii--)
2483     {
2484         queueDict = ghb_array_get(queue, ii);
2485         uiDict = ghb_dict_get(queueDict, "uiSettings");
2486         if (uiDict == NULL ||
2487             ghb_dict_get_value(uiDict, "job_status") == NULL)
2488         {
2489             ghb_array_remove(queue, ii);
2490             continue;
2491         }
2492         status = ghb_dict_get_int(uiDict, "job_status");
2493         if (status == GHB_QUEUE_DONE || status == GHB_QUEUE_CANCELED)
2494         {
2495             ghb_array_remove(queue, ii);
2496             continue;
2497         }
2498     }
2499     count = ghb_array_len(queue);
2500     if (count == 0)
2501     {
2502         ghb_value_free(&queue);
2503         goto find_pid;
2504     }
2505     else
2506     {
2507         GtkWidget *widget = GHB_WIDGET(ud->builder, "show_queue");
2508         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
2509         ud->queue = queue;
2510         for (ii = 0; ii < count; ii++)
2511         {
2512             queueDict = ghb_array_get(queue, ii);
2513             uiDict = ghb_dict_get(queueDict, "uiSettings");
2514             ghb_dict_set_int(uiDict, "job_unique_id", 0);
2515             ghb_dict_set_int(uiDict, "job_status", GHB_QUEUE_PENDING);
2516             add_to_queue_list(ud, queueDict);
2517         }
2518         ghb_queue_buttons_grey(ud);
2519         ghb_save_queue(ud->queue);
2520         ghb_update_pending(ud);
2521         ghb_queue_selection_init(ud);
2522     }
2523 
2524 done:
2525     ghb_write_pid_file();
2526 
2527     return FALSE;
2528 }
2529 
2530 static gboolean
queue_row_key(GtkListBoxRow * row,guint keyval,signal_user_data_t * ud)2531 queue_row_key(GtkListBoxRow * row, guint keyval, signal_user_data_t * ud)
2532 {
2533     gint            index;
2534 
2535     if (keyval != GDK_KEY_Delete)
2536         return FALSE;
2537 
2538     if (row != NULL)
2539     {
2540         index = gtk_list_box_row_get_index(row);
2541         ghb_queue_remove_row_internal(ud, index);
2542         ghb_save_queue(ud->queue);
2543         return TRUE;
2544     }
2545     return FALSE;
2546 }
2547 
2548 #if GTK_CHECK_VERSION(3, 90, 0)
2549 G_MODULE_EXPORT gboolean
queue_row_key_cb(GtkEventControllerKey * keycon,guint keyval,guint keycode,GdkModifierType state,signal_user_data_t * ud)2550 queue_row_key_cb(
2551     GtkEventControllerKey * keycon,
2552     guint                   keyval,
2553     guint                   keycode,
2554     GdkModifierType         state,
2555     signal_user_data_t    * ud)
2556 {
2557     GtkListBoxRow * row;
2558 
2559     row = GTK_LIST_BOX_ROW(gtk_event_controller_get_widget(
2560                                         GTK_EVENT_CONTROLLER(keycon)));
2561     return queue_row_key(row, keyval, ud);
2562 }
2563 
2564 #else
2565 G_MODULE_EXPORT gboolean
queue_key_press_cb(GtkWidget * widget,GdkEvent * event,signal_user_data_t * ud)2566 queue_key_press_cb(
2567     GtkWidget          * widget,
2568     GdkEvent           * event,
2569     signal_user_data_t * ud)
2570 {
2571     GtkListBoxRow * row;
2572     guint           keyval;
2573 
2574     row = gtk_list_box_get_selected_row(GTK_LIST_BOX(widget));
2575     ghb_event_get_keyval(event, &keyval);
2576     return queue_row_key(row, keyval, ud);
2577 }
2578 #endif
2579 
2580 G_MODULE_EXPORT void
show_queue_action_cb(GSimpleAction * action,GVariant * value,signal_user_data_t * ud)2581 show_queue_action_cb(GSimpleAction *action, GVariant *value,
2582                      signal_user_data_t *ud)
2583 {
2584     GtkWidget * queue_window;
2585     gboolean    state = g_variant_get_boolean(value);
2586 
2587     g_simple_action_set_state(action, value);
2588     queue_window = GHB_WIDGET(ud->builder, "queue_window");
2589     gtk_widget_set_visible(queue_window, state);
2590 }
2591 
2592 G_MODULE_EXPORT gboolean
queue_window_delete_cb(GtkWidget * xwidget,GdkEvent * event,signal_user_data_t * ud)2593 queue_window_delete_cb(
2594     GtkWidget *xwidget,
2595 #if !GTK_CHECK_VERSION(3, 90, 0)
2596     GdkEvent *event,
2597 #endif
2598     signal_user_data_t *ud)
2599 {
2600     gtk_widget_set_visible(xwidget, FALSE);
2601     GtkWidget *widget = GHB_WIDGET (ud->builder, "show_queue");
2602     gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
2603     return TRUE;
2604 }
2605 
2606 GhbValue *ghb_queue_edit_settings = NULL;
2607 
2608 G_MODULE_EXPORT void
queue_edit_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2609 queue_edit_action_cb(GSimpleAction *action, GVariant *param,
2610                      signal_user_data_t *ud)
2611 {
2612     GtkListBox    * lb;
2613     GtkListBoxRow * row;
2614     gint            index, status;
2615     GhbValue      * queueDict, *uiDict;
2616 
2617     lb = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
2618     row = gtk_list_box_get_selected_row(lb);
2619     if (row != NULL)
2620     {
2621         index = gtk_list_box_row_get_index(row);
2622         if (index < 0 || index >= ghb_array_len(ud->queue))
2623             return;
2624 
2625         queueDict = ghb_array_get(ud->queue, index);
2626         uiDict    = ghb_dict_get(queueDict, "uiSettings");
2627         ghb_queue_edit_settings = ghb_value_dup(uiDict);
2628         ghb_dict_set(ghb_queue_edit_settings,
2629                      "Job", ghb_value_dup(ghb_dict_get(queueDict, "Job")));
2630         ghb_dict_set(ghb_queue_edit_settings,
2631                      "Title", ghb_value_dup(ghb_dict_get(queueDict, "Title")));
2632         status = ghb_dict_get_int(uiDict, "job_status");
2633         if (status == GHB_QUEUE_PENDING)
2634         {
2635             // Remove the selected item
2636             gtk_container_remove(GTK_CONTAINER(lb), GTK_WIDGET(row));
2637             // Remove the corresponding item from the queue list
2638             ghb_array_remove(ud->queue, index);
2639             ghb_update_pending(ud);
2640         }
2641         const gchar *source;
2642         source = ghb_dict_get_string(ghb_queue_edit_settings, "source");
2643         ghb_do_scan(ud, source, 0, FALSE);
2644 
2645         GtkWidget *widget = GHB_WIDGET(ud->builder, "show_queue");
2646         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
2647     }
2648 }
2649 
2650 G_MODULE_EXPORT void
queue_export_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2651 queue_export_action_cb(GSimpleAction *action, GVariant *param,
2652                      signal_user_data_t *ud)
2653 {
2654     save_queue_file(ud);
2655 }
2656 
2657 G_MODULE_EXPORT void
queue_import_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2658 queue_import_action_cb(GSimpleAction *action, GVariant *param,
2659                      signal_user_data_t *ud)
2660 {
2661     open_queue_file(ud);
2662 }
2663 
2664 G_MODULE_EXPORT void
queue_open_clicked_cb(GtkWidget * widget,signal_user_data_t * ud)2665 queue_open_clicked_cb(GtkWidget *widget, signal_user_data_t *ud)
2666 {
2667     open_queue_file(ud);
2668 }
2669 
2670 G_MODULE_EXPORT void
queue_open_source_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2671 queue_open_source_action_cb(GSimpleAction *action, GVariant *param,
2672                             signal_user_data_t *ud)
2673 {
2674     GtkListBox    * lb;
2675     GtkListBoxRow * row;
2676     gint            index;
2677     GhbValue      * queueDict, * titleDict;
2678     GString       * str;
2679     const char    * path;
2680     char          * dir, * uri;
2681 
2682     lb  = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
2683     row = gtk_list_box_get_selected_row(lb);
2684     if (row != NULL)
2685     {
2686         index = gtk_list_box_row_get_index(row);
2687         if (index < 0 || index >= ghb_array_len(ud->queue))
2688         {
2689             return;
2690         }
2691         queueDict = ghb_array_get(ud->queue, index);
2692         titleDict = ghb_dict_get(queueDict, "Title");
2693         path      = ghb_dict_get_string(titleDict, "Path");
2694         dir       = g_path_get_dirname(path);
2695         str       = g_string_new(NULL);
2696         g_string_printf(str, "file://%s", dir);
2697         uri       = g_string_free(str, FALSE);
2698         ghb_browse_uri(ud, uri);
2699         g_free(uri);
2700         g_free(dir);
2701     }
2702 }
2703 
2704 G_MODULE_EXPORT void
queue_open_dest_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2705 queue_open_dest_action_cb(GSimpleAction *action, GVariant *param,
2706                           signal_user_data_t *ud)
2707 {
2708     GtkListBox    * lb;
2709     GtkListBoxRow * row;
2710     gint            index;
2711     GhbValue      * queueDict, * uiDict;
2712     GString       * str;
2713     const char    * path;
2714     char          * dir, * uri;
2715 
2716     lb  = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
2717     row = gtk_list_box_get_selected_row(lb);
2718     if (row != NULL)
2719     {
2720         index = gtk_list_box_row_get_index(row);
2721         if (index < 0 || index >= ghb_array_len(ud->queue))
2722         {
2723             return;
2724         }
2725         queueDict = ghb_array_get(ud->queue, index);
2726         uiDict    = ghb_dict_get(queueDict, "uiSettings");
2727         path      = ghb_dict_get_string(uiDict, "destination");
2728         dir       = g_path_get_dirname(path);
2729         str       = g_string_new(NULL);
2730         g_string_printf(str, "file://%s", dir);
2731         uri       = g_string_free(str, FALSE);
2732         ghb_browse_uri(ud, uri);
2733         g_free(uri);
2734         g_free(dir);
2735     }
2736 }
2737 
2738 G_MODULE_EXPORT void
queue_open_log_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2739 queue_open_log_action_cb(GSimpleAction *action, GVariant *param,
2740                          signal_user_data_t *ud)
2741 {
2742     GtkListBox    * lb;
2743     GtkListBoxRow * row;
2744     gint            index;
2745     GhbValue      * queueDict, * uiDict;
2746     GString       * str;
2747     const char    * path;
2748     char          * uri;
2749 
2750     lb  = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
2751     row = gtk_list_box_get_selected_row(lb);
2752     if (row != NULL)
2753     {
2754         index = gtk_list_box_row_get_index(row);
2755         if (index < 0 || index >= ghb_array_len(ud->queue))
2756         {
2757             return;
2758         }
2759         queueDict = ghb_array_get(ud->queue, index);
2760         uiDict    = ghb_dict_get(queueDict, "uiSettings");
2761         path      = ghb_dict_get_string(uiDict, "ActivityFilename");
2762         if (path == NULL)
2763         {
2764             return;
2765         }
2766         str       = g_string_new(NULL);
2767         g_string_printf(str, "file://%s", path);
2768         uri       = g_string_free(str, FALSE);
2769         ghb_browse_uri(ud, uri);
2770         g_free(uri);
2771     }
2772 }
2773 
2774 G_MODULE_EXPORT void
queue_open_log_dir_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2775 queue_open_log_dir_action_cb(GSimpleAction *action, GVariant *param,
2776                              signal_user_data_t *ud)
2777 {
2778     GtkListBox    * lb;
2779     GtkListBoxRow * row;
2780     gint            index;
2781     GhbValue      * queueDict, * uiDict;
2782     GString       * str;
2783     const char    * path;
2784     char          * dir, * uri;
2785 
2786     lb  = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
2787     row = gtk_list_box_get_selected_row(lb);
2788     if (row != NULL)
2789     {
2790         index = gtk_list_box_row_get_index(row);
2791         if (index < 0 || index >= ghb_array_len(ud->queue))
2792         {
2793             return;
2794         }
2795         queueDict = ghb_array_get(ud->queue, index);
2796         uiDict    = ghb_dict_get(queueDict, "uiSettings");
2797         path      = ghb_dict_get_string(uiDict, "ActivityFilename");
2798         if (path == NULL)
2799         {
2800             return;
2801         }
2802         dir       = g_path_get_dirname(path);
2803         str       = g_string_new(NULL);
2804         g_string_printf(str, "file://%s", dir);
2805         uri       = g_string_free(str, FALSE);
2806         ghb_browse_uri(ud, uri);
2807         g_free(uri);
2808         g_free(dir);
2809     }
2810 }
2811 
2812 G_MODULE_EXPORT void
queue_list_selection_changed_cb(GtkListBox * box,GtkListBoxRow * row,signal_user_data_t * ud)2813 queue_list_selection_changed_cb(GtkListBox *box, GtkListBoxRow *row,
2814                                 signal_user_data_t *ud)
2815 {
2816     GhbValue  * queueDict = NULL;
2817     int         index = -1;
2818 
2819     if (row != NULL)
2820     {
2821         index = gtk_list_box_row_get_index(row);
2822     }
2823     if (index >= 0 && index < ghb_array_len(ud->queue))
2824     {
2825         queueDict = ghb_array_get(ud->queue, index);
2826     }
2827     queue_update_summary(queueDict, ud);
2828     queue_update_stats(queueDict, ud);
2829     ghb_queue_select_log(ud);
2830     ghb_queue_buttons_grey(ud);
2831 }
2832 
2833 G_MODULE_EXPORT void
queue_add_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2834 queue_add_action_cb(GSimpleAction *action, GVariant *param,
2835                     signal_user_data_t *ud)
2836 {
2837     queue_add(ud, ud->settings, 0);
2838     ghb_queue_selection_init(ud);
2839     // Validation of settings may have changed audio list
2840     ghb_audio_list_refresh_all(ud);
2841 }
2842 
2843 G_MODULE_EXPORT void
queue_add_all_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2844 queue_add_all_action_cb(GSimpleAction *action, GVariant *param,
2845                         signal_user_data_t *ud)
2846 {
2847     GtkListBox *list;
2848     GtkWidget *row;
2849     gint count, ii;
2850     int max_title_len = 0;
2851     GhbValue * preset = NULL;
2852 
2853     list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list"));
2854 
2855     if (ghb_dict_get_bool(ud->prefs, "SyncTitleSettings"))
2856     {
2857         preset = ghb_settings_to_preset(ud->settings);
2858     }
2859 
2860     // Set up the list of titles
2861     count = ghb_array_len(ud->settings_array);
2862     for (ii = 0; ii < count; ii++)
2863     {
2864         GhbValue *settings;
2865         GtkLabel *label;
2866         GtkEntry *entry;
2867         GtkFileChooser *chooser;
2868         gchar *title_label;
2869         const gchar *dest_dir, *dest_file;
2870         int title_id, titleindex;
2871         const hb_title_t *title;
2872 
2873         row = title_create_row(ud);
2874         label = GTK_LABEL(find_widget(row, "title_label"));
2875         entry = GTK_ENTRY(find_widget(row, "title_file"));
2876         chooser = GTK_FILE_CHOOSER(find_widget(row, "title_dir"));
2877 
2878         settings = ghb_array_get(ud->settings_array, ii);
2879         if (preset != NULL)
2880         {
2881             ghb_preset_to_settings(settings, preset);
2882             ghb_set_title_settings(ud, settings);
2883         }
2884         title_id = ghb_dict_get_int(settings, "title");
2885         title = ghb_lookup_title(title_id, &titleindex);
2886         if (title != NULL)
2887         {
2888             int len;
2889 
2890             title_label = ghb_create_title_label(title);
2891             len = strnlen(title_label, PATH_MAX);
2892             if (len > max_title_len)
2893                 max_title_len = len;
2894 
2895             dest_file = ghb_dict_get_string(settings, "dest_file");
2896             dest_dir = ghb_dict_get_string(settings, "dest_dir");
2897 
2898             gtk_label_set_markup(label, title_label);
2899             ghb_editable_set_text(entry, dest_file);
2900             gtk_file_chooser_set_filename(chooser, dest_dir);
2901 
2902             g_free(title_label);
2903         }
2904 
2905         gtk_list_box_insert(list, row, -1);
2906     }
2907     // Now we need to set the width of the title label since it
2908     // can vary on each row
2909     if (max_title_len > 60)
2910         max_title_len = 60;
2911     for (ii = 0; ii < count; ii++)
2912     {
2913         GtkWidget *row;
2914         GtkLabel *label;
2915 
2916         row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii));
2917         label = GTK_LABEL(find_widget(row, "title_label"));
2918         gtk_label_set_max_width_chars(label, max_title_len);
2919         gtk_label_set_width_chars(label, max_title_len);
2920         gtk_label_set_ellipsize(label, PANGO_ELLIPSIZE_END);
2921     }
2922 
2923     // Clear "select all" and "clear all" options
2924     GtkToggleButton *select_all;
2925     GtkToggleButton *clear_all;
2926 
2927     clear_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder,
2928                                            "title_add_multiple_clear_all"));
2929     select_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder,
2930                                            "title_add_multiple_select_all"));
2931     gtk_toggle_button_set_active(clear_all, FALSE);
2932     gtk_toggle_button_set_active(select_all, FALSE);
2933     gtk_widget_set_sensitive(GTK_WIDGET(select_all), TRUE);
2934     gtk_widget_set_sensitive(GTK_WIDGET(clear_all), TRUE);
2935 
2936     // Disable selection of files with duplicate file names.
2937     title_add_multiple_check_conflicts(ud);
2938 
2939     // Pop up the title multiple selections dialog
2940     GtkResponseType response;
2941     GtkWidget *dialog = GHB_WIDGET(ud->builder, "title_add_multiple_dialog");
2942     response = gtk_dialog_run(GTK_DIALOG(dialog));
2943     gtk_widget_hide(dialog);
2944     if (response == GTK_RESPONSE_OK)
2945     {
2946         add_multiple_titles(ud);
2947     }
2948 
2949     // Clear title list
2950     ghb_container_empty(GTK_CONTAINER(list));
2951 }
2952 
2953 G_MODULE_EXPORT void
queue_delete_all_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2954 queue_delete_all_action_cb(GSimpleAction *action, GVariant *param,
2955                            signal_user_data_t *ud)
2956 {
2957     int ii, count;
2958 
2959     count = ghb_array_len(ud->queue);
2960     for (ii = count - 1; ii >= 0; ii--)
2961     {
2962         ghb_queue_remove_row_internal(ud, ii);
2963     }
2964     ghb_save_queue(ud->queue);
2965     ghb_update_pending(ud);
2966     ghb_queue_buttons_grey(ud);
2967 }
2968 
2969 G_MODULE_EXPORT void
queue_delete_complete_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2970 queue_delete_complete_action_cb(GSimpleAction *action, GVariant *param,
2971                                 signal_user_data_t *ud)
2972 {
2973     int        ii, count, status;
2974     GhbValue * queueDict, * uiDict;
2975 
2976     count = ghb_array_len(ud->queue);
2977     for (ii = count - 1; ii >= 0; ii--)
2978     {
2979         queueDict = ghb_array_get(ud->queue, ii);
2980         uiDict = ghb_dict_get(queueDict, "uiSettings");
2981         status = ghb_dict_get_int(uiDict, "job_status");
2982         if (status == GHB_QUEUE_DONE)
2983         {
2984             ghb_queue_remove_row_internal(ud, ii);
2985         }
2986     }
2987     ghb_save_queue(ud->queue);
2988     ghb_update_pending(ud);
2989     ghb_queue_buttons_grey(ud);
2990 }
2991 
2992 G_MODULE_EXPORT void
queue_reset_all_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2993 queue_reset_all_action_cb(GSimpleAction *action, GVariant *param,
2994                           signal_user_data_t *ud)
2995 {
2996     ghb_update_all_status(ud, GHB_QUEUE_PENDING);
2997     ghb_save_queue(ud->queue);
2998     queue_update_current_stats(ud);
2999     ghb_queue_select_log(ud);
3000     ghb_update_pending(ud);
3001     ghb_queue_buttons_grey(ud);
3002 }
3003 
3004 G_MODULE_EXPORT void
queue_reset_fail_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)3005 queue_reset_fail_action_cb(GSimpleAction *action, GVariant *param,
3006                           signal_user_data_t *ud)
3007 {
3008     int        ii, count, status;
3009     GhbValue * queueDict, * uiDict;
3010 
3011     count = ghb_array_len(ud->queue);
3012     for (ii = count - 1; ii >= 0; ii--)
3013     {
3014         queueDict = ghb_array_get(ud->queue, ii);
3015         uiDict = ghb_dict_get(queueDict, "uiSettings");
3016         status = ghb_dict_get_int(uiDict, "job_status");
3017         if (status == GHB_QUEUE_FAIL)
3018         {
3019             ghb_queue_update_status(ud, ii, GHB_QUEUE_PENDING);
3020         }
3021     }
3022     ghb_save_queue(ud->queue);
3023     queue_update_current_stats(ud);
3024     ghb_queue_select_log(ud);
3025     ghb_update_pending(ud);
3026     ghb_queue_buttons_grey(ud);
3027 }
3028 
3029 G_MODULE_EXPORT void
queue_reset_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)3030 queue_reset_action_cb(GSimpleAction *action, GVariant *param,
3031                           signal_user_data_t *ud)
3032 {
3033     GtkListBox    * lb;
3034     GtkListBoxRow * row;
3035     gint            index;
3036 
3037     lb  = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "queue_list"));
3038     row = gtk_list_box_get_selected_row(lb);
3039     if (row != NULL)
3040     {
3041         index = gtk_list_box_row_get_index(row);
3042         ghb_queue_update_status(ud, index, GHB_QUEUE_PENDING);
3043         ghb_save_queue(ud->queue);
3044         queue_update_current_stats(ud);
3045         ghb_queue_select_log(ud);
3046         ghb_update_pending(ud);
3047         ghb_queue_buttons_grey(ud);
3048     }
3049 }
3050 
3051 G_MODULE_EXPORT void
queue_remove_clicked_cb(GtkWidget * widget,signal_user_data_t * ud)3052 queue_remove_clicked_cb(GtkWidget *widget, signal_user_data_t *ud)
3053 {
3054     GtkListBoxRow * row;
3055     gint            index;
3056 
3057     row = list_box_get_row(widget);
3058     if (row != NULL)
3059     {
3060         index = gtk_list_box_row_get_index(row);
3061         ghb_queue_remove_row_internal(ud, index);
3062         ghb_save_queue(ud->queue);
3063     }
3064     ghb_update_pending(ud);
3065     ghb_queue_buttons_grey(ud);
3066 }
3067 
3068 G_MODULE_EXPORT void
queue_start_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)3069 queue_start_action_cb(GSimpleAction *action, GVariant *param,
3070                       signal_user_data_t *ud)
3071 {
3072     GhbValue *queueDict, *uiDict;
3073     gboolean running = FALSE;
3074     gint count, ii;
3075     gint status;
3076     gint state;
3077 
3078     state = ghb_get_queue_state();
3079     if (state & (GHB_STATE_WORKING | GHB_STATE_SEARCHING |
3080                  GHB_STATE_SCANNING | GHB_STATE_MUXING))
3081     {
3082         ghb_cancel_encode(ud, _("You are currently encoding.  "
3083                                 "What would you like to do?\n\n"));
3084         return;
3085     }
3086 
3087     count = ghb_array_len(ud->queue);
3088     for (ii = 0; ii < count; ii++)
3089     {
3090         queueDict = ghb_array_get(ud->queue, ii);
3091         uiDict = ghb_dict_get(queueDict, "uiSettings");
3092         status = ghb_dict_get_int(uiDict, "job_status");
3093         if ((status == GHB_QUEUE_RUNNING) ||
3094             (status == GHB_QUEUE_PENDING))
3095         {
3096             running = TRUE;
3097             break;
3098         }
3099     }
3100     if (!running)
3101     {
3102         // The queue has no running or pending jobs.
3103         // Add current settings to the queue, then run.
3104         if (!queue_add(ud, ud->settings, 0))
3105         {
3106             return;
3107         }
3108         ghb_queue_selection_init(ud);
3109         // Validation of settings may have changed audio list
3110         ghb_audio_list_refresh_all(ud);
3111     }
3112     if (state == GHB_STATE_IDLE)
3113     {
3114         // Add the first pending queue item and start
3115         ghb_start_next_job(ud);
3116     }
3117 }
3118 
3119 G_MODULE_EXPORT void
queue_pause_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)3120 queue_pause_action_cb(GSimpleAction *action, GVariant *param,
3121                       signal_user_data_t *ud)
3122 {
3123     ghb_pause_resume_queue();
3124 }
3125 
3126 static gboolean clear_select_all_busy = FALSE;
3127 
3128 G_MODULE_EXPORT void
title_add_multiple_select_all_cb(GtkWidget * widget,signal_user_data_t * ud)3129 title_add_multiple_select_all_cb(GtkWidget *widget, signal_user_data_t *ud)
3130 {
3131     gint count, ii;
3132     GhbValue *settings;
3133     GtkWidget *row;
3134     GtkListBox *list;
3135     GtkToggleButton *selected;
3136     gboolean can_select;
3137     GtkToggleButton *clear_all;
3138     GtkToggleButton *select_all;
3139 
3140     if (!ghb_widget_boolean(widget))
3141         return;
3142 
3143     clear_select_all_busy = TRUE;
3144 
3145     clear_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder,
3146                                            "title_add_multiple_clear_all"));
3147     select_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder,
3148                                            "title_add_multiple_select_all"));
3149     gtk_toggle_button_set_active(clear_all, FALSE);
3150     gtk_widget_set_sensitive(GTK_WIDGET(select_all), FALSE);
3151     gtk_widget_set_sensitive(GTK_WIDGET(clear_all), TRUE);
3152 
3153     list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list"));
3154     count = ghb_array_len(ud->settings_array);
3155     for (ii = 0; ii < count; ii++)
3156     {
3157         row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii));
3158         selected = GTK_TOGGLE_BUTTON(find_widget(row, "title_selected"));
3159         settings = ghb_array_get(ud->settings_array, ii);
3160         can_select = title_multiple_can_select(ud->settings_array, ii);
3161         ghb_dict_set_bool(settings, "title_selected", can_select);
3162         gtk_toggle_button_set_active(selected, TRUE);
3163         title_add_multiple_set_sensitive(GTK_WIDGET(row), can_select);
3164     }
3165     clear_select_all_busy = FALSE;
3166 }
3167 
3168 G_MODULE_EXPORT void
title_add_multiple_clear_all_cb(GtkWidget * widget,signal_user_data_t * ud)3169 title_add_multiple_clear_all_cb(GtkWidget *widget, signal_user_data_t *ud)
3170 {
3171     gint count, ii;
3172     GhbValue *settings;
3173     GtkWidget *row;
3174     GtkListBox *list;
3175     GtkToggleButton *selected;
3176     GtkToggleButton *clear_all;
3177     GtkToggleButton *select_all;
3178 
3179     if (!ghb_widget_boolean(widget))
3180         return;
3181 
3182     clear_select_all_busy = TRUE;
3183 
3184     clear_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder,
3185                                            "title_add_multiple_clear_all"));
3186     select_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder,
3187                                            "title_add_multiple_select_all"));
3188     gtk_toggle_button_set_active(select_all, FALSE);
3189     gtk_widget_set_sensitive(GTK_WIDGET(select_all), TRUE);
3190     gtk_widget_set_sensitive(GTK_WIDGET(clear_all), FALSE);
3191 
3192     list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list"));
3193     count = ghb_array_len(ud->settings_array);
3194     for (ii = 0; ii < count; ii++)
3195     {
3196         row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii));
3197         selected = GTK_TOGGLE_BUTTON(find_widget(row, "title_selected"));
3198         settings = ghb_array_get(ud->settings_array, ii);
3199         ghb_dict_set_bool(settings, "title_selected", FALSE);
3200         gtk_toggle_button_set_active(selected, FALSE);
3201     }
3202     clear_select_all_busy = FALSE;
3203 }
3204 
3205 G_MODULE_EXPORT void
title_selected_cb(GtkWidget * widget,signal_user_data_t * ud)3206 title_selected_cb(GtkWidget *widget, signal_user_data_t *ud)
3207 {
3208     GhbValue *settings;
3209     gboolean selected;
3210     GtkToggleButton *select_all;
3211     GtkToggleButton *clear_all;
3212     gboolean can_select;
3213 
3214     if (clear_select_all_busy)
3215         return;
3216 
3217     GtkListBoxRow * row = list_box_get_row(widget);
3218     if (row == NULL)
3219         return;
3220 
3221     clear_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder,
3222                                            "title_add_multiple_clear_all"));
3223     select_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder,
3224                                            "title_add_multiple_select_all"));
3225     gtk_toggle_button_set_active(select_all, FALSE);
3226     gtk_toggle_button_set_active(clear_all, FALSE);
3227     gtk_widget_set_sensitive(GTK_WIDGET(clear_all), TRUE);
3228     gtk_widget_set_sensitive(GTK_WIDGET(select_all), TRUE);
3229 
3230     gint index = gtk_list_box_row_get_index(row);
3231     selected = ghb_widget_boolean(widget);
3232     settings = ghb_array_get(ud->settings_array, index);
3233     can_select = title_multiple_can_select(ud->settings_array, index);
3234     ghb_dict_set_bool(settings, "title_selected",
3235                              selected && can_select);
3236 }
3237 
3238 G_MODULE_EXPORT void
title_dest_file_cb(GtkWidget * widget,signal_user_data_t * ud)3239 title_dest_file_cb(GtkWidget *widget, signal_user_data_t *ud)
3240 {
3241     GhbValue *settings;
3242     const gchar *dest_dir;
3243     gchar *dest_file, *dest;
3244     GtkListBoxRow * row = list_box_get_row(widget);
3245     if (row == NULL)
3246         return;
3247     gint index = gtk_list_box_row_get_index(row);
3248 
3249     dest_file = ghb_widget_string(widget);
3250     settings = ghb_array_get(ud->settings_array, index);
3251 
3252     ghb_dict_set_string(settings, "dest_file", dest_file);
3253     dest_dir = ghb_dict_get_string(settings, "dest_dir");
3254     dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
3255     ghb_dict_set_string(settings, "destination", dest);
3256     GhbValue *dest_dict = ghb_get_job_dest_settings(settings);
3257     ghb_dict_set_string(dest_dict, "File", dest);
3258 
3259     // Check if changing the destination file name resolves
3260     // a file name conflict.  Enable selection if so.
3261     // Disable selection if it creates a conflict!!!
3262     gboolean selected, can_select;
3263 
3264     widget     = find_widget(GTK_WIDGET(row), "title_selected");
3265     selected   = ghb_widget_boolean(widget);
3266     can_select = title_multiple_can_select(ud->settings_array, index);
3267 
3268     ghb_dict_set_bool(settings, "title_selected", selected && can_select);
3269     title_add_multiple_set_sensitive(GTK_WIDGET(row), can_select);
3270 
3271     g_free(dest_file);
3272     g_free(dest);
3273 
3274     title_add_multiple_set_conflict_label(ud,
3275         title_add_multiple_are_conflicts(ud));
3276 }
3277 
3278 G_MODULE_EXPORT void
title_dest_dir_cb(GtkWidget * widget,signal_user_data_t * ud)3279 title_dest_dir_cb(GtkWidget *widget, signal_user_data_t *ud)
3280 {
3281     GhbValue *settings;
3282     const gchar *dest_file;
3283     gchar *dest_dir, *dest;
3284     GtkListBoxRow * row = list_box_get_row(widget);
3285     if (row == NULL)
3286         return;
3287     gint index = gtk_list_box_row_get_index(row);
3288 
3289     dest_dir = ghb_widget_string(widget);
3290     settings = ghb_array_get(ud->settings_array, index);
3291 
3292     ghb_dict_set_string(settings, "dest_dir", dest_dir);
3293     dest_file = ghb_dict_get_string(settings, "dest_file");
3294     dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
3295     ghb_dict_set_string(settings, "destination", dest);
3296     GhbValue *dest_dict = ghb_get_job_dest_settings(settings);
3297     ghb_dict_set_string(dest_dict, "File", dest);
3298 
3299     // Check if changing the destination file name resolves
3300     // a file name conflict.  Enable selection if so.
3301     // Disable selection if it creates a conflict!!!
3302     gboolean selected, can_select;
3303 
3304     widget     = find_widget(GTK_WIDGET(row), "title_selected");
3305     selected   = ghb_widget_boolean(widget);
3306     can_select = title_multiple_can_select(ud->settings_array, index);
3307 
3308     ghb_dict_set_bool(settings, "title_selected", selected && can_select);
3309     title_add_multiple_set_sensitive(GTK_WIDGET(row), can_select);
3310 
3311     g_free(dest_dir);
3312     g_free(dest);
3313 
3314     title_add_multiple_set_conflict_label(ud,
3315         title_add_multiple_are_conflicts(ud));
3316 }
3317 
3318 // Set up view of row to be dragged
3319 G_MODULE_EXPORT void
3320 #if GTK_CHECK_VERSION(3, 90, 0)
queue_drag_begin_cb(GtkWidget * widget,GdkDrag * context,signal_user_data_t * ud)3321 queue_drag_begin_cb(GtkWidget          * widget,
3322                     GdkDrag            * context,
3323                     signal_user_data_t * ud)
3324 #else
3325 queue_drag_begin_cb(GtkWidget          * widget,
3326                     GdkDragContext     * context,
3327                     signal_user_data_t * ud)
3328 #endif
3329 {
3330     GtkListBox      * lb;
3331     GtkWidget       * row;
3332 
3333     row = gtk_widget_get_ancestor(widget, GTK_TYPE_LIST_BOX_ROW);
3334     lb  = GTK_LIST_BOX(gtk_widget_get_parent(row));
3335     gtk_list_box_select_row(lb, GTK_LIST_BOX_ROW(row));
3336 
3337 #if GTK_CHECK_VERSION(3, 90, 0)
3338     GdkPaintable * paintable = gtk_widget_paintable_new(row);
3339     gtk_drag_set_icon_paintable(context, paintable, 0, 0);
3340     g_object_unref(paintable);
3341 #else
3342     GtkAllocation     alloc;
3343     cairo_surface_t * surface;
3344     cairo_t         * cr;
3345     int               x, y;
3346 
3347     gtk_widget_get_allocation(row, &alloc);
3348     surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
3349                                          alloc.width, alloc.height);
3350     cr = cairo_create(surface);
3351 
3352     gtk_style_context_add_class(gtk_widget_get_style_context(row), "drag-icon");
3353     gtk_widget_draw(row, cr);
3354     gtk_style_context_remove_class(gtk_widget_get_style_context(row),
3355                                    "drag-icon");
3356 
3357     gtk_widget_translate_coordinates(widget, row, 0, 0, &x, &y);
3358     cairo_surface_set_device_offset(surface, -x, -y);
3359     gtk_drag_set_icon_surface(context, surface);
3360 
3361     cairo_destroy(cr);
3362     cairo_surface_destroy(surface);
3363 #endif
3364 
3365     g_object_set_data(G_OBJECT(gtk_widget_get_parent(row)), "drag-row", row);
3366     gtk_style_context_add_class(gtk_widget_get_style_context(row), "drag-row");
3367 }
3368 
3369 G_MODULE_EXPORT void
3370 #if GTK_CHECK_VERSION(3, 90, 0)
queue_drag_end_cb(GtkWidget * widget,GdkDrag * context,signal_user_data_t * ud)3371 queue_drag_end_cb(GtkWidget          * widget,
3372                   GdkDrag            * context,
3373                   signal_user_data_t * ud)
3374 #else
3375 queue_drag_end_cb(GtkWidget          * widget,
3376                   GdkDragContext     * context,
3377                   signal_user_data_t * ud)
3378 #endif
3379 {
3380     GtkWidget * row;
3381 
3382     row = gtk_widget_get_ancestor(widget, GTK_TYPE_LIST_BOX_ROW);
3383     g_object_set_data(G_OBJECT(gtk_widget_get_parent(row)), "drag-row", NULL);
3384     gtk_style_context_remove_class(gtk_widget_get_style_context(row),
3385                                    "drag-row");
3386     gtk_style_context_remove_class(gtk_widget_get_style_context(row),
3387                                    "drag-hover");
3388 }
3389 
3390 // Set selection to the row being dragged
3391 G_MODULE_EXPORT void
3392 #if GTK_CHECK_VERSION(3, 90, 0)
queue_drag_data_get_cb(GtkWidget * widget,GdkDrag * context,GtkSelectionData * selection_data,signal_user_data_t * ud)3393 queue_drag_data_get_cb(GtkWidget          * widget,
3394                        GdkDrag            * context,
3395                        GtkSelectionData   * selection_data,
3396                        signal_user_data_t * ud)
3397 #else
3398 queue_drag_data_get_cb(GtkWidget          * widget,
3399                        GdkDragContext     * context,
3400                        GtkSelectionData   * selection_data,
3401                        guint                info,
3402                        guint                time,
3403                        signal_user_data_t * ud)
3404 #endif
3405 {
3406     GtkWidget * row;
3407 
3408     row = gtk_widget_get_ancestor(widget, GTK_TYPE_LIST_BOX_ROW);
3409     gtk_selection_data_set(selection_data,
3410                        ghb_atom_string("GTK_LIST_BOX_ROW"), 32,
3411                        (const guchar *)&row, sizeof (gpointer));
3412 }
3413 
3414 G_MODULE_EXPORT void
3415 #if GTK_CHECK_VERSION(3, 90, 0)
queue_drag_leave_cb(GtkListBox * lb,GdkDrop * ctx,signal_user_data_t * ud)3416 queue_drag_leave_cb(
3417     GtkListBox         * lb,
3418     GdkDrop            * ctx,
3419     signal_user_data_t * ud)
3420 #else
3421 queue_drag_leave_cb(
3422     GtkListBox         * lb,
3423     GdkDragContext     * ctx,
3424     guint                time,
3425     signal_user_data_t * ud)
3426 #endif
3427 {
3428     GtkWidget * drag_row;
3429     GtkWidget * row_before;
3430     GtkWidget * row_after;
3431 
3432     drag_row   = GTK_WIDGET(g_object_get_data(G_OBJECT(lb), "drag-row"));
3433     row_before = GTK_WIDGET(g_object_get_data(G_OBJECT(lb), "row-before"));
3434     row_after  = GTK_WIDGET(g_object_get_data(G_OBJECT(lb), "row-after"));
3435 
3436     gtk_style_context_remove_class(gtk_widget_get_style_context(drag_row),
3437                                    "drag-hover");
3438     if (row_before)
3439     {
3440         gtk_style_context_remove_class(gtk_widget_get_style_context(row_before),
3441                                        "drag-hover-bottom");
3442     }
3443     if (row_after)
3444     {
3445         gtk_style_context_remove_class(gtk_widget_get_style_context(row_after),
3446                                        "drag-hover-top");
3447     }
3448 }
3449 
3450 static GtkListBoxRow *
get_last_row(GtkListBox * list)3451 get_last_row(GtkListBox * list)
3452 {
3453     int i;
3454     GtkListBoxRow * row;
3455 
3456     row = NULL;
3457     for (i = 0; ; i++)
3458     {
3459         GtkListBoxRow * tmp;
3460         tmp = gtk_list_box_get_row_at_index(list, i);
3461         if (tmp == NULL)
3462             return row;
3463         row = tmp;
3464     }
3465     return row;
3466 }
3467 
3468 static GtkListBoxRow *
get_row_before(GtkListBox * list,GtkListBoxRow * row)3469 get_row_before (GtkListBox    * list, GtkListBoxRow * row)
3470 {
3471     int pos = gtk_list_box_row_get_index(row);
3472     return gtk_list_box_get_row_at_index(list, pos - 1);
3473 }
3474 
3475 static GtkListBoxRow *
get_row_after(GtkListBox * list,GtkListBoxRow * row)3476 get_row_after (GtkListBox    * list, GtkListBoxRow * row)
3477 {
3478     int pos = gtk_list_box_row_get_index(row);
3479     return gtk_list_box_get_row_at_index(list, pos + 1);
3480 }
3481 
3482 G_MODULE_EXPORT gboolean
3483 #if GTK_CHECK_VERSION(3, 90, 0)
queue_drag_motion_cb(GtkListBox * lb,GdkDrop * ctx,gint x,gint y,signal_user_data_t * ud)3484 queue_drag_motion_cb(
3485     GtkListBox         * lb,
3486     GdkDrop            * ctx,
3487     gint                 x,
3488     gint                 y,
3489     signal_user_data_t * ud)
3490 #else
3491 queue_drag_motion_cb(
3492     GtkListBox         * lb,
3493     GdkDragContext     * ctx,
3494     gint                 x,
3495     gint                 y,
3496     guint                time,
3497     signal_user_data_t * ud)
3498 #endif
3499 {
3500     GtkAllocation   alloc;
3501     GtkWidget     * row;
3502     int             hover_row_y;
3503     int             hover_row_height;
3504     GtkWidget     * drag_row;
3505     GtkWidget     * row_before;
3506     GtkWidget     * row_after;
3507 
3508     row = GTK_WIDGET(gtk_list_box_get_row_at_y(GTK_LIST_BOX(lb), y));
3509 
3510     drag_row   = GTK_WIDGET(g_object_get_data(G_OBJECT(lb), "drag-row"));
3511     row_before = GTK_WIDGET(g_object_get_data(G_OBJECT(lb), "row-before"));
3512     row_after  = GTK_WIDGET(g_object_get_data(G_OBJECT(lb), "row-after"));
3513 
3514     gtk_style_context_remove_class(gtk_widget_get_style_context(drag_row),
3515                                    "drag-hover");
3516     if (row_before)
3517     {
3518         gtk_style_context_remove_class(gtk_widget_get_style_context(row_before),
3519                                        "drag-hover-bottom");
3520     }
3521     if (row_after)
3522     {
3523         gtk_style_context_remove_class(gtk_widget_get_style_context(row_after),
3524                                        "drag-hover-top");
3525     }
3526 
3527     if (row)
3528     {
3529         gtk_widget_get_allocation(row, &alloc);
3530         hover_row_y = alloc.y;
3531         hover_row_height = alloc.height;
3532 
3533         if (y < hover_row_y + hover_row_height/2)
3534         {
3535             row_after = row;
3536             row_before = GTK_WIDGET(get_row_before(lb, GTK_LIST_BOX_ROW(row)));
3537         }
3538         else
3539         {
3540             row_before = row;
3541             row_after = GTK_WIDGET(get_row_after(lb, GTK_LIST_BOX_ROW(row)));
3542         }
3543     }
3544     else
3545     {
3546         row_before = GTK_WIDGET(get_last_row(lb));
3547         row_after = NULL;
3548     }
3549 
3550     g_object_set_data(G_OBJECT(lb), "row-before", row_before);
3551     g_object_set_data(G_OBJECT(lb), "row-after", row_after);
3552 
3553     if (drag_row == row_before || drag_row == row_after)
3554     {
3555         gtk_style_context_add_class(gtk_widget_get_style_context(drag_row),
3556                                     "drag-hover");
3557         return FALSE;
3558     }
3559 
3560     if (row_before)
3561     {
3562         gtk_style_context_add_class(gtk_widget_get_style_context(row_before),
3563                                     "drag-hover-bottom");
3564     }
3565     if (row_after)
3566     {
3567         gtk_style_context_add_class(gtk_widget_get_style_context(row_after), "drag-hover-top");
3568     }
3569 
3570     return TRUE;
3571 }
3572 
3573 G_MODULE_EXPORT void
3574 #if GTK_CHECK_VERSION(3, 90, 0)
queue_drag_data_received_cb(GtkListBox * lb,GdkDrop * context,GtkSelectionData * selection_data,signal_user_data_t * ud)3575 queue_drag_data_received_cb(GtkListBox         * lb,
3576                             GdkDrop            * context,
3577                             GtkSelectionData   * selection_data,
3578                             signal_user_data_t * ud)
3579 #else
3580 queue_drag_data_received_cb(GtkListBox         * lb,
3581                             GdkDragContext     * context,
3582                             gint                 x,
3583                             gint                 y,
3584                             GtkSelectionData   * selection_data,
3585                             guint                info,
3586                             guint32              time,
3587                             signal_user_data_t * ud)
3588 #endif
3589 {
3590     GtkWidget * row_before;
3591     GtkWidget * row_after;
3592     GtkWidget * src_row;
3593     int         src_index, dst_index;
3594 
3595     row_before = GTK_WIDGET(g_object_get_data(G_OBJECT(lb), "row-before"));
3596     row_after  = GTK_WIDGET(g_object_get_data(G_OBJECT(lb), "row-after"));
3597 
3598     g_object_set_data(G_OBJECT(lb), "row-before", NULL);
3599     g_object_set_data(G_OBJECT(lb), "row-after", NULL);
3600 
3601     if (row_before)
3602     {
3603         gtk_style_context_remove_class(gtk_widget_get_style_context(row_before),
3604                                        "drag-hover-bottom");
3605     }
3606     if (row_after)
3607     {
3608         gtk_style_context_remove_class(gtk_widget_get_style_context(row_after),
3609                                        "drag-hover-top");
3610     }
3611 
3612     src_row = GTK_WIDGET(*(gpointer*)gtk_selection_data_get_data(selection_data));
3613 
3614     if (src_row == row_after)
3615     {
3616         gtk_list_box_select_row(lb, GTK_LIST_BOX_ROW(src_row));
3617         return;
3618     }
3619 
3620     src_index = gtk_list_box_row_get_index(GTK_LIST_BOX_ROW(src_row));
3621     if (row_after)
3622     {
3623         dst_index = gtk_list_box_row_get_index(GTK_LIST_BOX_ROW(row_after));
3624     }
3625     else
3626     {
3627         dst_index = gtk_list_box_row_get_index(GTK_LIST_BOX_ROW(row_before)) + 1;
3628     }
3629 
3630     if (src_index < dst_index)
3631     {
3632         // The source row is removed before re-inserting it elsewhere
3633         // in the list.  If the source is before the dest, the dest position
3634         // moves
3635         dst_index -= 1;
3636     }
3637     g_object_ref(src_row);
3638 
3639     gtk_container_remove(GTK_CONTAINER(lb), src_row);
3640     gtk_list_box_insert(lb, src_row, dst_index);
3641     g_object_unref(src_row);
3642 
3643     GhbValue   * queueDict;
3644 
3645     queueDict = ghb_array_get(ud->queue, src_index);
3646     ghb_value_incref(queueDict);
3647     ghb_array_remove(ud->queue, src_index);
3648     ghb_array_insert(ud->queue, dst_index, queueDict);
3649 
3650     // Force refresh of current selection
3651     gtk_list_box_unselect_row(lb, GTK_LIST_BOX_ROW(src_row));
3652     gtk_list_box_select_row(lb, GTK_LIST_BOX_ROW(src_row));
3653 
3654     ghb_save_queue(ud->queue);
3655 }
3656