OSDN Git Service

LinGui: fix live preview scaling for PAR's < 1
[handbrake-jp/handbrake-jp-git.git] / gtk / src / preview.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * preview.c
4  * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
5  * 
6  * preview.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 <unistd.h>
15 #include <glib.h>
16 #include <glib/gstdio.h>
17 #include <glib-object.h>
18 #include <gtk/gtk.h>
19 #include <gdk/gdkx.h>
20 #include <gst/gst.h>
21 #include <gst/interfaces/xoverlay.h>
22 #include <gst/video/video.h>
23 #include <gst/pbutils/missing-plugins.h>
24 #include "settings.h"
25 #include "callbacks.h"
26 #include "hb-backend.h"
27 #include "preview.h"
28 #include "values.h"
29 #include "hb.h"
30
31 #define PREVIEW_STATE_IMAGE 0
32 #define PREVIEW_STATE_LIVE 1
33
34 struct preview_s
35 {
36         GstElement *play;
37         gint64 len;
38         gint64 pos;
39         gboolean seek_lock;
40         gboolean progress_lock;
41         gint width;
42         gint height;
43         GtkWidget *view;
44         gulong xid;
45         GdkPixbuf *pix;
46         gint button_width;
47         gint button_height;
48         gint frame;
49         gint state;
50         gboolean pause;
51         gboolean encoded[10];
52         gint encode_frame;
53         gchar *current;
54 };
55
56 static gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data);
57 static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg, 
58                                 gpointer data);
59 gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event, 
60                                 signal_user_data_t *ud);
61
62 void
63 ghb_preview_init(signal_user_data_t *ud)
64 {
65         GstBus *bus;
66         GstElement *xover;
67
68         ud->preview = g_malloc0(sizeof(preview_t));
69         ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
70         gtk_widget_realize(ud->preview->view);
71         g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
72                                         G_CALLBACK(preview_expose_cb), ud);
73         ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
74
75         ud->preview->play = gst_element_factory_make("playbin", "play");
76         ud->preview->pause = TRUE;
77         //xover = gst_element_factory_make("xvimagesink", "xover");
78         xover = gst_element_factory_make("gconfvideosink", "xover");
79         g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
80         //g_object_set(G_OBJECT(xover), "force-aspect-ratio", TRUE, NULL);
81
82         bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
83         gst_bus_add_watch(bus, live_preview_cb, ud);
84         gst_bus_set_sync_handler(bus, create_window, ud->preview);
85         gst_object_unref(bus);
86 }
87
88 void
89 ghb_preview_cleanup(signal_user_data_t *ud)
90 {
91         if (ud->preview->current)
92         {
93                 ud->preview->current = NULL;
94                 g_free(ud->preview->current);
95         }
96 }
97
98 static GstBusSyncReply
99 create_window(GstBus *bus, GstMessage *msg, gpointer data)
100 {
101         preview_t *preview = (preview_t*)data;
102
103         switch (GST_MESSAGE_TYPE(msg))
104         {
105         case GST_MESSAGE_ELEMENT:
106         {
107                 if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
108                         return GST_BUS_PASS;
109                 gst_x_overlay_set_xwindow_id(
110                         GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
111                 gst_message_unref(msg);
112                 return GST_BUS_DROP;
113         } break;
114
115         default:
116         {
117         } break;
118         }
119         return GST_BUS_PASS;
120 }
121
122 static GList *
123 get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
124 {
125         GValueArray *info_arr = NULL;
126         GList *ret = NULL;
127         guint ii;
128
129         if (play == NULL)
130                 return NULL;
131
132         g_object_get(play, "stream-info-value-array", &info_arr, NULL);
133         if (info_arr == NULL)
134                 return NULL;
135
136         for (ii = 0; ii < info_arr->n_values; ++ii) 
137         {
138                 GObject *info_obj;
139                 GValue *val;
140
141                 val = g_value_array_get_nth(info_arr, ii);
142                 info_obj = g_value_get_object(val);
143                 if (info_obj) 
144                 {
145                         GParamSpec *pspec;
146                         GEnumValue *value;
147                         gint type = -1;
148
149                         g_object_get(info_obj, "type", &type, NULL);
150                         pspec = g_object_class_find_property(
151                                                 G_OBJECT_GET_CLASS (info_obj), "type");
152                         value = g_enum_get_value(
153                                                 G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
154                         if (value) 
155                         {
156                                 if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
157                                         g_ascii_strcasecmp (value->value_name, typestr) == 0) 
158                                 {
159                                         ret = g_list_prepend (ret, g_object_ref (info_obj));
160                                 }
161                         }
162                 }
163         }
164         g_value_array_free (info_arr);
165         return g_list_reverse (ret);
166 }
167
168 static void
169 caps_set(GstCaps *caps, preview_t *preview)
170 {
171         GstStructure *ss;
172
173         ss = gst_caps_get_structure(caps, 0);
174         if (ss)
175         {
176                 gint fps_n, fps_d, width, height;
177                 guint num, den, par_n, par_d;
178                 guint disp_par_n, disp_par_d;
179                 const GValue *par;
180                 GValue disp_par = {0,};
181                 GstElement *xover;
182                 GObjectClass *klass;
183                 GParamSpec *pspec;
184
185                 gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
186                 gst_structure_get_int(ss, "width", &width);
187                 gst_structure_get_int(ss, "height", &height);
188                 par = gst_structure_get_value(ss, "pixel-aspect-ratio");
189                 par_n = gst_value_get_fraction_numerator(par);
190                 par_d = gst_value_get_fraction_denominator(par);
191
192                 g_value_init(&disp_par, GST_TYPE_FRACTION);
193                 gst_value_set_fraction(&disp_par, 1, 1);
194                 g_object_get(preview->play, "video-sink", &xover, NULL);
195                 klass = G_OBJECT_GET_CLASS(xover);
196                 pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
197                 if (pspec)
198                 {
199                         GValue par_prop = {0,};
200
201                         g_value_init(&par_prop, pspec->value_type);
202                         g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
203                                                                         &par_prop);
204                         if (!g_value_transform(&par_prop, &disp_par))
205                         {
206                                 g_warning("transform failed");
207                                 gst_value_set_fraction(&disp_par, 1, 1);
208                         }
209                         g_value_unset(&par_prop);
210                 }
211                 disp_par_n = gst_value_get_fraction_numerator(&disp_par);
212                 disp_par_d = gst_value_get_fraction_denominator(&disp_par);
213                 g_value_unset(&disp_par);
214                 gst_video_calculate_display_ratio(
215                         &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
216
217                 if (par_n > par_d)
218                         width = gst_util_uint64_scale_int(height, num, den);
219                 else
220                         height = gst_util_uint64_scale_int(width, den, num);
221                 
222                 if (width != preview->width || height != preview->height)
223                 {
224                         gtk_widget_set_size_request(preview->view, width, height);
225                         preview->width = width;
226                         preview->height = height;
227                 }
228         }
229 }
230
231 static void
232 update_stream_info(preview_t *preview)
233 {
234         GList *vstreams, *ll;
235         GstPad *vpad = NULL;
236
237         vstreams = get_stream_info_objects_for_type(preview->play, "video");
238         if (vstreams)
239         {
240                 for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
241                 {
242                         g_object_get(ll->data, "object", &vpad, NULL);
243                 }
244         }
245         if (vpad)
246         {
247                 GstCaps *caps;
248
249                 caps = gst_pad_get_negotiated_caps(vpad);
250                 if (caps)
251                 {
252                         caps_set(caps, preview);
253                         gst_caps_unref(caps);
254                 }
255                 //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
256                 gst_object_unref(vpad);
257         }
258         g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
259         g_list_free(vstreams);
260 }
261
262 static gboolean
263 live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
264 {
265         signal_user_data_t *ud = (signal_user_data_t*)data;
266
267         switch (GST_MESSAGE_TYPE(msg))
268         {
269         case GST_MESSAGE_ERROR:
270         {
271                 GError *err;
272                 gchar *debug;
273
274                 gst_message_parse_error(msg, &err, &debug);
275                 g_warning("Gstreamer Error: %s", err->message);
276                 g_error_free(err);
277                 g_free(debug);
278         } break;
279
280         case GST_MESSAGE_ELEMENT:
281         {
282                 if (gst_is_missing_plugin_message(msg))
283                 {
284                         gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
285                         gchar *message, *desc;
286                         desc = gst_missing_plugin_message_get_description(msg);
287                         message = g_strdup_printf(
288                                                 "Missing GStreamer plugin\n"
289                                                 "Audio or Video may not play as expected\n\n%s",
290                                                 desc);
291                         ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
292                         g_free(message);
293                         gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
294                 }
295         } break;
296
297         case GST_MESSAGE_STATE_CHANGED:
298         {
299                 GstState state, pending;
300                 gst_element_get_state(ud->preview->play, &state, &pending, 0);
301                 if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
302                 {
303                         update_stream_info(ud->preview);
304                 }
305         } break;
306
307         case GST_MESSAGE_EOS:
308         {
309                 // Done
310                 GtkImage *img;
311
312                 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
313                 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
314                 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
315                 ud->preview->pause = TRUE;
316                 gst_element_seek(ud->preview->play, 1.0,
317                         GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
318                         GST_SEEK_TYPE_SET, 0,
319                         GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
320         } break;
321
322         default:
323         {
324                 // Ignore
325         }
326         }
327         return TRUE;
328 }
329
330 void
331 live_preview_start(signal_user_data_t *ud)
332 {
333         GtkImage *img;
334         gchar *uri;
335
336         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
337         if (!ud->preview->encoded[ud->preview->frame])
338         {
339                 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
340                 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
341                 ud->preview->pause = TRUE;
342                 return;
343         }
344
345         uri = g_strdup_printf("file://%s", ud->preview->current);
346         gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
347         ud->preview->state = PREVIEW_STATE_LIVE;
348         g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
349         gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
350         ud->preview->pause = FALSE;
351         g_free(uri);
352 }
353
354 void
355 live_preview_pause(signal_user_data_t *ud)
356 {
357         GtkImage *img;
358
359         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
360         gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
361         gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
362         ud->preview->pause = TRUE;
363 }
364
365 void
366 live_preview_stop(signal_user_data_t *ud)
367 {
368         GtkImage *img;
369         GtkRange *progress;
370
371         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
372         gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
373         gst_element_set_state(ud->preview->play, GST_STATE_NULL);
374         ud->preview->pause = TRUE;
375         ud->preview->state = PREVIEW_STATE_IMAGE;
376
377         progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
378         gtk_range_set_value(progress, 0);
379 }
380
381 void
382 ghb_live_reset(signal_user_data_t *ud)
383 {
384         gboolean encoded;
385
386         if (!ud->preview->pause)
387                 live_preview_stop(ud);
388         if (ud->preview->current)
389         {
390                 g_free(ud->preview->current);
391                 ud->preview->current = NULL;
392         }
393         encoded = ud->preview->encoded[ud->preview->frame];
394         memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
395         if (encoded)
396                 ghb_set_preview_image(ud);
397 }
398
399 extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
400
401 void
402 live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
403 {
404         gchar *tmp_dir;
405         gchar *name;
406         gint frame = ud->preview->frame;
407
408         tmp_dir = ghb_get_tmp_dir();
409         name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
410         if (ud->preview->current)
411                 g_free(ud->preview->current);
412         ud->preview->current = name;
413
414         if (ud->preview->encoded[frame] &&
415                 g_file_test(name, G_FILE_TEST_IS_REGULAR))
416         {
417                 if (ud->preview->pause)
418                         live_preview_start(ud);
419                 else
420                         live_preview_pause(ud);
421         }
422         else
423         {
424                 GValue *js;
425
426                 ud->preview->encode_frame = frame;
427                 js = ghb_value_dup(ud->settings);
428                 ghb_settings_set_string(js, "destination", name);
429                 ghb_settings_set_int(js, "start_frame", ud->preview->frame);
430                 ghb_settings_set_int(js, "live_duration", 15);
431                 ghb_add_live_job(js, 0);
432                 ghb_start_live_encode();
433                 ghb_value_free(js);
434         }
435 }
436
437 void
438 ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
439 {
440         GtkWidget *widget;
441         GtkWidget *prog;
442
443         prog = GHB_WIDGET(ud->builder, "live_encode_progress");
444         if (success && 
445                 ud->preview->encode_frame == ud->preview->frame)
446         {
447                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
448                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
449                 ud->preview->encoded[ud->preview->encode_frame] = TRUE;
450                 live_preview_start(ud);
451                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
452                 gtk_widget_hide (widget);
453                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
454                 gtk_widget_show (widget);
455         }
456         else
457         {
458                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
459                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
460                 ud->preview->encoded[ud->preview->encode_frame] = FALSE;
461         }
462 }
463
464 static gboolean
465 unlock_progress_cb(signal_user_data_t *ud)
466 {
467         ud->preview->progress_lock = FALSE;
468         // This function is initiated by g_idle_add.  Must return false
469         // so that it is not called again
470         return FALSE;
471 }
472
473 void
474 ghb_live_preview_progress(signal_user_data_t *ud)
475 {
476         GstFormat fmt = GST_FORMAT_TIME;
477         gint64 len = -1, pos = -1;
478
479         if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
480                 return;
481
482         ud->preview->progress_lock = TRUE;
483         if (gst_element_query_duration(ud->preview->play, &fmt, &len))
484         {
485                 if (len != -1 && fmt == GST_FORMAT_TIME)
486                 {
487                         ud->preview->len = len / GST_MSECOND;
488                 }
489         }
490         if (gst_element_query_position(ud->preview->play, &fmt, &pos))
491         {
492                 if (pos != -1 && fmt == GST_FORMAT_TIME)
493                 {
494                         ud->preview->pos = pos / GST_MSECOND;
495                 }
496         }
497         if (ud->preview->len > 0)
498         {
499                 GtkRange *progress;
500                 gdouble percent;
501
502                 percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
503                 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
504                 gtk_range_set_value(progress, percent);
505         }
506         g_idle_add((GSourceFunc)unlock_progress_cb, ud);
507 }
508
509 static gboolean
510 unlock_seek_cb(signal_user_data_t *ud)
511 {
512         ud->preview->seek_lock = FALSE;
513         // This function is initiated by g_idle_add.  Must return false
514         // so that it is not called again
515         return FALSE;
516 }
517
518 void
519 live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
520 {
521         gdouble dval;
522         gint64 pos;
523
524         if (ud->preview->progress_lock)
525                 return;
526
527         ud->preview->seek_lock = TRUE;
528         dval = gtk_range_get_value(GTK_RANGE(widget));
529         pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
530         gst_element_seek(ud->preview->play, 1.0,
531                 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
532                 GST_SEEK_TYPE_SET, pos,
533                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
534         g_idle_add((GSourceFunc)unlock_seek_cb, ud);
535 }
536
537 void
538 ghb_set_preview_image(signal_user_data_t *ud)
539 {
540         GtkWidget *widget;
541         gint preview_width, preview_height, target_height, width, height;
542
543         g_debug("set_preview_button_image ()");
544         gint titleindex;
545
546         live_preview_stop(ud);
547
548         titleindex = ghb_settings_combo_int(ud->settings, "title");
549         if (titleindex < 0) return;
550         widget = GHB_WIDGET (ud->builder, "preview_frame");
551         ud->preview->frame = ghb_widget_int(widget) - 1;
552         if (ud->preview->encoded[ud->preview->frame])
553         {
554                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
555                 gtk_widget_hide (widget);
556                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
557                 gtk_widget_show (widget);
558         }
559         else
560         {
561                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
562                 gtk_widget_hide (widget);
563                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
564                 gtk_widget_show (widget);
565                 widget = GHB_WIDGET(ud->builder, "live_encode_progress");
566                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
567                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
568         }
569         if (ud->preview->pix != NULL)
570                 g_object_unref(ud->preview->pix);
571         ud->preview->pix = ghb_get_preview_image(titleindex, ud->preview->frame, 
572                                                                                         ud->settings, TRUE);
573         if (ud->preview->pix == NULL) return;
574         preview_width = gdk_pixbuf_get_width(ud->preview->pix);
575         preview_height = gdk_pixbuf_get_height(ud->preview->pix);
576         widget = GHB_WIDGET (ud->builder, "preview_image");
577         if (preview_width != ud->preview->width || 
578                 preview_height != ud->preview->height)
579         {
580                 gtk_widget_set_size_request(widget, preview_width, preview_height);
581                 ud->preview->width = preview_width;
582                 ud->preview->height = preview_height;
583         }
584         gdk_draw_pixbuf(
585                 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
586                 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
587
588         gchar *text = g_strdup_printf("%d x %d", preview_width, preview_height);
589         widget = GHB_WIDGET (ud->builder, "preview_dims");
590         gtk_label_set_text(GTK_LABEL(widget), text);
591         g_free(text);
592         
593         g_debug("preview %d x %d", preview_width, preview_height);
594         target_height = MIN(ud->preview->button_height, 128);
595         height = target_height;
596         width = preview_width * height / preview_height;
597
598         if ((height >= 16) && (width >= 16))
599         {
600                 GdkPixbuf *scaled_preview;
601                 scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width, 
602                                                                                                 height, GDK_INTERP_NEAREST);
603                 if (scaled_preview != NULL)
604                 {
605                         widget = GHB_WIDGET (ud->builder, "preview_button_image");
606                         gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
607                         g_object_unref (scaled_preview);
608                 }
609         }
610 }
611
612 static gboolean
613 delayed_expose_cb(signal_user_data_t *ud)
614 {
615         GstElement *vsink;
616         GstXOverlay *xover;
617
618         g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
619         if (GST_IS_BIN(vsink))
620                 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
621                                                                 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
622         else
623                 xover = GST_X_OVERLAY(vsink);
624         gst_x_overlay_expose(xover);
625         // This function is initiated by g_idle_add.  Must return false
626         // so that it is not called again
627         return FALSE;
628 }
629
630 gboolean
631 preview_expose_cb(
632         GtkWidget *widget, 
633         GdkEventExpose *event, 
634         signal_user_data_t *ud)
635 {
636         if (ud->preview->state == PREVIEW_STATE_LIVE)
637         {
638                 if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
639                 {
640                         GstElement *vsink;
641                         GstXOverlay *xover;
642
643                         g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
644                         if (GST_IS_BIN(vsink))
645                                 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
646                                                                                 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
647                         else
648                                 xover = GST_X_OVERLAY(vsink);
649                         gst_x_overlay_expose(xover);
650                         // For some reason, the exposed region doesn't always get
651                         // cleaned up here. But a delayed gst_x_overlay_expose()
652                         // takes care of it.
653                         g_idle_add((GSourceFunc)delayed_expose_cb, ud);
654                         return FALSE;
655                 }
656                 return TRUE;
657         }
658
659         gdk_draw_pixbuf(
660                 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
661                 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
662         return TRUE;
663 }
664
665 void
666 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
667 {
668         g_debug("allocate %d x %d", allocation->width, allocation->height);
669         if (ud->preview->button_width == allocation->width &&
670                 ud->preview->button_height == allocation->height)
671         {
672                 // Nothing to do. Bug out.
673                 g_debug("nothing to do");
674                 return;
675         }
676         g_debug("prev allocate %d x %d", ud->preview->button_width, 
677                         ud->preview->button_height);
678         ud->preview->button_width = allocation->width;
679         ud->preview->button_height = allocation->height;
680         ghb_set_preview_image(ud);
681 }
682
683 void
684 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
685 {
686         gint titleindex;
687
688         titleindex = ghb_settings_combo_int(ud->settings, "title");
689         if (titleindex < 0) return;
690         g_debug("titleindex %d", titleindex);
691
692         GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
693         gtk_widget_show (widget);
694 }
695
696 void
697 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
698 {
699         ghb_set_preview_image(ud);
700 }
701