OSDN Git Service

LinGui: Make preset key/values mostly align with macui presets.
[handbrake-jp/handbrake-jp-git.git] / gtk / src / queuehandler.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * callbacks.c
4  * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
5  * 
6  * callbacks.c is free software.
7  * 
8  * You may redistribute it and/or modify it under the terms of the
9  * GNU General Public License, as published by the Free Software
10  * Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  */
13
14 #include <gtk/gtk.h>
15 #include <gdk/gdkkeysyms.h>
16 #include <glib/gstdio.h>
17 #include <gio/gio.h>
18 #include "hb.h"
19 #include "settings.h"
20 #include "hb-backend.h"
21 #include "values.h"
22 #include "callbacks.h"
23 #include "presets.h"
24 #include "ghb-dvd.h"
25
26 void
27 queue_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
28 {
29         GtkTreeModel *store;
30         GtkTreeIter iter, piter;
31         
32         g_debug("queue_list_selection_changed_cb ()");
33         // A queue entry is made up of a parent and multiple
34         // children that are visible when expanded.  When and entry
35         // is selected, I want the parent to be selected.
36         // This is purely cosmetic.
37         if (gtk_tree_selection_get_selected(selection, &store, &iter))
38         {
39                 GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_edit");
40                 gtk_widget_set_sensitive (widget, TRUE);
41                 if (gtk_tree_model_iter_parent (store, &piter, &iter))
42                 {
43                         GtkTreePath *path;
44                         GtkTreeView *treeview;
45                         
46                         gtk_tree_selection_select_iter (selection, &piter);
47                         path = gtk_tree_model_get_path (store, &piter);
48                         treeview = gtk_tree_selection_get_tree_view (selection);
49                         // Make the parent visible in scroll window if it is not.
50                         gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0);
51                         gtk_tree_path_free(path);
52                 }
53         }
54         else
55         {
56                 GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_edit");
57                 gtk_widget_set_sensitive (widget, FALSE);
58         }
59 }
60
61 static void
62 add_to_queue_list(signal_user_data_t *ud, GValue *settings, GtkTreeIter *piter)
63 {
64         GtkTreeView *treeview;
65         GtkTreeIter iter;
66         GtkTreeStore *store;
67         gchar *info;
68         gint status;
69         GtkTreeIter citer;
70         gchar *dest, *preset, *vol_name, *basename;
71         const gchar *vcodec, *container;
72         gchar *fps, *vcodec_abbr;
73         gint title, start_chapter, end_chapter, width, height;
74         gint source_width, source_height;
75         gboolean pass2, anamorphic, round_dim, keep_aspect, vqtype, turbo;
76         gboolean tweaks;
77         
78         g_debug("update_queue_list ()");
79         if (settings == NULL) return;
80         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
81         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
82                 
83         tweaks = ghb_settings_get_boolean(settings, "allow_tweaks");
84         title = ghb_settings_combo_int(settings, "title");
85         start_chapter = ghb_settings_get_int(settings, "start_chapter");
86         end_chapter = ghb_settings_get_int(settings, "end_chapter");
87         pass2 = ghb_settings_get_boolean(settings, "VideoTwoPass");
88         vol_name = ghb_settings_get_string(settings, "volume_label");
89         dest = ghb_settings_get_string(settings, "destination");
90         basename = g_path_get_basename(dest);
91         info = g_strdup_printf 
92         (
93                 "<big><b>%s</b></big> "
94                 "<small>(Title %d, Chapters %d through %d, %d Video %s)"
95                 " --> %s</small>",
96                  vol_name, title+1, start_chapter, end_chapter, 
97                  pass2 ? 2:1, pass2 ? "Passes":"Pass", basename
98         );
99
100         if (piter)
101                 iter = *piter;
102         else
103                 gtk_tree_store_append(store, &iter, NULL);
104
105         gtk_tree_store_set(store, &iter, 1, info, 2, "hb-queue-delete", -1);
106         g_free(info);
107         status = ghb_settings_get_int(settings, "job_status");
108         switch (status)
109         {
110                 case GHB_QUEUE_PENDING:
111                         gtk_tree_store_set(store, &iter, 0, "hb-queue-job", -1);
112                         break;
113                 case GHB_QUEUE_CANCELED:
114                         gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
115                         break;
116                 case GHB_QUEUE_RUNNING:
117                         gtk_tree_store_set(store, &iter, 0, "hb-working0", -1);
118                         break;
119                 case GHB_QUEUE_DONE:
120                         gtk_tree_store_set(store, &iter, 0, "hb-complete", -1);
121                         break;
122                 default:
123                         gtk_tree_store_set(store, &iter, 0, "hb-queue-job", -1);
124                         break;
125         }
126
127         GString *str = g_string_new("");
128         gboolean markers;
129         gboolean preset_modified;
130         gint mux;
131         const GValue *path;
132
133         container = ghb_settings_combo_option(settings, "FileFormat");
134         mux = ghb_settings_combo_int(settings, "FileFormat");
135         preset_modified = ghb_settings_get_boolean(settings, "preset_modified");
136         path = ghb_settings_get_value(settings, "preset");
137         preset = ghb_preset_path_string(path);
138         markers = ghb_settings_get_boolean(settings, "ChapterMarkers");
139
140         if (preset_modified)
141                 g_string_append_printf(str, 
142                         "<b>Customized Preset Based On:</b> <small>%s</small>\n", 
143                         preset);
144         else
145                 g_string_append_printf(str, 
146                         "<b>Preset:</b> <small>%s</small>\n", 
147                         preset);
148
149         if (markers)
150         {
151                 g_string_append_printf(str, 
152                         "<b>Format:</b> <small>%s Container, Chapter Markers</small>\n", 
153                         container);
154         }
155         else
156         {
157                 g_string_append_printf(str, 
158                         "<b>Format:</b> <small>%s Container</small>\n", container);
159         }
160         if (mux == HB_MUX_MP4)
161         {
162                 gboolean ipod, http, large;
163
164                 ipod = ghb_settings_get_boolean(settings, "Mp4iPodCompatible");
165                 http = ghb_settings_get_boolean(settings, "Mp4HttpOptimize");
166                 large = ghb_settings_get_boolean(settings, "Mp4LargeFile");
167                 if (http || ipod || large)
168                 {
169                         g_string_append_printf(str, "<b>MP4 Options:</b><small>");
170                         if (ipod)
171                                 g_string_append_printf(str, " - iPod 5G Support");
172                         if (http)
173                                 g_string_append_printf(str, " - Web Optimized");
174                         if (large)
175                                 g_string_append_printf(str, " - Large File Size (>4GB)");
176                         g_string_append_printf(str, "</small>\n");
177                 }
178         }
179         g_string_append_printf(str, 
180                 "<b>Destination:</b> <small>%s</small>\n", dest);
181
182         width = ghb_settings_get_int(settings, "scale_width");
183         height = ghb_settings_get_int(settings, "scale_height");
184         anamorphic = ghb_settings_get_boolean(settings, "anamorphic");
185         round_dim = ghb_settings_get_boolean(settings, "ModDimensions");
186         keep_aspect = ghb_settings_get_boolean(settings, "PictureKeepRatio");
187
188         gchar *aspect_desc;
189         if (anamorphic)
190         {
191                 if (round_dim)
192                 {
193                         aspect_desc = "(Anamorphic)";
194                 }
195                 else
196                 {
197                         aspect_desc = "(Strict Anamorphic)";
198                 }
199         }
200         else
201         {
202                 if (keep_aspect)
203                 {
204                         aspect_desc = "(Aspect Preserved)";
205                 }
206                 else
207                 {
208                         aspect_desc = "(Aspect Lost)";
209                 }
210         }
211         vqtype = ghb_settings_get_boolean(settings, "vquality_type_constant");
212
213         gchar *vq_desc = "Error";
214         gchar *vq_units = "";
215         gchar *vqstr;
216         gdouble vqvalue;
217         if (!vqtype)
218         {
219                 vqtype = ghb_settings_get_boolean(settings, "vquality_type_target");
220                 if (!vqtype)
221                 {
222                         // Has to be bitrate
223                         vqvalue = ghb_settings_get_int(settings, "VideoAvgBitrate");
224                         vq_desc = "Bitrate:";
225                         vq_units = "kbps";
226                 }
227                 else
228                 {
229                         // Target file size
230                         vqvalue = ghb_settings_get_int(settings, "VideoTargetSize");
231                         vq_desc = "Target Size:";
232                         vq_units = "MB";
233                 }
234                 vqstr = g_strdup_printf("%d", (gint)vqvalue);
235         }
236         else
237         {
238                 // Constant quality
239                 vqvalue = ghb_settings_get_double(settings, "VideoQualitySlider");
240                 vq_desc = "Constant Quality:";
241                 if (ghb_settings_get_boolean(settings, "directqp"))
242                 {
243                         vqstr = g_strdup_printf("%d", (gint)vqvalue);
244                         vq_units = "(crf)";
245                 }
246                 else
247                 {
248                         vqstr = g_strdup_printf("%.1f", 100*vqvalue);
249                         vq_units = "%";
250                 }
251         }
252         fps = ghb_settings_get_string(settings, "VideoFramerate");
253         if (strcmp("source", fps) == 0)
254         {
255                 g_free(fps);
256                 if (ghb_settings_get_boolean(settings, "PictureDetelecine"))
257                         fps = g_strdup("Same As Source (vfr detelecine)");
258                 else
259                         fps = g_strdup("Same As Source (variable)");
260         }
261         else
262         {
263                 gchar *tmp;
264                 tmp = g_strdup_printf("%s (constant frame rate)", fps);
265                 g_free(fps);
266                 fps = tmp;
267         }
268         vcodec = ghb_settings_combo_option(settings, "VideoEncoder");
269         vcodec_abbr = ghb_settings_get_string(settings, "VideoEncoder");
270         source_width = ghb_settings_get_int(settings, "source_width");
271         source_height = ghb_settings_get_int(settings, "source_height");
272         g_string_append_printf(str,
273                 "<b>Picture:</b> Source: <small>%d x %d, Output %d x %d %s</small>\n",
274                  source_width, source_height, width, height, aspect_desc);
275
276         gboolean decomb;
277         gboolean filters = FALSE;
278
279         decomb = ghb_settings_get_boolean(settings, "PictureDecomb");
280         g_string_append_printf(str, "<b>Filters:</b><small>");
281         if (ghb_settings_get_boolean(settings, "PictureDetelecine"))
282         {
283                 g_string_append_printf(str, " - Detelecine");
284                 filters = TRUE;
285         }
286         if (decomb)
287         {
288                 g_string_append_printf(str, " - Decomb");
289                 filters = TRUE;
290         }
291         else
292         {
293                 gint deint = ghb_settings_combo_int(settings, 
294                                         tweaks ? "tweak_PictureDeinterlace":"PictureDeinterlace");
295                 if (deint)
296                 {
297                         const gchar *opt = ghb_settings_combo_option(settings,
298                                         tweaks ? "tweak_PictureDeinterlace":"PictureDeinterlace");
299                         g_string_append_printf(str, " - Deinterlace: %s", opt);
300                         filters = TRUE;
301                 }
302         }
303         gint denoise = ghb_settings_combo_int(settings, 
304                                 tweaks ? "tweak_PictureDenoise":"PictureDenoise");
305         if (denoise)
306         {
307                 const gchar *opt = ghb_settings_combo_option(settings,
308                                 tweaks ? "tweak_PictureDenoise":"PictureDenoise");
309                 g_string_append_printf(str, " - Denoise: %s", opt);
310                 filters = TRUE;
311         }
312         gint deblock = ghb_settings_get_int(settings, "PictureDeblock");
313         if (deblock >= 5)
314         {
315                 g_string_append_printf(str, " - Deblock (%d)", deblock);
316                 filters = TRUE;
317         }
318         if (ghb_settings_get_boolean(settings, "VideoGrayScale"))
319         {
320                 g_string_append_printf(str, " - Grayscale");
321                 filters = TRUE;
322         }
323         if (!filters)
324                 g_string_append_printf(str, " None");
325         g_string_append_printf(str, "</small>\n");
326
327         g_string_append_printf(str,
328                 "<b>Video:</b> <small>%s, Framerate: %s, %s %s%s</small>\n",
329                  vcodec, fps, vq_desc, vqstr, vq_units);
330
331         turbo = ghb_settings_get_boolean(settings, "VideoTurboTwoPass");
332         if (turbo)
333         {
334                 g_string_append_printf(str, "<b>Turbo:</b> <small>On</small>\n");
335         }
336         if (strcmp(vcodec_abbr, "x264") == 0)
337         {
338                 gchar *x264opts = ghb_build_x264opts_string(settings);
339                 g_string_append_printf(str, 
340                         "<b>x264 Options:</b> <small>%s</small>\n", x264opts);
341                 g_free(x264opts);
342         }
343         // Add the audios
344         gint count, ii;
345         const GValue *audio_list;
346
347         audio_list = ghb_settings_get_value(settings, "audio_list");
348         count = ghb_array_len(audio_list);
349         for (ii = 0; ii < count; ii++)
350         {
351                 gchar *bitrate, *samplerate, *track;
352                 const gchar *acodec, *mix;
353                 GValue *asettings;
354
355                 asettings = ghb_array_get_nth(audio_list, ii);
356
357                 acodec = ghb_settings_combo_option(asettings, "AudioEncoder");
358                 bitrate = ghb_settings_get_string(asettings, "AudioBitrate");
359                 samplerate = ghb_settings_get_string(asettings, "AudioSamplerate");
360                 if (strcmp("source", samplerate) == 0)
361                 {
362                         g_free(samplerate);
363                         samplerate = g_strdup("Same As Source");
364                 }
365                 track = ghb_settings_get_string(asettings, "AudioTrackDescription");
366                 mix = ghb_settings_combo_option(asettings, "AudioMixdown");
367                 g_string_append_printf(str,
368                         "<b>Audio:</b><small> %s, Encoder: %s, Mixdown: %s, SampleRate: %s, Bitrate: %s</small>",
369                          track, acodec, mix, samplerate, bitrate);
370                 if (ii < count-1)
371                         g_string_append_printf(str, "\n");
372                 g_free(track);
373                 g_free(bitrate);
374                 g_free(samplerate);
375         }
376         info = g_string_free(str, FALSE);
377         gtk_tree_store_append(store, &citer, &iter);
378         gtk_tree_store_set(store, &citer, 1, info, -1);
379         g_free(info);
380         g_free(fps);
381         g_free(vcodec_abbr);
382         g_free(vol_name);
383         g_free(dest);
384         g_free(preset);
385 }
386
387 void
388 audio_list_refresh(signal_user_data_t *ud)
389 {
390         GtkTreeView *treeview;
391         GtkTreeIter iter;
392         GtkListStore *store;
393         gboolean done;
394         gint row = 0;
395         GValue *audio_list;
396
397         g_debug("ghb_audio_list_refresh ()");
398         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list"));
399         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
400         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
401         {
402                 do
403                 {
404                         const gchar *track, *codec, *br, *sr, *mix;
405                         gchar *drc, *s_track, *s_codec, *s_br, *s_sr, *s_mix;
406                         gdouble s_drc;
407                         GValue *asettings;
408
409                         audio_list = ghb_settings_get_value(ud->settings, "audio_list");
410                         if (row >= ghb_array_len(audio_list))
411                                 return;
412                         asettings = ghb_array_get_nth(audio_list, row);
413
414                         track = ghb_settings_combo_option(asettings, "AudioTrack");
415                         codec = ghb_settings_combo_option(asettings, "AudioEncoder");
416                         br = ghb_settings_combo_option(asettings, "AudioBitrate");
417                         sr = ghb_settings_combo_option(asettings, "AudioSamplerate");
418                         mix = ghb_settings_combo_option(asettings, "AudioMixdown");
419                         drc = ghb_settings_get_string(asettings, "AudioTrackDRCSlider");
420
421                         s_track = ghb_settings_get_string(asettings, "AudioTrack");
422                         s_codec = ghb_settings_get_string(asettings, "AudioEncoder");
423                         s_br = ghb_settings_get_string(asettings, "AudioBitrate");
424                         s_sr = ghb_settings_get_string(asettings, "AudioSamplerate");
425                         s_mix = ghb_settings_get_string(asettings, "AudioMixdown");
426                         s_drc = ghb_settings_get_double(asettings, "AudioTrackDRCSlider");
427
428                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, 
429                                 // These are displayed in list
430                                 0, track,
431                                 1, codec,
432                                 2, br,
433                                 3, sr,
434                                 4, mix,
435                                 // These are used to set combo values when an item is selected
436                                 5, drc,
437                                 6, s_track,
438                                 7, s_codec,
439                                 8, s_br,
440                                 9, s_sr,
441                                 10, s_mix,
442                                 11, s_drc,
443                                 -1);
444                         g_free(drc);
445                         g_free(s_track);
446                         g_free(s_codec);
447                         g_free(s_br);
448                         g_free(s_sr);
449                         g_free(s_mix);
450                         done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
451                         row++;
452                 } while (!done);
453         }
454 }
455
456 static gboolean
457 validate_settings(signal_user_data_t *ud)
458 {
459         // Check to see if the dest file exists or is
460         // already in the queue
461         gchar *message, *dest;
462         gint count, ii;
463         gint titleindex;
464
465         titleindex = ghb_settings_combo_int(ud->settings, "title");
466         if (titleindex < 0) return FALSE;
467         dest = ghb_settings_get_string(ud->settings, "destination");
468         count = ghb_array_len(ud->queue);
469         for (ii = 0; ii < count; ii++)
470         {
471                 GValue *js;
472                 gchar *filename;
473
474                 js = ghb_array_get_nth(ud->queue, ii);
475                 filename = ghb_settings_get_string(js, "destination");
476                 if (strcmp(dest, filename) == 0)
477                 {
478                         message = g_strdup_printf(
479                                                 "Destination: %s\n\n"
480                                                 "Another queued job has specified the same destination.\n"
481                                                 "Do you want to overwrite?",
482                                                 dest);
483                         if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Overwrite"))
484                         {
485                                 g_free(filename);
486                                 g_free(dest);
487                                 g_free(message);
488                                 return FALSE;
489                         }
490                         g_free(message);
491                         break;
492                 }
493                 g_free(filename);
494         }
495         gchar *destdir = g_path_get_dirname(dest);
496         if (!g_file_test(destdir, G_FILE_TEST_IS_DIR))
497         {
498                 message = g_strdup_printf(
499                                         "Destination: %s\n\n"
500                                         "This is not a valid directory.",
501                                         destdir);
502                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
503                 g_free(dest);
504                 g_free(message);
505                 g_free(destdir);
506                 return FALSE;
507         }
508         if (g_access(destdir, R_OK|W_OK) != 0)
509         {
510                 message = g_strdup_printf(
511                                         "Destination: %s\n\n"
512                                         "Can not read or write the directory.",
513                                         destdir);
514                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
515                 g_free(dest);
516                 g_free(message);
517                 g_free(destdir);
518                 return FALSE;
519         }
520         GFile *gfile;
521         GFileInfo *info;
522         guint64 size;
523         gchar *resolved = ghb_resolve_symlink(destdir);
524
525         gfile = g_file_new_for_path(resolved);
526         info = g_file_query_filesystem_info(gfile, 
527                                                 G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, NULL);
528         if (info != NULL)
529         {
530                 if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE))
531                 {
532                         size = g_file_info_get_attribute_uint64(info, 
533                                                                         G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
534                         
535                         gint64 fsize = 10L * 1024L * 1024L * 1024L;
536                         if (size < fsize)
537                         {
538                                 message = g_strdup_printf(
539                                                         "Destination filesystem is almost full: %uM free\n\n"
540                                                         "Encode may be incomplete if you proceed.\n",
541                                                         (guint)(size / (1024L*1024L)));
542                                 if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Proceed"))
543                                 {
544                                         g_free(dest);
545                                         g_free(message);
546                                         return FALSE;
547                                 }
548                                 g_free(message);
549                         }
550                 }
551                 g_object_unref(info);
552         }
553         g_object_unref(gfile);
554         g_free(resolved);
555         g_free(destdir);
556         if (g_file_test(dest, G_FILE_TEST_EXISTS))
557         {
558                 message = g_strdup_printf(
559                                         "Destination: %s\n\n"
560                                         "File already exhists.\n"
561                                         "Do you want to overwrite?",
562                                         dest);
563                 if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Overwrite"))
564                 {
565                         g_free(dest);
566                         g_free(message);
567                         return FALSE;
568                 }
569                 g_free(message);
570                 g_unlink(dest);
571         }
572         g_free(dest);
573         // Validate video quality is in a reasonable range
574         if (!ghb_validate_vquality(ud->settings))
575         {
576                 return FALSE;
577         }
578         // Validate audio settings
579         if (!ghb_validate_audio(ud))
580         {
581                 return FALSE;
582         }
583         // Validate video settings
584         if (!ghb_validate_video(ud))
585         {
586                 return FALSE;
587         }
588         // Validate container settings
589         if (!ghb_validate_container(ud))
590         {
591                 return FALSE;
592         }
593         // Validate filter settings
594         if (!ghb_validate_filters(ud))
595         {
596                 return FALSE;
597         }
598         audio_list_refresh(ud);
599         return TRUE;
600 }
601
602 static gboolean
603 queue_add(signal_user_data_t *ud)
604 {
605         // Add settings to the queue
606         GValue *settings;
607         gint titleindex;
608         gint titlenum;
609         
610         g_debug("queue_add ()");
611         if (!validate_settings(ud))
612         {
613                 return FALSE;
614         }
615         if (ud->queue == NULL)
616                 ud->queue = ghb_array_value_new(32);
617         // Make a copy of current settings to be used for the new job
618         settings = ghb_value_dup(ud->settings);
619         ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING);
620         ghb_settings_set_int(settings, "job_unique_id", 0);
621         titleindex = ghb_settings_combo_int(settings, "title");
622         titlenum = ghb_get_title_number(titleindex);
623         ghb_settings_set_int(settings, "titlenum", titlenum);
624         ghb_array_append(ud->queue, settings);
625         add_to_queue_list(ud, settings, NULL);
626         ghb_save_queue(ud->queue);
627
628         return TRUE;
629 }
630
631 void
632 queue_add_clicked_cb(GtkWidget *widget, signal_user_data_t *ud)
633 {
634         g_debug("queue_add_clicked_cb ()");
635         queue_add(ud);
636 }
637
638 void
639 queue_remove_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud)
640 {
641         GtkTreeView *treeview;
642         GtkTreePath *treepath;
643         GtkTreeModel *store;
644         GtkTreeIter iter;
645         gint row;
646         gint *indices;
647         gint unique_id;
648         GValue *settings;
649         gint status;
650
651         g_debug("queue_remove_clicked_cb ()");
652         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
653         store = gtk_tree_view_get_model(treeview);
654         treepath = gtk_tree_path_new_from_string (path);
655         if (gtk_tree_path_get_depth(treepath) > 1) return;
656         if (gtk_tree_model_get_iter(store, &iter, treepath))
657         {
658                 // Find the entry in the queue
659                 indices = gtk_tree_path_get_indices (treepath);
660                 row = indices[0];
661                 // Can only free the treepath After getting what I need from
662                 // indices since this points into treepath somewhere.
663                 gtk_tree_path_free (treepath);
664                 if (row < 0) return;
665                 if (row >= ghb_array_len(ud->queue))
666                         return;
667                 settings = ghb_array_get_nth(ud->queue, row);
668                 status = ghb_settings_get_int(settings, "job_status");
669                 if (status == GHB_QUEUE_RUNNING)
670                 {
671                         // Ask if wants to stop encode.
672                         if (!ghb_cancel_encode(NULL))
673                         {
674                                 return;
675                         }
676                         unique_id = ghb_settings_get_int(settings, "job_unique_id");
677                         ghb_remove_job(unique_id);
678                 }
679                 // Remove the selected item
680                 gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
681                 // Remove the corresponding item from the queue list
682                 GValue *old = ghb_array_get_nth(ud->queue, row);
683                 ghb_value_free(old);
684                 ghb_array_remove(ud->queue, row);
685                 ghb_save_queue(ud->queue);
686         }
687         else
688         {       
689                 gtk_tree_path_free (treepath);
690         }
691 }
692
693 static gint
694 find_last_finished(GValue *queue)
695 {
696         GValue *js;
697         gint ii, count;
698         gint status;
699         
700         g_debug("find_last_finished");
701         count = ghb_array_len(queue);
702         for (ii = 0; ii < count; ii++)
703         {
704                 js = ghb_array_get_nth(queue, ii);
705                 status = ghb_settings_get_int(js, "job_status");
706                 if (status != GHB_QUEUE_DONE && status != GHB_QUEUE_RUNNING)
707                 {
708                         return ii-1;
709                 }
710         }
711         return -1;
712 }
713
714 // This little bit is needed to prevent the default drag motion
715 // handler from expanding rows if you hover over them while
716 // dragging.
717 // Also controls where valid drop locations are
718 gboolean
719 queue_drag_motion_cb(
720         GtkTreeView *tv,
721         GdkDragContext *ctx,
722         gint x,
723         gint y,
724         guint time,
725         signal_user_data_t *ud)
726 {
727         GtkTreePath *path = NULL;
728         GtkTreeViewDropPosition pos;
729         gint *indices, row, status, finished;
730         GValue *js;
731         GtkTreeIter iter;
732         GtkTreeView *srctv;
733         GtkTreeModel *model;
734         GtkTreeSelection *select;
735
736         // This bit checks to see if the source is allowed to be
737         // moved.  Only pending and canceled items may be moved.
738         srctv = GTK_TREE_VIEW(gtk_drag_get_source_widget(ctx));
739         select = gtk_tree_view_get_selection (srctv);
740         gtk_tree_selection_get_selected (select, &model, &iter);
741         path = gtk_tree_model_get_path (model, &iter);
742         indices = gtk_tree_path_get_indices(path);
743         row = indices[0];
744         gtk_tree_path_free(path);
745         js = ghb_array_get_nth(ud->queue, row);
746         status = ghb_settings_get_int(js, "job_status");
747         if (status != GHB_QUEUE_PENDING && status != GHB_QUEUE_CANCELED)
748         {
749                 gdk_drag_status(ctx, 0, time);
750                 return TRUE;
751         }
752
753         // The reset checks that the destination is a valid position
754         // in the list.  Can not move above any finished or running items
755         gtk_tree_view_get_dest_row_at_pos (tv, x, y, &path, &pos);
756         if (path == NULL)
757         {
758                 gdk_drag_status(ctx, GDK_ACTION_MOVE, time);
759                 return TRUE;
760         }
761         // Don't allow *drop into*
762         if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
763                 pos = GTK_TREE_VIEW_DROP_BEFORE;
764         if (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
765                 pos = GTK_TREE_VIEW_DROP_AFTER;
766         // Don't allow droping int child items
767         if (gtk_tree_path_get_depth(path) > 1)
768         {
769                 gtk_tree_path_up(path);
770                 pos = GTK_TREE_VIEW_DROP_AFTER;
771         }
772         indices = gtk_tree_path_get_indices(path);
773         row = indices[0];
774         js = ghb_array_get_nth(ud->queue, row);
775
776         finished = find_last_finished(ud->queue);
777         if (row < finished)
778         {
779                 gtk_tree_path_free(path);
780                 gdk_drag_status(ctx, 0, time);
781                 return TRUE;
782         }
783         if (pos != GTK_TREE_VIEW_DROP_AFTER && 
784                 row == finished)
785         {
786                 gtk_tree_path_free(path);
787                 gdk_drag_status(ctx, 0, time);
788                 return TRUE;
789         }
790         gtk_tree_view_set_drag_dest_row(tv, path, pos);
791         gtk_tree_path_free(path);
792         gdk_drag_status(ctx, GDK_ACTION_MOVE, time);
793         return TRUE;
794 }
795
796 void 
797 queue_drag_cb(
798         GtkTreeView *dstwidget, 
799         GdkDragContext *dc, 
800         gint x, gint y, 
801         GtkSelectionData *selection_data, 
802         guint info, guint t, 
803         signal_user_data_t *ud)
804 {
805         GtkTreePath *path = NULL;
806         //GtkTreeModel *model;
807         GtkTreeViewDropPosition pos;
808         GtkTreeIter dstiter, srciter;
809         gint *indices, row;
810         GValue *js;
811         
812         GtkTreeModel *dstmodel = gtk_tree_view_get_model(dstwidget);
813                         
814         g_debug("queue_drag_cb ()");
815         // This doesn't work here for some reason...
816         // gtk_tree_view_get_drag_dest_row(dstwidget, &path, &pos);
817         gtk_tree_view_get_dest_row_at_pos (dstwidget, x, y, &path, &pos);
818         // This little hack is needed because attempting to drop after
819         // the last item gives us no path or pos.
820         if (path == NULL)
821         {
822                 gint n_children;
823
824                 n_children = gtk_tree_model_iter_n_children(dstmodel, NULL);
825                 if (n_children)
826                 {
827                         pos = GTK_TREE_VIEW_DROP_AFTER;
828                         path = gtk_tree_path_new_from_indices(n_children-1, -1);
829                 }
830                 else
831                 {
832                         pos = GTK_TREE_VIEW_DROP_BEFORE;
833                         path = gtk_tree_path_new_from_indices(0, -1);
834                 }
835         }
836         if (path)
837         {
838                 if (gtk_tree_path_get_depth(path) > 1)
839                         gtk_tree_path_up(path);
840                 if (gtk_tree_model_get_iter (dstmodel, &dstiter, path))
841                 {
842                         GtkTreeIter iter;
843                         GtkTreeView *srcwidget;
844                         GtkTreeModel *srcmodel;
845                         GtkTreeSelection *select;
846                         GtkTreePath *srcpath = NULL;
847                         GtkTreePath *dstpath = NULL;
848
849                         srcwidget = GTK_TREE_VIEW(gtk_drag_get_source_widget(dc));
850                         //srcmodel = gtk_tree_view_get_model(srcwidget);
851                         select = gtk_tree_view_get_selection (srcwidget);
852                         gtk_tree_selection_get_selected (select, &srcmodel, &srciter);
853
854                         srcpath = gtk_tree_model_get_path (srcmodel, &srciter);
855                         indices = gtk_tree_path_get_indices(srcpath);
856                         row = indices[0];
857                         gtk_tree_path_free(srcpath);
858                         js = ghb_array_get_nth(ud->queue, row);
859
860                         switch (pos)
861                         {
862                                 case GTK_TREE_VIEW_DROP_BEFORE:
863                                 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
864                                         gtk_tree_store_insert_before (GTK_TREE_STORE (dstmodel), 
865                                                                                                         &iter, NULL, &dstiter);
866                                         break;
867
868                                 case GTK_TREE_VIEW_DROP_AFTER:
869                                 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
870                                         gtk_tree_store_insert_after (GTK_TREE_STORE (dstmodel), 
871                                                                                                         &iter, NULL, &dstiter);
872                                         break;
873
874                                 default:
875                                         break;
876                         }
877                         // Reset job to pending
878                         ghb_settings_set_int(js, "job_status", GHB_QUEUE_PENDING);
879                         add_to_queue_list(ud, js, &iter);
880
881                         dstpath = gtk_tree_model_get_path (dstmodel, &iter);
882                         indices = gtk_tree_path_get_indices(dstpath);
883                         row = indices[0];
884                         gtk_tree_path_free(dstpath);
885                         ghb_array_insert(ud->queue, row, js);
886
887                         srcpath = gtk_tree_model_get_path (srcmodel, &srciter);
888                         indices = gtk_tree_path_get_indices(srcpath);
889                         row = indices[0];
890                         gtk_tree_path_free(srcpath);
891                         ghb_array_remove(ud->queue, row);
892                         gtk_tree_store_remove (GTK_TREE_STORE (srcmodel), &srciter);
893                         ghb_save_queue(ud->queue);
894                 }
895                 gtk_tree_path_free(path);
896         }
897 }
898
899 void
900 ghb_queue_buttons_grey(signal_user_data_t *ud, gboolean working)
901 {
902         GtkWidget *widget;
903         GtkAction *action;
904         gint queue_count;
905         gint titleindex;
906         gboolean title_ok;
907
908         queue_count = ghb_array_len(ud->queue);
909         titleindex = ghb_settings_combo_int(ud->settings, "title");
910         title_ok = (titleindex >= 0);
911
912         widget = GHB_WIDGET (ud->builder, "queue_start1");
913         gtk_widget_set_sensitive (widget, !working && (title_ok || queue_count));
914         widget = GHB_WIDGET (ud->builder, "queue_start2");
915         gtk_widget_set_sensitive (widget, !working && (title_ok || queue_count));
916         action = GHB_ACTION (ud->builder, "queue_start_menu");
917         gtk_action_set_sensitive (action, !working && (title_ok || queue_count));
918         widget = GHB_WIDGET (ud->builder, "queue_pause1");
919         gtk_widget_set_sensitive (widget, working);
920         widget = GHB_WIDGET (ud->builder, "queue_pause2");
921         gtk_widget_set_sensitive (widget, working);
922         action = GHB_ACTION (ud->builder, "queue_pause_menu");
923         gtk_action_set_sensitive (action, working);
924         widget = GHB_WIDGET (ud->builder, "queue_stop");
925         gtk_widget_set_sensitive (widget, working);
926         action = GHB_ACTION (ud->builder, "queue_stop_menu");
927         gtk_action_set_sensitive (action, working);
928 }
929
930 void
931 queue_list_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, GtkCellRenderer *cell)
932 {
933         GtkTreeViewColumn *column;
934         gint width;
935         
936         column = gtk_tree_view_get_column (GTK_TREE_VIEW(widget), 0);
937         width = gtk_tree_view_column_get_width(column);
938         g_debug("col width %d alloc width %d", width, allocation->width);
939         // Set new wrap-width.  Shave a little off to accomidate the icons
940         // that share this column.
941         if (width >= 564) // Don't allow below a certain size
942                 g_object_set(cell, "wrap-width", width-70, NULL);
943 }
944
945 void
946 queue_start_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
947 {
948         GValue *js;
949         gboolean running = FALSE;
950         gint count, ii;
951         gint status;
952         gint state;
953
954         count = ghb_array_len(ud->queue);
955         for (ii = 0; ii < count; ii++)
956         {
957                 js = ghb_array_get_nth(ud->queue, ii);
958                 status = ghb_settings_get_int(js, "job_status");
959                 if ((status == GHB_QUEUE_RUNNING) || 
960                         (status == GHB_QUEUE_PENDING))
961                 {
962                         running = TRUE;
963                         break;
964                 }
965         }
966         if (!running)
967         {
968                 // The queue has no running or pending jobs.
969                 // Add current settings to the queue, then run.
970                 if (!queue_add(ud))
971                         return;
972         }
973         state = ghb_get_queue_state();
974         if (state == GHB_STATE_IDLE)
975         {
976                 // Add the first pending queue item and start
977                 ud->current_job = ghb_start_next_job(ud, TRUE);
978         }
979 }
980
981 void
982 queue_stop_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
983 {
984         ud->cancel_encode = TRUE;
985         ghb_cancel_encode(NULL);
986 }
987
988 void
989 queue_pause_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
990 {
991         ghb_pause_queue();
992 }
993
994 gboolean
995 ghb_reload_queue(signal_user_data_t *ud)
996 {
997         GValue *queue;
998         gint unfinished = 0;
999         gint count, ii;
1000         gint status;
1001         GValue *settings;
1002         gchar *message;
1003
1004         g_debug("ghb_reload_queue");
1005         queue = ghb_load_queue();
1006         // Look for unfinished entries
1007         count = ghb_array_len(queue);
1008         for (ii = 0; ii < count; ii++)
1009         {
1010                 settings = ghb_array_get_nth(queue, ii);
1011                 status = ghb_settings_get_int(settings, "job_status");
1012                 if (status != GHB_QUEUE_DONE && status != GHB_QUEUE_CANCELED)
1013                 {
1014                         unfinished++;
1015                 }
1016         }
1017         if (unfinished)
1018         {
1019                 message = g_strdup_printf(
1020                                         "You have %d unfinished job%s in a saved queue.\n\n"
1021                                         "Would you like to reload %s?",
1022                                         unfinished, 
1023                                         (unfinished > 1) ? "s" : "",
1024                                         (unfinished > 1) ? "them" : "it");
1025                 if (ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "No", "Yes"))
1026                 {
1027                         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
1028                         gtk_widget_show (widget);
1029
1030                         ud->queue = queue;
1031                         // First get rid of any old items we don't want
1032                         for (ii = count-1; ii >= 0; ii--)
1033                         {
1034                                 settings = ghb_array_get_nth(queue, ii);
1035                                 status = ghb_settings_get_int(settings, "job_status");
1036                                 if (status == GHB_QUEUE_DONE || status == GHB_QUEUE_CANCELED)
1037                                 {
1038                                         GValue *old = ghb_array_get_nth(queue, ii);
1039                                         ghb_value_free(old);
1040                                         ghb_array_remove(queue, ii);
1041                                 }
1042                         }
1043                         count = ghb_array_len(queue);
1044                         for (ii = 0; ii < count; ii++)
1045                         {
1046                                 settings = ghb_array_get_nth(queue, ii);
1047                                 ghb_settings_set_int(settings, "job_unique_id", 0);
1048                                 ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING);
1049                                 add_to_queue_list(ud, settings, NULL);
1050                         }
1051                         ghb_queue_buttons_grey(ud, FALSE);
1052                 }
1053                 else
1054                 {
1055                         ghb_value_free(queue);
1056                         ghb_remove_queue_file();
1057                 }
1058                 g_free(message);
1059         }
1060         return FALSE;
1061 }
1062
1063 gboolean 
1064 queue_key_press_cb(
1065         GtkWidget *widget, 
1066         GdkEventKey *event,
1067         signal_user_data_t *ud)
1068 {
1069         GtkTreeView *treeview;
1070         GtkTreeSelection *selection;
1071         GtkTreeModel *store;
1072         GtkTreeIter iter;
1073         gint row;
1074         gint *indices;
1075         gint unique_id;
1076         GValue *settings;
1077         gint status;
1078
1079         g_debug("queue_key_press_cb ()");
1080         if (event->keyval != GDK_Delete)
1081                 return FALSE;
1082         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1083         store = gtk_tree_view_get_model(treeview);
1084
1085         selection = gtk_tree_view_get_selection (treeview);
1086         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1087         {
1088                 GtkTreePath *treepath;
1089
1090                 treepath = gtk_tree_model_get_path (store, &iter);
1091                 // Find the entry in the queue
1092                 indices = gtk_tree_path_get_indices (treepath);
1093                 row = indices[0];
1094                 // Can only free the treepath After getting what I need from
1095                 // indices since this points into treepath somewhere.
1096                 gtk_tree_path_free (treepath);
1097                 if (row < 0) return FALSE;
1098                 if (row >= ghb_array_len(ud->queue))
1099                         return FALSE;
1100                 settings = ghb_array_get_nth(ud->queue, row);
1101                 status = ghb_settings_get_int(settings, "job_status");
1102                 if (status == GHB_QUEUE_RUNNING)
1103                 {
1104                         // Ask if wants to stop encode.
1105                         if (!ghb_cancel_encode(NULL))
1106                         {
1107                                 return TRUE;
1108                         }
1109                         unique_id = ghb_settings_get_int(settings, "job_unique_id");
1110                         ghb_remove_job(unique_id);
1111                 }
1112                 // Remove the selected item
1113                 gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1114                 // Remove the corresponding item from the queue list
1115                 GValue *old = ghb_array_get_nth(ud->queue, row);
1116                 ghb_value_free(old);
1117                 ghb_array_remove(ud->queue, row);
1118                 ghb_save_queue(ud->queue);
1119                 return TRUE;
1120         }
1121         return FALSE;
1122 }
1123
1124 GValue *ghb_queue_edit_settings = NULL;
1125
1126 void
1127 queue_edit_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1128 {
1129         GtkTreeView *treeview;
1130         GtkTreeSelection *selection;
1131         GtkTreeModel *store;
1132         GtkTreeIter iter;
1133         gint row;
1134         gint *indices;
1135         gint status;
1136
1137         g_debug("queue_key_press_cb ()");
1138         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1139         store = gtk_tree_view_get_model(treeview);
1140
1141         selection = gtk_tree_view_get_selection (treeview);
1142         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1143         {
1144                 GtkTreePath *treepath;
1145
1146                 treepath = gtk_tree_model_get_path (store, &iter);
1147                 // Find the entry in the queue
1148                 indices = gtk_tree_path_get_indices (treepath);
1149                 row = indices[0];
1150                 // Can only free the treepath After getting what I need from
1151                 // indices since this points into treepath somewhere.
1152                 gtk_tree_path_free (treepath);
1153                 if (row < 0) return;
1154                 if (row >= ghb_array_len(ud->queue))
1155                         return;
1156                 ghb_queue_edit_settings = ghb_array_get_nth(ud->queue, row);
1157                 status = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
1158                 if (status == GHB_QUEUE_PENDING)
1159                 {
1160                         // Remove the selected item
1161                         gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1162                         // Remove the corresponding item from the queue list
1163                         ghb_array_remove(ud->queue, row);
1164                 }
1165                 gchar *source;
1166                 source = ghb_settings_get_string(ghb_queue_edit_settings, "source");
1167                 ghb_do_scan(ud, source, FALSE);
1168                 g_free(source);
1169         }
1170 }
1171