OSDN Git Service

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