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