OSDN Git Service

LinGui: Picture Filter enhancements
[handbrake-jp/handbrake-jp-git.git] / gtk / src / queuehandler.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * callbacks.c
4  * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
5  * 
6  * callbacks.c is free software.
7  * 
8  * You may redistribute it and/or modify it under the terms of the
9  * GNU General Public License, as published by the Free Software
10  * Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  */
13
14 #include <gtk/gtk.h>
15 #include <gdk/gdkkeysyms.h>
16 #include <glib/gstdio.h>
17 #include <gio/gio.h>
18 #include "hb.h"
19 #include "settings.h"
20 #include "hb-backend.h"
21 #include "values.h"
22 #include "callbacks.h"
23 #include "presets.h"
24 #include "ghb-dvd.h"
25
26 void
27 queue_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
28 {
29         GtkTreeModel *store;
30         GtkTreeIter iter, piter;
31         
32         g_debug("queue_list_selection_changed_cb ()");
33         // A queue entry is made up of a parent and multiple
34         // children that are visible when expanded.  When and entry
35         // is selected, I want the parent to be selected.
36         // This is purely cosmetic.
37         if (gtk_tree_selection_get_selected(selection, &store, &iter))
38         {
39                 GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_edit");
40                 gtk_widget_set_sensitive (widget, TRUE);
41                 if (gtk_tree_model_iter_parent (store, &piter, &iter))
42                 {
43                         GtkTreePath *path;
44                         GtkTreeView *treeview;
45                         
46                         gtk_tree_selection_select_iter (selection, &piter);
47                         path = gtk_tree_model_get_path (store, &piter);
48                         treeview = gtk_tree_selection_get_tree_view (selection);
49                         // Make the parent visible in scroll window if it is not.
50                         gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0);
51                         gtk_tree_path_free(path);
52                 }
53         }
54         else
55         {
56                 GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_edit");
57                 gtk_widget_set_sensitive (widget, FALSE);
58         }
59 }
60
61 static void
62 add_to_queue_list(signal_user_data_t *ud, GValue *settings, GtkTreeIter *piter)
63 {
64         GtkTreeView *treeview;
65         GtkTreeIter iter;
66         GtkTreeStore *store;
67         gchar *info;
68         gint status;
69         GtkTreeIter citer;
70         gchar *dest, *preset, *vol_name, *basename;
71         const gchar *vcodec, *container;
72         gchar *fps, *vcodec_abbr;
73         gint title, start_chapter, end_chapter, width, height;
74         gint source_width, source_height;
75         gboolean pass2, anamorphic, round_dim, keep_aspect, vqtype, turbo;
76         gboolean tweaks;
77         gchar *escape;
78         
79         g_debug("update_queue_list ()");
80         if (settings == NULL) return;
81         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
82         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
83                 
84         tweaks = ghb_settings_get_boolean(settings, "allow_tweaks");
85         title = ghb_settings_get_int(settings, "titlenum");
86         start_chapter = ghb_settings_get_int(settings, "start_chapter");
87         end_chapter = ghb_settings_get_int(settings, "end_chapter");
88         pass2 = ghb_settings_get_boolean(settings, "VideoTwoPass");
89         vol_name = ghb_settings_get_string(settings, "volume_label");
90         dest = ghb_settings_get_string(settings, "destination");
91         basename = g_path_get_basename(dest);
92         escape = g_markup_escape_text(basename, -1);
93         info = g_strdup_printf 
94         (
95                 "<big><b>%s</b></big> "
96                 "<small>(Title %d, Chapters %d through %d, %d Video %s)"
97                 " --> %s</small>",
98                  vol_name, title, start_chapter, end_chapter, 
99                  pass2 ? 2:1, pass2 ? "Passes":"Pass", escape
100         );
101         g_free(basename);
102         g_free(escape);
103
104         if (piter)
105                 iter = *piter;
106         else
107                 gtk_tree_store_append(store, &iter, NULL);
108
109         gtk_tree_store_set(store, &iter, 1, info, 2, "hb-queue-delete", -1);
110         g_free(info);
111         status = ghb_settings_get_int(settings, "job_status");
112         switch (status)
113         {
114                 case GHB_QUEUE_PENDING:
115                         gtk_tree_store_set(store, &iter, 0, "hb-queue-job", -1);
116                         break;
117                 case GHB_QUEUE_CANCELED:
118                         gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
119                         break;
120                 case GHB_QUEUE_RUNNING:
121                         gtk_tree_store_set(store, &iter, 0, "hb-working0", -1);
122                         break;
123                 case GHB_QUEUE_DONE:
124                         gtk_tree_store_set(store, &iter, 0, "hb-complete", -1);
125                         break;
126                 default:
127                         gtk_tree_store_set(store, &iter, 0, "hb-queue-job", -1);
128                         break;
129         }
130
131         GString *str = g_string_new("");
132         gboolean markers;
133         gboolean preset_modified;
134         gint mux;
135         const GValue *path;
136
137         container = ghb_settings_combo_option(settings, "FileFormat");
138         mux = ghb_settings_combo_int(settings, "FileFormat");
139         preset_modified = ghb_settings_get_boolean(settings, "preset_modified");
140         path = ghb_settings_get_value(settings, "preset");
141         preset = ghb_preset_path_string(path);
142         markers = ghb_settings_get_boolean(settings, "ChapterMarkers");
143
144         if (preset_modified)
145                 g_string_append_printf(str, 
146                         "<b>Modified Preset Based On:</b> <small>%s</small>\n", 
147                         preset);
148         else
149                 g_string_append_printf(str, 
150                         "<b>Preset:</b> <small>%s</small>\n", 
151                         preset);
152
153         if (markers)
154         {
155                 g_string_append_printf(str, 
156                         "<b>Format:</b> <small>%s Container, Chapter Markers</small>\n", 
157                         container);
158         }
159         else
160         {
161                 g_string_append_printf(str, 
162                         "<b>Format:</b> <small>%s Container</small>\n", container);
163         }
164         if (mux == HB_MUX_MP4)
165         {
166                 gboolean ipod, http, large;
167
168                 ipod = ghb_settings_get_boolean(settings, "Mp4iPodCompatible");
169                 http = ghb_settings_get_boolean(settings, "Mp4HttpOptimize");
170                 large = ghb_settings_get_boolean(settings, "Mp4LargeFile");
171                 if (http || ipod || large)
172                 {
173                         g_string_append_printf(str, "<b>MP4 Options:</b><small>");
174                         if (ipod)
175                                 g_string_append_printf(str, " - iPod 5G Support");
176                         if (http)
177                                 g_string_append_printf(str, " - Web Optimized");
178                         if (large)
179                                 g_string_append_printf(str, " - Large File Size (>4GB)");
180                         g_string_append_printf(str, "</small>\n");
181                 }
182         }
183         escape = g_markup_escape_text(dest, -1);
184         g_string_append_printf(str, 
185                 "<b>Destination:</b> <small>%s</small>\n", escape);
186
187         width = ghb_settings_get_int(settings, "scale_width");
188         height = ghb_settings_get_int(settings, "scale_height");
189         anamorphic = ghb_settings_get_boolean(settings, "anamorphic");
190         round_dim = ghb_settings_get_boolean(settings, "ModDimensions");
191         keep_aspect = ghb_settings_get_boolean(settings, "PictureKeepRatio");
192
193         gchar *aspect_desc;
194         if (anamorphic)
195         {
196                 if (round_dim)
197                 {
198                         aspect_desc = "(Anamorphic)";
199                 }
200                 else
201                 {
202                         aspect_desc = "(Strict Anamorphic)";
203                 }
204         }
205         else
206         {
207                 if (keep_aspect)
208                 {
209                         aspect_desc = "(Aspect Preserved)";
210                 }
211                 else
212                 {
213                         aspect_desc = "(Aspect Lost)";
214                 }
215         }
216         vqtype = ghb_settings_get_boolean(settings, "vquality_type_constant");
217
218         gchar *vq_desc = "Error";
219         gchar *vq_units = "";
220         gchar *vqstr;
221         gdouble vqvalue;
222         if (!vqtype)
223         {
224                 vqtype = ghb_settings_get_boolean(settings, "vquality_type_target");
225                 if (!vqtype)
226                 {
227                         // Has to be bitrate
228                         vqvalue = ghb_settings_get_int(settings, "VideoAvgBitrate");
229                         vq_desc = "Bitrate:";
230                         vq_units = "kbps";
231                 }
232                 else
233                 {
234                         // Target file size
235                         vqvalue = ghb_settings_get_int(settings, "VideoTargetSize");
236                         vq_desc = "Target Size:";
237                         vq_units = "MB";
238                 }
239                 vqstr = g_strdup_printf("%d", (gint)vqvalue);
240         }
241         else
242         {
243                 // Constant quality
244                 vqvalue = ghb_settings_get_double(settings, "VideoQualitySlider");
245                 vq_desc = "Constant Quality:";
246                 if (ghb_settings_get_boolean(settings, "directqp"))
247                 {
248                         vqstr = g_strdup_printf("%d", (gint)vqvalue);
249                         vq_units = "(crf)";
250                 }
251                 else
252                 {
253                         vqstr = g_strdup_printf("%.1f", 100*vqvalue);
254                         vq_units = "%";
255                 }
256         }
257         fps = ghb_settings_get_string(settings, "VideoFramerate");
258         if (strcmp("source", fps) == 0)
259         {
260                 g_free(fps);
261                 if (ghb_settings_combo_int(settings, "PictureDetelecine"))
262                         fps = g_strdup("Same As Source (vfr detelecine)");
263                 else
264                         fps = g_strdup("Same As Source (variable)");
265         }
266         else
267         {
268                 gchar *tmp;
269                 tmp = g_strdup_printf("%s (constant frame rate)", fps);
270                 g_free(fps);
271                 fps = tmp;
272         }
273         vcodec = ghb_settings_combo_option(settings, "VideoEncoder");
274         vcodec_abbr = ghb_settings_get_string(settings, "VideoEncoder");
275         source_width = ghb_settings_get_int(settings, "source_width");
276         source_height = ghb_settings_get_int(settings, "source_height");
277         g_string_append_printf(str,
278                 "<b>Picture:</b> Source: <small>%d x %d, Output %d x %d %s</small>\n",
279                  source_width, source_height, width, height, aspect_desc);
280
281         gboolean decomb;
282         gboolean filters = FALSE;
283
284         decomb = ghb_settings_combo_int(settings, "PictureDecomb");
285         g_string_append_printf(str, "<b>Filters:</b><small>");
286         if (ghb_settings_combo_int(settings, "PictureDetelecine"))
287         {
288                 g_string_append_printf(str, " - Detelecine");
289                 filters = TRUE;
290         }
291         if (decomb)
292         {
293                 g_string_append_printf(str, " - Decomb");
294                 filters = TRUE;
295         }
296         else
297         {
298                 gint deint = ghb_settings_combo_int(settings, "PictureDeinterlace");
299                 if (deint)
300                 {
301                         const gchar *opt = ghb_settings_combo_option(settings,
302                                                                                                         "PictureDeinterlace");
303                         g_string_append_printf(str, " - Deinterlace: %s", opt);
304                         filters = TRUE;
305                 }
306         }
307         gint denoise = ghb_settings_combo_int(settings, "PictureDenoise");
308         if (denoise)
309         {
310                 const gchar *opt = ghb_settings_combo_option(settings,
311                                                                                                         "PictureDenoise");
312                 g_string_append_printf(str, " - Denoise: %s", opt);
313                 filters = TRUE;
314         }
315         gint deblock = ghb_settings_get_int(settings, "PictureDeblock");
316         if (deblock >= 5)
317         {
318                 g_string_append_printf(str, " - Deblock (%d)", deblock);
319                 filters = TRUE;
320         }
321         if (ghb_settings_get_boolean(settings, "VideoGrayScale"))
322         {
323                 g_string_append_printf(str, " - Grayscale");
324                 filters = TRUE;
325         }
326         if (!filters)
327                 g_string_append_printf(str, " None");
328         g_string_append_printf(str, "</small>\n");
329
330         g_string_append_printf(str,
331                 "<b>Video:</b> <small>%s, Framerate: %s, %s %s%s</small>\n",
332                  vcodec, fps, vq_desc, vqstr, vq_units);
333
334         turbo = ghb_settings_get_boolean(settings, "VideoTurboTwoPass");
335         if (turbo)
336         {
337                 g_string_append_printf(str, "<b>Turbo:</b> <small>On</small>\n");
338         }
339         if (strcmp(vcodec_abbr, "x264") == 0)
340         {
341                 gchar *x264opts = ghb_build_x264opts_string(settings);
342                 g_string_append_printf(str, 
343                         "<b>x264 Options:</b> <small>%s</small>\n", x264opts);
344                 g_free(x264opts);
345         }
346         // Add the audios
347         gint count, ii;
348         const GValue *audio_list;
349
350         audio_list = ghb_settings_get_value(settings, "audio_list");
351         count = ghb_array_len(audio_list);
352         for (ii = 0; ii < count; ii++)
353         {
354                 gchar *bitrate, *samplerate, *track;
355                 const gchar *acodec, *mix;
356                 GValue *asettings;
357
358                 asettings = ghb_array_get_nth(audio_list, ii);
359
360                 acodec = ghb_settings_combo_option(asettings, "AudioEncoder");
361                 bitrate = ghb_settings_get_string(asettings, "AudioBitrate");
362                 samplerate = ghb_settings_get_string(asettings, "AudioSamplerate");
363                 if (strcmp("source", samplerate) == 0)
364                 {
365                         g_free(samplerate);
366                         samplerate = g_strdup("Same As Source");
367                 }
368                 track = ghb_settings_get_string(asettings, "AudioTrackDescription");
369                 mix = ghb_settings_combo_option(asettings, "AudioMixdown");
370                 g_string_append_printf(str,
371                         "<b>Audio:</b><small> %s, Encoder: %s, Mixdown: %s, SampleRate: %s, Bitrate: %s</small>",
372                          track, acodec, mix, samplerate, bitrate);
373                 if (ii < count-1)
374                         g_string_append_printf(str, "\n");
375                 g_free(track);
376                 g_free(bitrate);
377                 g_free(samplerate);
378         }
379         info = g_string_free(str, FALSE);
380         gtk_tree_store_append(store, &citer, &iter);
381         gtk_tree_store_set(store, &citer, 1, info, -1);
382         g_free(info);
383         g_free(fps);
384         g_free(vcodec_abbr);
385         g_free(vol_name);
386         g_free(dest);
387         g_free(preset);
388 }
389
390 void
391 audio_list_refresh(signal_user_data_t *ud)
392 {
393         GtkTreeView *treeview;
394         GtkTreeIter iter;
395         GtkListStore *store;
396         gboolean done;
397         gint row = 0;
398         const GValue *audio_list;
399
400         g_debug("ghb_audio_list_refresh ()");
401         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list"));
402         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
403         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
404         {
405                 do
406                 {
407                         const gchar *track, *codec, *br, *sr, *mix;
408                         gchar *drc, *s_track, *s_codec, *s_br, *s_sr, *s_mix;
409                         gdouble s_drc;
410                         GValue *asettings;
411
412                         audio_list = ghb_settings_get_value(ud->settings, "audio_list");
413                         if (row >= ghb_array_len(audio_list))
414                                 return;
415                         asettings = ghb_array_get_nth(audio_list, row);
416
417                         track = ghb_settings_combo_option(asettings, "AudioTrack");
418                         codec = ghb_settings_combo_option(asettings, "AudioEncoder");
419                         br = ghb_settings_combo_option(asettings, "AudioBitrate");
420                         sr = ghb_settings_combo_option(asettings, "AudioSamplerate");
421                         mix = ghb_settings_combo_option(asettings, "AudioMixdown");
422                         drc = ghb_settings_get_string(asettings, "AudioTrackDRCSlider");
423
424                         s_track = ghb_settings_get_string(asettings, "AudioTrack");
425                         s_codec = ghb_settings_get_string(asettings, "AudioEncoder");
426                         s_br = ghb_settings_get_string(asettings, "AudioBitrate");
427                         s_sr = ghb_settings_get_string(asettings, "AudioSamplerate");
428                         s_mix = ghb_settings_get_string(asettings, "AudioMixdown");
429                         s_drc = ghb_settings_get_double(asettings, "AudioTrackDRCSlider");
430
431                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, 
432                                 // These are displayed in list
433                                 0, track,
434                                 1, codec,
435                                 2, br,
436                                 3, sr,
437                                 4, mix,
438                                 // These are used to set combo values when an item is selected
439                                 5, drc,
440                                 6, s_track,
441                                 7, s_codec,
442                                 8, s_br,
443                                 9, s_sr,
444                                 10, s_mix,
445                                 11, s_drc,
446                                 -1);
447                         g_free(drc);
448                         g_free(s_track);
449                         g_free(s_codec);
450                         g_free(s_br);
451                         g_free(s_sr);
452                         g_free(s_mix);
453                         done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
454                         row++;
455                 } while (!done);
456         }
457 }
458
459 static gboolean
460 validate_settings(signal_user_data_t *ud)
461 {
462         // Check to see if the dest file exists or is
463         // already in the queue
464         gchar *message, *dest;
465         gint count, ii;
466         gint titleindex;
467
468         titleindex = ghb_settings_combo_int(ud->settings, "title");
469         if (titleindex < 0) return FALSE;
470         dest = ghb_settings_get_string(ud->settings, "destination");
471         count = ghb_array_len(ud->queue);
472         for (ii = 0; ii < count; ii++)
473         {
474                 GValue *js;
475                 gchar *filename;
476
477                 js = ghb_array_get_nth(ud->queue, ii);
478                 filename = ghb_settings_get_string(js, "destination");
479                 if (strcmp(dest, filename) == 0)
480                 {
481                         message = g_strdup_printf(
482                                                 "Destination: %s\n\n"
483                                                 "Another queued job has specified the same destination.\n"
484                                                 "Do you want to overwrite?",
485                                                 dest);
486                         if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Overwrite"))
487                         {
488                                 g_free(filename);
489                                 g_free(dest);
490                                 g_free(message);
491                                 return FALSE;
492                         }
493                         g_free(message);
494                         break;
495                 }
496                 g_free(filename);
497         }
498         gchar *destdir = g_path_get_dirname(dest);
499         if (!g_file_test(destdir, G_FILE_TEST_IS_DIR))
500         {
501                 message = g_strdup_printf(
502                                         "Destination: %s\n\n"
503                                         "This is not a valid directory.",
504                                         destdir);
505                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
506                 g_free(dest);
507                 g_free(message);
508                 g_free(destdir);
509                 return FALSE;
510         }
511         if (g_access(destdir, R_OK|W_OK) != 0)
512         {
513                 message = g_strdup_printf(
514                                         "Destination: %s\n\n"
515                                         "Can not read or write the directory.",
516                                         destdir);
517                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
518                 g_free(dest);
519                 g_free(message);
520                 g_free(destdir);
521                 return FALSE;
522         }
523         GFile *gfile;
524         GFileInfo *info;
525         guint64 size;
526         gchar *resolved = ghb_resolve_symlink(destdir);
527
528         gfile = g_file_new_for_path(resolved);
529         info = g_file_query_filesystem_info(gfile, 
530                                                 G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, NULL);
531         if (info != NULL)
532         {
533                 if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE))
534                 {
535                         size = g_file_info_get_attribute_uint64(info, 
536                                                                         G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
537                         
538                         gint64 fsize = (guint64)10 * 1024 * 1024 * 1024;
539                         if (size < fsize)
540                         {
541                                 message = g_strdup_printf(
542                                                         "Destination filesystem is almost full: %uM free\n\n"
543                                                         "Encode may be incomplete if you proceed.\n",
544                                                         (guint)(size / (1024L*1024L)));
545                                 if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Proceed"))
546                                 {
547                                         g_free(dest);
548                                         g_free(message);
549                                         return FALSE;
550                                 }
551                                 g_free(message);
552                         }
553                 }
554                 g_object_unref(info);
555         }
556         g_object_unref(gfile);
557         g_free(resolved);
558         g_free(destdir);
559         if (g_file_test(dest, G_FILE_TEST_EXISTS))
560         {
561                 message = g_strdup_printf(
562                                         "Destination: %s\n\n"
563                                         "File already exhists.\n"
564                                         "Do you want to overwrite?",
565                                         dest);
566                 if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Overwrite"))
567                 {
568                         g_free(dest);
569                         g_free(message);
570                         return FALSE;
571                 }
572                 g_free(message);
573                 g_unlink(dest);
574         }
575         g_free(dest);
576         // Validate video quality is in a reasonable range
577         if (!ghb_validate_vquality(ud->settings))
578         {
579                 return FALSE;
580         }
581         // Validate audio settings
582         if (!ghb_validate_audio(ud))
583         {
584                 return FALSE;
585         }
586         // Validate video settings
587         if (!ghb_validate_video(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
1004         // I really shouldn't have to do this, but at startup the
1005         // initial window size is larger than it should be.  This
1006         // make it adjust to the proper size.
1007         GtkWindow *hb_window;
1008         hb_window = GTK_WINDOW(GHB_WIDGET (ud->builder, "hb_window"));
1009         gtk_window_resize(hb_window, 16, 16);
1010
1011         queue = ghb_load_queue();
1012         // Look for unfinished entries
1013         count = ghb_array_len(queue);
1014         for (ii = 0; ii < count; ii++)
1015         {
1016                 settings = ghb_array_get_nth(queue, ii);
1017                 status = ghb_settings_get_int(settings, "job_status");
1018                 if (status != GHB_QUEUE_DONE && status != GHB_QUEUE_CANCELED)
1019                 {
1020                         unfinished++;
1021                 }
1022         }
1023         if (unfinished)
1024         {
1025                 message = g_strdup_printf(
1026                                         "You have %d unfinished job%s in a saved queue.\n\n"
1027                                         "Would you like to reload %s?",
1028                                         unfinished, 
1029                                         (unfinished > 1) ? "s" : "",
1030                                         (unfinished > 1) ? "them" : "it");
1031                 if (ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "No", "Yes"))
1032                 {
1033                         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
1034                         gtk_widget_show (widget);
1035
1036                         ud->queue = queue;
1037                         // First get rid of any old items we don't want
1038                         for (ii = count-1; ii >= 0; ii--)
1039                         {
1040                                 settings = ghb_array_get_nth(queue, ii);
1041                                 status = ghb_settings_get_int(settings, "job_status");
1042                                 if (status == GHB_QUEUE_DONE || status == GHB_QUEUE_CANCELED)
1043                                 {
1044                                         GValue *old = ghb_array_get_nth(queue, ii);
1045                                         ghb_value_free(old);
1046                                         ghb_array_remove(queue, ii);
1047                                 }
1048                         }
1049                         count = ghb_array_len(queue);
1050                         for (ii = 0; ii < count; ii++)
1051                         {
1052                                 settings = ghb_array_get_nth(queue, ii);
1053                                 ghb_settings_set_int(settings, "job_unique_id", 0);
1054                                 ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING);
1055                                 add_to_queue_list(ud, settings, NULL);
1056                         }
1057                         ghb_queue_buttons_grey(ud, FALSE);
1058                 }
1059                 else
1060                 {
1061                         ghb_value_free(queue);
1062                         ghb_remove_queue_file();
1063                 }
1064                 g_free(message);
1065         }
1066         return FALSE;
1067 }
1068
1069 gboolean 
1070 queue_key_press_cb(
1071         GtkWidget *widget, 
1072         GdkEventKey *event,
1073         signal_user_data_t *ud)
1074 {
1075         GtkTreeView *treeview;
1076         GtkTreeSelection *selection;
1077         GtkTreeModel *store;
1078         GtkTreeIter iter;
1079         gint row;
1080         gint *indices;
1081         gint unique_id;
1082         GValue *settings;
1083         gint status;
1084
1085         g_debug("queue_key_press_cb ()");
1086         if (event->keyval != GDK_Delete)
1087                 return FALSE;
1088         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1089         store = gtk_tree_view_get_model(treeview);
1090
1091         selection = gtk_tree_view_get_selection (treeview);
1092         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1093         {
1094                 GtkTreePath *treepath;
1095
1096                 treepath = gtk_tree_model_get_path (store, &iter);
1097                 // Find the entry in the queue
1098                 indices = gtk_tree_path_get_indices (treepath);
1099                 row = indices[0];
1100                 // Can only free the treepath After getting what I need from
1101                 // indices since this points into treepath somewhere.
1102                 gtk_tree_path_free (treepath);
1103                 if (row < 0) return FALSE;
1104                 if (row >= ghb_array_len(ud->queue))
1105                         return FALSE;
1106                 settings = ghb_array_get_nth(ud->queue, row);
1107                 status = ghb_settings_get_int(settings, "job_status");
1108                 if (status == GHB_QUEUE_RUNNING)
1109                 {
1110                         // Ask if wants to stop encode.
1111                         if (!ghb_cancel_encode(NULL))
1112                         {
1113                                 return TRUE;
1114                         }
1115                         unique_id = ghb_settings_get_int(settings, "job_unique_id");
1116                         ghb_remove_job(unique_id);
1117                 }
1118                 // Remove the selected item
1119                 gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1120                 // Remove the corresponding item from the queue list
1121                 GValue *old = ghb_array_get_nth(ud->queue, row);
1122                 ghb_value_free(old);
1123                 ghb_array_remove(ud->queue, row);
1124                 ghb_save_queue(ud->queue);
1125                 return TRUE;
1126         }
1127         return FALSE;
1128 }
1129
1130 GValue *ghb_queue_edit_settings = NULL;
1131
1132 void
1133 queue_edit_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1134 {
1135         GtkTreeView *treeview;
1136         GtkTreeSelection *selection;
1137         GtkTreeModel *store;
1138         GtkTreeIter iter;
1139         gint row;
1140         gint *indices;
1141         gint status;
1142
1143         g_debug("queue_key_press_cb ()");
1144         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1145         store = gtk_tree_view_get_model(treeview);
1146
1147         selection = gtk_tree_view_get_selection (treeview);
1148         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1149         {
1150                 GtkTreePath *treepath;
1151
1152                 treepath = gtk_tree_model_get_path (store, &iter);
1153                 // Find the entry in the queue
1154                 indices = gtk_tree_path_get_indices (treepath);
1155                 row = indices[0];
1156                 // Can only free the treepath After getting what I need from
1157                 // indices since this points into treepath somewhere.
1158                 gtk_tree_path_free (treepath);
1159                 if (row < 0) return;
1160                 if (row >= ghb_array_len(ud->queue))
1161                         return;
1162                 ghb_queue_edit_settings = ghb_array_get_nth(ud->queue, row);
1163                 status = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
1164                 if (status == GHB_QUEUE_PENDING)
1165                 {
1166                         // Remove the selected item
1167                         gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1168                         // Remove the corresponding item from the queue list
1169                         ghb_array_remove(ud->queue, row);
1170                 }
1171                 gchar *source;
1172                 source = ghb_settings_get_string(ghb_queue_edit_settings, "source");
1173                 ghb_do_scan(ud, source, FALSE);
1174                 g_free(source);
1175         }
1176 }
1177