OSDN Git Service

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