OSDN Git Service

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