OSDN Git Service

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