OSDN Git Service

LinGui: add status icon that sits in the system tray
[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 exists.\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
644         GtkStatusIcon *si;
645
646         si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
647         gtk_status_icon_set_from_icon_name(si, "hb-status");
648
649         if (ud->queue == NULL)
650                 ud->queue = ghb_array_value_new(32);
651         // Make a copy of current settings to be used for the new job
652         settings = ghb_value_dup(ud->settings);
653         ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING);
654         ghb_settings_set_int(settings, "job_unique_id", 0);
655         titleindex = ghb_settings_combo_int(settings, "title");
656         titlenum = ghb_get_title_number(titleindex);
657         ghb_settings_set_int(settings, "titlenum", titlenum);
658         ghb_array_append(ud->queue, settings);
659         add_to_queue_list(ud, settings, NULL);
660         ghb_save_queue(ud->queue);
661
662         return TRUE;
663 }
664
665 void
666 queue_add_clicked_cb(GtkWidget *widget, signal_user_data_t *ud)
667 {
668         g_debug("queue_add_clicked_cb ()");
669         queue_add(ud);
670 }
671
672 void
673 queue_remove_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud)
674 {
675         GtkTreeView *treeview;
676         GtkTreePath *treepath;
677         GtkTreeModel *store;
678         GtkTreeIter iter;
679         gint row;
680         gint *indices;
681         gint unique_id;
682         GValue *settings;
683         gint status;
684
685         g_debug("queue_remove_clicked_cb ()");
686         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
687         store = gtk_tree_view_get_model(treeview);
688         treepath = gtk_tree_path_new_from_string (path);
689         if (gtk_tree_path_get_depth(treepath) > 1) return;
690         if (gtk_tree_model_get_iter(store, &iter, treepath))
691         {
692                 // Find the entry in the queue
693                 indices = gtk_tree_path_get_indices (treepath);
694                 row = indices[0];
695                 // Can only free the treepath After getting what I need from
696                 // indices since this points into treepath somewhere.
697                 gtk_tree_path_free (treepath);
698                 if (row < 0) return;
699                 if (row >= ghb_array_len(ud->queue))
700                         return;
701                 settings = ghb_array_get_nth(ud->queue, row);
702                 status = ghb_settings_get_int(settings, "job_status");
703                 if (status == GHB_QUEUE_RUNNING)
704                 {
705                         // Ask if wants to stop encode.
706                         if (!ghb_cancel_encode(NULL))
707                         {
708                                 return;
709                         }
710                         unique_id = ghb_settings_get_int(settings, "job_unique_id");
711                         ghb_remove_job(unique_id);
712                 }
713                 // Remove the selected item
714                 gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
715                 // Remove the corresponding item from the queue list
716                 GValue *old = ghb_array_get_nth(ud->queue, row);
717                 ghb_value_free(old);
718                 ghb_array_remove(ud->queue, row);
719                 ghb_save_queue(ud->queue);
720         }
721         else
722         {       
723                 gtk_tree_path_free (treepath);
724         }
725 }
726
727 static gint
728 find_last_finished(GValue *queue)
729 {
730         GValue *js;
731         gint ii, count;
732         gint status;
733         
734         g_debug("find_last_finished");
735         count = ghb_array_len(queue);
736         for (ii = 0; ii < count; ii++)
737         {
738                 js = ghb_array_get_nth(queue, ii);
739                 status = ghb_settings_get_int(js, "job_status");
740                 if (status != GHB_QUEUE_DONE && status != GHB_QUEUE_RUNNING)
741                 {
742                         return ii-1;
743                 }
744         }
745         return -1;
746 }
747
748 // This little bit is needed to prevent the default drag motion
749 // handler from expanding rows if you hover over them while
750 // dragging.
751 // Also controls where valid drop locations are
752 gboolean
753 queue_drag_motion_cb(
754         GtkTreeView *tv,
755         GdkDragContext *ctx,
756         gint x,
757         gint y,
758         guint time,
759         signal_user_data_t *ud)
760 {
761         GtkTreePath *path = NULL;
762         GtkTreeViewDropPosition pos;
763         gint *indices, row, status, finished;
764         GValue *js;
765         GtkTreeIter iter;
766         GtkTreeView *srctv;
767         GtkTreeModel *model;
768         GtkTreeSelection *select;
769
770         // This bit checks to see if the source is allowed to be
771         // moved.  Only pending and canceled items may be moved.
772         srctv = GTK_TREE_VIEW(gtk_drag_get_source_widget(ctx));
773         select = gtk_tree_view_get_selection (srctv);
774         gtk_tree_selection_get_selected (select, &model, &iter);
775         path = gtk_tree_model_get_path (model, &iter);
776         indices = gtk_tree_path_get_indices(path);
777         row = indices[0];
778         gtk_tree_path_free(path);
779         js = ghb_array_get_nth(ud->queue, row);
780         status = ghb_settings_get_int(js, "job_status");
781         if (status != GHB_QUEUE_PENDING && status != GHB_QUEUE_CANCELED)
782         {
783                 gdk_drag_status(ctx, 0, time);
784                 return TRUE;
785         }
786
787         // The reset checks that the destination is a valid position
788         // in the list.  Can not move above any finished or running items
789         gtk_tree_view_get_dest_row_at_pos (tv, x, y, &path, &pos);
790         if (path == NULL)
791         {
792                 gdk_drag_status(ctx, GDK_ACTION_MOVE, time);
793                 return TRUE;
794         }
795         // Don't allow *drop into*
796         if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
797                 pos = GTK_TREE_VIEW_DROP_BEFORE;
798         if (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
799                 pos = GTK_TREE_VIEW_DROP_AFTER;
800         // Don't allow droping int child items
801         if (gtk_tree_path_get_depth(path) > 1)
802         {
803                 gtk_tree_path_up(path);
804                 pos = GTK_TREE_VIEW_DROP_AFTER;
805         }
806         indices = gtk_tree_path_get_indices(path);
807         row = indices[0];
808         js = ghb_array_get_nth(ud->queue, row);
809
810         finished = find_last_finished(ud->queue);
811         if (row < finished)
812         {
813                 gtk_tree_path_free(path);
814                 gdk_drag_status(ctx, 0, time);
815                 return TRUE;
816         }
817         if (pos != GTK_TREE_VIEW_DROP_AFTER && 
818                 row == finished)
819         {
820                 gtk_tree_path_free(path);
821                 gdk_drag_status(ctx, 0, time);
822                 return TRUE;
823         }
824         gtk_tree_view_set_drag_dest_row(tv, path, pos);
825         gtk_tree_path_free(path);
826         gdk_drag_status(ctx, GDK_ACTION_MOVE, time);
827         return TRUE;
828 }
829
830 void 
831 queue_drag_cb(
832         GtkTreeView *dstwidget, 
833         GdkDragContext *dc, 
834         gint x, gint y, 
835         GtkSelectionData *selection_data, 
836         guint info, guint t, 
837         signal_user_data_t *ud)
838 {
839         GtkTreePath *path = NULL;
840         //GtkTreeModel *model;
841         GtkTreeViewDropPosition pos;
842         GtkTreeIter dstiter, srciter;
843         gint *indices, row;
844         GValue *js;
845         
846         GtkTreeModel *dstmodel = gtk_tree_view_get_model(dstwidget);
847                         
848         g_debug("queue_drag_cb ()");
849         // This doesn't work here for some reason...
850         // gtk_tree_view_get_drag_dest_row(dstwidget, &path, &pos);
851         gtk_tree_view_get_dest_row_at_pos (dstwidget, x, y, &path, &pos);
852         // This little hack is needed because attempting to drop after
853         // the last item gives us no path or pos.
854         if (path == NULL)
855         {
856                 gint n_children;
857
858                 n_children = gtk_tree_model_iter_n_children(dstmodel, NULL);
859                 if (n_children)
860                 {
861                         pos = GTK_TREE_VIEW_DROP_AFTER;
862                         path = gtk_tree_path_new_from_indices(n_children-1, -1);
863                 }
864                 else
865                 {
866                         pos = GTK_TREE_VIEW_DROP_BEFORE;
867                         path = gtk_tree_path_new_from_indices(0, -1);
868                 }
869         }
870         if (path)
871         {
872                 if (gtk_tree_path_get_depth(path) > 1)
873                         gtk_tree_path_up(path);
874                 if (gtk_tree_model_get_iter (dstmodel, &dstiter, path))
875                 {
876                         GtkTreeIter iter;
877                         GtkTreeView *srcwidget;
878                         GtkTreeModel *srcmodel;
879                         GtkTreeSelection *select;
880                         GtkTreePath *srcpath = NULL;
881                         GtkTreePath *dstpath = NULL;
882
883                         srcwidget = GTK_TREE_VIEW(gtk_drag_get_source_widget(dc));
884                         //srcmodel = gtk_tree_view_get_model(srcwidget);
885                         select = gtk_tree_view_get_selection (srcwidget);
886                         gtk_tree_selection_get_selected (select, &srcmodel, &srciter);
887
888                         srcpath = gtk_tree_model_get_path (srcmodel, &srciter);
889                         indices = gtk_tree_path_get_indices(srcpath);
890                         row = indices[0];
891                         gtk_tree_path_free(srcpath);
892                         js = ghb_array_get_nth(ud->queue, row);
893
894                         switch (pos)
895                         {
896                                 case GTK_TREE_VIEW_DROP_BEFORE:
897                                 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
898                                         gtk_tree_store_insert_before (GTK_TREE_STORE (dstmodel), 
899                                                                                                         &iter, NULL, &dstiter);
900                                         break;
901
902                                 case GTK_TREE_VIEW_DROP_AFTER:
903                                 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
904                                         gtk_tree_store_insert_after (GTK_TREE_STORE (dstmodel), 
905                                                                                                         &iter, NULL, &dstiter);
906                                         break;
907
908                                 default:
909                                         break;
910                         }
911                         // Reset job to pending
912                         ghb_settings_set_int(js, "job_status", GHB_QUEUE_PENDING);
913                         add_to_queue_list(ud, js, &iter);
914
915                         dstpath = gtk_tree_model_get_path (dstmodel, &iter);
916                         indices = gtk_tree_path_get_indices(dstpath);
917                         row = indices[0];
918                         gtk_tree_path_free(dstpath);
919                         ghb_array_insert(ud->queue, row, js);
920
921                         srcpath = gtk_tree_model_get_path (srcmodel, &srciter);
922                         indices = gtk_tree_path_get_indices(srcpath);
923                         row = indices[0];
924                         gtk_tree_path_free(srcpath);
925                         ghb_array_remove(ud->queue, row);
926                         gtk_tree_store_remove (GTK_TREE_STORE (srcmodel), &srciter);
927                         ghb_save_queue(ud->queue);
928                 }
929                 gtk_tree_path_free(path);
930         }
931 }
932
933 void
934 ghb_queue_buttons_grey(signal_user_data_t *ud, gboolean working)
935 {
936         GtkWidget *widget;
937         GtkAction *action;
938         gint queue_count;
939         gint titleindex;
940         gboolean title_ok;
941
942         queue_count = ghb_array_len(ud->queue);
943         titleindex = ghb_settings_combo_int(ud->settings, "title");
944         title_ok = (titleindex >= 0);
945
946         widget = GHB_WIDGET (ud->builder, "queue_start1");
947         gtk_widget_set_sensitive (widget, !working && (title_ok || queue_count));
948         widget = GHB_WIDGET (ud->builder, "queue_start2");
949         gtk_widget_set_sensitive (widget, !working && (title_ok || queue_count));
950         action = GHB_ACTION (ud->builder, "queue_start_menu");
951         gtk_action_set_sensitive (action, !working && (title_ok || queue_count));
952         widget = GHB_WIDGET (ud->builder, "queue_pause1");
953         gtk_widget_set_sensitive (widget, working);
954         widget = GHB_WIDGET (ud->builder, "queue_pause2");
955         gtk_widget_set_sensitive (widget, working);
956         action = GHB_ACTION (ud->builder, "queue_pause_menu");
957         gtk_action_set_sensitive (action, working);
958         widget = GHB_WIDGET (ud->builder, "queue_stop");
959         gtk_widget_set_sensitive (widget, working);
960         action = GHB_ACTION (ud->builder, "queue_stop_menu");
961         gtk_action_set_sensitive (action, working);
962 }
963
964 void
965 queue_list_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, GtkCellRenderer *cell)
966 {
967         GtkTreeViewColumn *column;
968         gint width;
969         
970         column = gtk_tree_view_get_column (GTK_TREE_VIEW(widget), 0);
971         width = gtk_tree_view_column_get_width(column);
972         g_debug("col width %d alloc width %d", width, allocation->width);
973         // Set new wrap-width.  Shave a little off to accomidate the icons
974         // that share this column.
975         if (width >= 564) // Don't allow below a certain size
976                 g_object_set(cell, "wrap-width", width-70, NULL);
977 }
978
979 void
980 queue_start_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
981 {
982         GValue *js;
983         gboolean running = FALSE;
984         gint count, ii;
985         gint status;
986         gint state;
987
988         count = ghb_array_len(ud->queue);
989         for (ii = 0; ii < count; ii++)
990         {
991                 js = ghb_array_get_nth(ud->queue, ii);
992                 status = ghb_settings_get_int(js, "job_status");
993                 if ((status == GHB_QUEUE_RUNNING) || 
994                         (status == GHB_QUEUE_PENDING))
995                 {
996                         running = TRUE;
997                         break;
998                 }
999         }
1000         if (!running)
1001         {
1002                 // The queue has no running or pending jobs.
1003                 // Add current settings to the queue, then run.
1004                 if (!queue_add(ud))
1005                         return;
1006         }
1007         state = ghb_get_queue_state();
1008         if (state == GHB_STATE_IDLE)
1009         {
1010                 // Add the first pending queue item and start
1011                 ud->current_job = ghb_start_next_job(ud, TRUE);
1012         }
1013 }
1014
1015 void
1016 queue_stop_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1017 {
1018         ud->cancel_encode = TRUE;
1019         ghb_cancel_encode(NULL);
1020 }
1021
1022 void
1023 queue_pause_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1024 {
1025         ghb_pause_queue();
1026 }
1027
1028 gboolean
1029 ghb_reload_queue(signal_user_data_t *ud)
1030 {
1031         GValue *queue;
1032         gint unfinished = 0;
1033         gint count, ii;
1034         gint status;
1035         GValue *settings;
1036         gchar *message;
1037
1038         g_debug("ghb_reload_queue");
1039
1040         queue = ghb_load_queue();
1041         // Look for unfinished entries
1042         count = ghb_array_len(queue);
1043         for (ii = 0; ii < count; ii++)
1044         {
1045                 settings = ghb_array_get_nth(queue, ii);
1046                 status = ghb_settings_get_int(settings, "job_status");
1047                 if (status != GHB_QUEUE_DONE && status != GHB_QUEUE_CANCELED)
1048                 {
1049                         unfinished++;
1050                 }
1051         }
1052         if (unfinished)
1053         {
1054                 message = g_strdup_printf(
1055                                         "You have %d unfinished job%s in a saved queue.\n\n"
1056                                         "Would you like to reload %s?",
1057                                         unfinished, 
1058                                         (unfinished > 1) ? "s" : "",
1059                                         (unfinished > 1) ? "them" : "it");
1060                 if (ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "No", "Yes"))
1061                 {
1062                         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
1063                         gtk_widget_show (widget);
1064
1065                         ud->queue = queue;
1066                         // First get rid of any old items we don't want
1067                         for (ii = count-1; ii >= 0; ii--)
1068                         {
1069                                 settings = ghb_array_get_nth(queue, ii);
1070                                 status = ghb_settings_get_int(settings, "job_status");
1071                                 if (status == GHB_QUEUE_DONE || status == GHB_QUEUE_CANCELED)
1072                                 {
1073                                         GValue *old = ghb_array_get_nth(queue, ii);
1074                                         ghb_value_free(old);
1075                                         ghb_array_remove(queue, ii);
1076                                 }
1077                         }
1078                         count = ghb_array_len(queue);
1079                         for (ii = 0; ii < count; ii++)
1080                         {
1081                                 settings = ghb_array_get_nth(queue, ii);
1082                                 ghb_settings_set_int(settings, "job_unique_id", 0);
1083                                 ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING);
1084                                 add_to_queue_list(ud, settings, NULL);
1085                         }
1086                         ghb_queue_buttons_grey(ud, FALSE);
1087                 }
1088                 else
1089                 {
1090                         ghb_value_free(queue);
1091                         ghb_remove_queue_file();
1092                 }
1093                 g_free(message);
1094         }
1095         return FALSE;
1096 }
1097
1098 gboolean 
1099 queue_key_press_cb(
1100         GtkWidget *widget, 
1101         GdkEventKey *event,
1102         signal_user_data_t *ud)
1103 {
1104         GtkTreeView *treeview;
1105         GtkTreeSelection *selection;
1106         GtkTreeModel *store;
1107         GtkTreeIter iter;
1108         gint row;
1109         gint *indices;
1110         gint unique_id;
1111         GValue *settings;
1112         gint status;
1113
1114         g_debug("queue_key_press_cb ()");
1115         if (event->keyval != GDK_Delete)
1116                 return FALSE;
1117         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1118         store = gtk_tree_view_get_model(treeview);
1119
1120         selection = gtk_tree_view_get_selection (treeview);
1121         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1122         {
1123                 GtkTreePath *treepath;
1124
1125                 treepath = gtk_tree_model_get_path (store, &iter);
1126                 // Find the entry in the queue
1127                 indices = gtk_tree_path_get_indices (treepath);
1128                 row = indices[0];
1129                 // Can only free the treepath After getting what I need from
1130                 // indices since this points into treepath somewhere.
1131                 gtk_tree_path_free (treepath);
1132                 if (row < 0) return FALSE;
1133                 if (row >= ghb_array_len(ud->queue))
1134                         return FALSE;
1135                 settings = ghb_array_get_nth(ud->queue, row);
1136                 status = ghb_settings_get_int(settings, "job_status");
1137                 if (status == GHB_QUEUE_RUNNING)
1138                 {
1139                         // Ask if wants to stop encode.
1140                         if (!ghb_cancel_encode(NULL))
1141                         {
1142                                 return TRUE;
1143                         }
1144                         unique_id = ghb_settings_get_int(settings, "job_unique_id");
1145                         ghb_remove_job(unique_id);
1146                 }
1147                 // Remove the selected item
1148                 gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1149                 // Remove the corresponding item from the queue list
1150                 GValue *old = ghb_array_get_nth(ud->queue, row);
1151                 ghb_value_free(old);
1152                 ghb_array_remove(ud->queue, row);
1153                 ghb_save_queue(ud->queue);
1154                 return TRUE;
1155         }
1156         return FALSE;
1157 }
1158
1159 GValue *ghb_queue_edit_settings = NULL;
1160
1161 void
1162 queue_edit_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1163 {
1164         GtkTreeView *treeview;
1165         GtkTreeSelection *selection;
1166         GtkTreeModel *store;
1167         GtkTreeIter iter;
1168         gint row;
1169         gint *indices;
1170         gint status;
1171
1172         g_debug("queue_key_press_cb ()");
1173         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1174         store = gtk_tree_view_get_model(treeview);
1175
1176         selection = gtk_tree_view_get_selection (treeview);
1177         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1178         {
1179                 GtkTreePath *treepath;
1180
1181                 treepath = gtk_tree_model_get_path (store, &iter);
1182                 // Find the entry in the queue
1183                 indices = gtk_tree_path_get_indices (treepath);
1184                 row = indices[0];
1185                 // Can only free the treepath After getting what I need from
1186                 // indices since this points into treepath somewhere.
1187                 gtk_tree_path_free (treepath);
1188                 if (row < 0) return;
1189                 if (row >= ghb_array_len(ud->queue))
1190                         return;
1191                 ghb_queue_edit_settings = ghb_array_get_nth(ud->queue, row);
1192                 status = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
1193                 if (status == GHB_QUEUE_PENDING)
1194                 {
1195                         // Remove the selected item
1196                         gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1197                         // Remove the corresponding item from the queue list
1198                         ghb_array_remove(ud->queue, row);
1199                 }
1200                 gchar *source;
1201                 source = ghb_settings_get_string(ghb_queue_edit_settings, "source");
1202                 ghb_do_scan(ud, source, 0, FALSE);
1203                 g_free(source);
1204         }
1205 }
1206