[mistelix] Let user select which image or video frame wants to use for a button in a DVD menu



commit 5377218f2778f8cb23a5905353f0ec2534f7b7d8
Author: Jordi Mas <jmas softcatala org>
Date:   Sun Aug 9 23:18:14 2009 +0200

    Let user select which image or video frame wants to use for a button in a DVD menu

 libmistelix/mistelix.h                 |    2 +-
 libmistelix/thumbnail.c                |    4 +-
 src/Backends/GStreamer/Thumbnail.cs    |    6 +-
 src/Core/Button.cs                     |   32 +++++++-
 src/Core/SlideShow.cs                  |   32 +++++++-
 src/Core/TaskDispatcher.cs             |    4 +
 src/Core/Video.cs                      |   42 ++++++++++
 src/DataModel/ButtonProjectElement.cs  |   10 ++-
 src/DataModel/ThumbnailCollection.cs   |   78 +++++++++++++++++
 src/DataModel/VideoProjectElement.cs   |    4 +-
 src/DataModel/VisibleProjectElement.cs |    4 +
 src/Dialogs/ButtonPropertiesDialog.cs  |  142 +++++++++++++++++++++++++++++++-
 src/Makefile.am                        |    3 +-
 src/Widgets/VideosFileView.cs          |    3 +-
 src/mistelix.glade                     |   41 +++++++++
 15 files changed, 392 insertions(+), 15 deletions(-)
---
diff --git a/libmistelix/mistelix.h b/libmistelix/mistelix.h
index 7397296..40fbad2 100644
--- a/libmistelix/mistelix.h
+++ b/libmistelix/mistelix.h
@@ -51,7 +51,7 @@ int mistelix_video_convert (const char* filein, const char* fileout, unsigned in
 
 int mistelix_video_supported (const char* file, char* msg);
 
-void mistelix_video_screenshot (const char* filein, void** image);
+void mistelix_video_screenshot (const char* filein, int second, void** image);
 
 unsigned int mistelix_get_plugins_count ();
 
diff --git a/libmistelix/thumbnail.c b/libmistelix/thumbnail.c
index 8c7287b..3f2399b 100644
--- a/libmistelix/thumbnail.c
+++ b/libmistelix/thumbnail.c
@@ -31,7 +31,7 @@ mistelix_take_screenshot (GstElement* playbin, GstBus* bus, GstElement* pixbufsi
 	Creates a video screenshot using playbin and gdkpixbufsink
 */
 void
-mistelix_video_screenshot (const char* filein, void** image)
+mistelix_video_screenshot (const char* filein, int second, void** image)
 {
 	GstElement *playbin, *pixbufsink, *fakesink;
 	GstBus* bus;
@@ -56,7 +56,7 @@ mistelix_video_screenshot (const char* filein, void** image)
 	g_assert (bus);
 
 	/* Go to the second 5 since the beginning is black for many videos */
-	pix = mistelix_take_screenshot (playbin, bus, pixbufsink, 5);
+	pix = mistelix_take_screenshot (playbin, bus, pixbufsink, second);
 
 	gst_element_set_state (playbin, GST_STATE_NULL);
 	gst_element_get_state (playbin, NULL, NULL, SEEK_TIMEOUT);
diff --git a/src/Backends/GStreamer/Thumbnail.cs b/src/Backends/GStreamer/Thumbnail.cs
index 8554a72..907faad 100644
--- a/src/Backends/GStreamer/Thumbnail.cs
+++ b/src/Backends/GStreamer/Thumbnail.cs
@@ -33,7 +33,7 @@ namespace Mistelix.Backends.GStreamer
 	public static class Thumbnail
 	{
 		[DllImport ("libmistelix")]	
-		static extern void mistelix_video_screenshot (string filein, out IntPtr pixbuf);
+		static extern void mistelix_video_screenshot (string filein, int second, out IntPtr pixbuf);
 	
 		
 		static Thumbnail ()
@@ -41,12 +41,12 @@ namespace Mistelix.Backends.GStreamer
 
 		}
 
-		static public Gdk.Pixbuf VideoScreenshot (string file)
+		static public Gdk.Pixbuf VideoScreenshot (string file, int second)
 		{
 			Gdk.Pixbuf thumbnail;
 			IntPtr pix;
 
-			mistelix_video_screenshot (file, out pix);
+			mistelix_video_screenshot (file, second, out pix);
 
 			if (pix == IntPtr.Zero)
 				throw new InvalidOperationException ("No screenshot taken");
diff --git a/src/Core/Button.cs b/src/Core/Button.cs
index fcb9630..7f4813f 100644
--- a/src/Core/Button.cs
+++ b/src/Core/Button.cs
@@ -50,6 +50,17 @@ namespace Mistelix.Core
 		{
 		}
 
+		public override int ThumbnailIndex {
+			get { return base.ThumbnailIndex;}
+			set { 
+				if (base.ThumbnailIndex == value)
+					return;
+
+				base.ThumbnailIndex = value;
+				InvalidateThumbnail ();
+			}
+		}
+
 		// Space needed horizontally to represent the object
 		public int DrawingBoxWidth {
 			get {
@@ -144,6 +155,7 @@ namespace Mistelix.Core
 		{
 			Gdk.Pixbuf pix = null;
 			Resolution size;
+			Gdk.Pixbuf im;
 
 			size = ThumbnailSizeManager.List [project.Details.ButtonThumbnailSize];
 
@@ -155,7 +167,25 @@ namespace Mistelix.Core
 				if (element is SlideShowProjectElement == false && element is VideoProjectElement == false)
 					throw new InvalidOperationException ("Only videos and slideshows allowed");
 
-				pix =  element.GetThumbnail (size.Width, size.Height);
+				if (element is SlideShowProjectElement) {
+					SlideShowProjectElement slide;
+					string file;
+
+					slide = (SlideShowProjectElement) element;
+					file = slide.images[ThumbnailIndex].image;
+					im = new Gdk.Pixbuf (file);
+				}
+				else { // VideoProjectElement
+					VideoProjectElement video;
+
+					video = (VideoProjectElement) element;
+					im = Backends.GStreamer.Thumbnail.VideoScreenshot (video.filename, ThumbnailIndex);
+				}
+
+				int max = Math.Max (im.Width, im.Height);
+				Gdk.Pixbuf scaled = im.ScaleSimple (size.Width * im.Width / max, size.Height * im.Height / max, InterpType.Nearest);
+				im.Dispose ();
+				pix = scaled;
 
 				imgbox_width = pix.Width;
 				imgbox_height = pix.Height;
diff --git a/src/Core/SlideShow.cs b/src/Core/SlideShow.cs
index 983011c..4314595 100644
--- a/src/Core/SlideShow.cs
+++ b/src/Core/SlideShow.cs
@@ -22,6 +22,7 @@
 //
 
 using System;
+using Gdk;
 
 using Mistelix.Transitions;
 using Mistelix.Core;
@@ -119,7 +120,36 @@ namespace Mistelix.Core
 			Backends.GStreamer.SlideShow.Close ();
 			Logger.Debug ("SlideShow.Generate. Finished generation");
 			return project.FileToFullPath (outfile);
-		}			
+		}
+
+		public override ThumbnailCollection GetThumbnailRepresentations (int width, int height)
+		{
+			ThumbnailCollection collection = new ThumbnailCollection ();
+			int idx = 0;
+
+			foreach (SlideImage image in images)
+			{
+				ThumbnailCollection.ItemTask task = new ThumbnailCollection.ItemTask (image);
+				task.ThumbnailIndex = idx++;
+				task.DoEventHandler += delegate (object obj, EventArgs args)
+				{
+					ThumbnailCollection.ItemTask item = (ThumbnailCollection.ItemTask) obj;
+					SlideImage img = (SlideImage) item.Data;
+					Gdk.Pixbuf im = Backends.ThumbnailCache.Factory.Provider.GetThumbnail (img.image, width, height);
+
+					if (im == null)
+						im = new Gdk.Pixbuf (img.image);
+
+					int max = Math.Max (im.Width, im.Height);
+					Gdk.Pixbuf scaled = im.ScaleSimple (width * im.Width / max, height * im.Height / max, InterpType.Nearest);
+
+					item.Pixbuf = scaled;
+					im.Dispose ();
+				};
+				collection.AddItem (task);
+			}
+			return collection;
+		}
 	}
 }
 
diff --git a/src/Core/TaskDispatcher.cs b/src/Core/TaskDispatcher.cs
index bdf7a7d..fee95cc 100644
--- a/src/Core/TaskDispatcher.cs
+++ b/src/Core/TaskDispatcher.cs
@@ -41,6 +41,10 @@ namespace Mistelix.Core
 			tasks = new List <Task> ();
 		}
 
+		public List <Task> Tasks {
+			get { return tasks; }
+		}
+
 		public void AddTask (Task task)
 		{
 			lock (tasks) {
diff --git a/src/Core/Video.cs b/src/Core/Video.cs
index e4b24cf..2bb97b0 100644
--- a/src/Core/Video.cs
+++ b/src/Core/Video.cs
@@ -35,6 +35,12 @@ namespace Mistelix.Core
 {
 	public class Video : VideoProjectElement
 	{
+		public struct ThumbVideo
+		{
+			public string filename;
+			public int second;
+		}
+
 		public Video ()
 		{
 		}
@@ -64,5 +70,41 @@ namespace Mistelix.Core
 			rslt =  Backends.GStreamer.Video.Convert (filename, outfile, (uint) project.Details.FramesPerSecond);
 			return outfile;
 		}
+
+		public override ThumbnailCollection GetThumbnailRepresentations (int width, int height)
+		{
+			ThumbnailCollection collection = new ThumbnailCollection ();
+			int [] seconds = {0, 5, 10, 15, 20, 60};
+
+			foreach (int second in seconds)
+			{
+				ThumbnailCollection.ItemTask task;
+				ThumbVideo thumb;
+
+				thumb = new ThumbVideo ();
+				thumb.filename = filename;
+				thumb.second = second;
+				task = new ThumbnailCollection.ItemTask (thumb);
+				task.ThumbnailIndex = second;
+
+				task.DoEventHandler += delegate (object obj, EventArgs args)
+				{
+					ThumbnailCollection.ItemTask item = (ThumbnailCollection.ItemTask) obj;
+					ThumbVideo thumbv = (ThumbVideo) item.Data;
+					Gdk.Pixbuf im = Backends.GStreamer.Thumbnail.VideoScreenshot (thumbv.filename, thumbv.second);
+
+					if (im == null)
+						return;
+
+					int max = Math.Max (im.Width, im.Height);
+					Gdk.Pixbuf scaled = im.ScaleSimple (width * im.Width / max, height * im.Height / max, InterpType.Nearest);
+
+					item.Pixbuf = scaled;
+					im.Dispose ();
+				};
+				collection.AddItem (task);
+			}
+			return collection;
+		}
 	}
 }
diff --git a/src/DataModel/ButtonProjectElement.cs b/src/DataModel/ButtonProjectElement.cs
index 8d0c666..e1340c2 100644
--- a/src/DataModel/ButtonProjectElement.cs
+++ b/src/DataModel/ButtonProjectElement.cs
@@ -42,7 +42,8 @@ namespace Mistelix.DataModel
 		int y;
 		int width;
 		int height;
-		int linked_id; // Associated slide or video
+		int linked_id; // Associated slideshow or video
+		int thumbnail_idx; // index to thumbnail slideshow or video
 		string text;
 		ButtonShowAs showas;
 
@@ -89,6 +90,13 @@ namespace Mistelix.DataModel
 			set { linked_id = value;}
 		}
 
+		// Index to image in a slideshow or second in a video element
+		[XmlElementAttribute ("thumbnail")]
+		public virtual int ThumbnailIndex {
+			get { return thumbnail_idx;}
+			set { thumbnail_idx = value;}
+		}
+
 		[XmlElementAttribute ("showas", DataType="int")]
 		public ButtonShowAs ShowAs {
 			get { return showas;}
diff --git a/src/DataModel/ThumbnailCollection.cs b/src/DataModel/ThumbnailCollection.cs
new file mode 100644
index 0000000..d88e48e
--- /dev/null
+++ b/src/DataModel/ThumbnailCollection.cs
@@ -0,0 +1,78 @@
+//
+// Copyright (C) 2009 Jordi Mas i Hernandez, jmas softcatala org
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using Gdk;
+
+using Mistelix.Core;
+
+namespace Mistelix.DataModel
+{
+	// Every project visible element can have several thumbnails that can represent it
+	// This collection is the base to hold these representations
+	public class ThumbnailCollection : IEnumerable
+	{
+		public class ItemTask : Task
+		{
+			Pixbuf pixbuf;
+			int thumbnail_idx;
+
+			public ItemTask (object obj) : base (obj)
+			{
+
+			}
+			
+			public Pixbuf Pixbuf {
+				get {return pixbuf; }
+				set {pixbuf = value;}
+			}
+
+			public int ThumbnailIndex {
+				get {return thumbnail_idx; }
+				set {thumbnail_idx = value;}
+			}
+		}
+
+		TaskDispatcher dispatcher;
+
+		public ThumbnailCollection ()
+		{
+			dispatcher = new TaskDispatcher ();
+		}
+
+		public TaskDispatcher Dispatcher {
+			get { return dispatcher; }
+		}
+
+		public void AddItem (ItemTask item)
+		{
+			dispatcher.AddTask (item);
+		}
+
+		public IEnumerator GetEnumerator ()
+		{
+			return dispatcher.Tasks.GetEnumerator ();
+		}
+	}
+}
diff --git a/src/DataModel/VideoProjectElement.cs b/src/DataModel/VideoProjectElement.cs
index 746ecb7..a0815d8 100644
--- a/src/DataModel/VideoProjectElement.cs
+++ b/src/DataModel/VideoProjectElement.cs
@@ -36,6 +36,8 @@ namespace Mistelix.DataModel
 	[XmlInclude (typeof (Core.Video))]
 	public class VideoProjectElement : VisibleProjectElement
 	{
+		const int SECOND_FOR_SHOOT = 5;
+
 		public VideoProjectElement ()
 		{
 		}
@@ -63,7 +65,7 @@ namespace Mistelix.DataModel
 		public override Gdk.Pixbuf GetThumbnail (int width, int height)
 		{
 			Gdk.Pixbuf im;
-			im = Backends.GStreamer.Thumbnail.VideoScreenshot (filename);
+			im = Backends.GStreamer.Thumbnail.VideoScreenshot (filename, SECOND_FOR_SHOOT);
 			int max = Math.Max (im.Width, im.Height);
 			Gdk.Pixbuf scaled = im.ScaleSimple (width * im.Width / max, height * im.Height / max, InterpType.Nearest);
 			im.Dispose ();
diff --git a/src/DataModel/VisibleProjectElement.cs b/src/DataModel/VisibleProjectElement.cs
index 4aec1af..50a1bbc 100644
--- a/src/DataModel/VisibleProjectElement.cs
+++ b/src/DataModel/VisibleProjectElement.cs
@@ -27,6 +27,8 @@ using Mistelix.Core;
 
 namespace Mistelix.DataModel
 {
+	// A visible project element can be part of a DVD menu and it is visible in project element view
+	// These are videos or slideshows
 	public abstract class VisibleProjectElement : ProjectElement
 	{
 		public string filename;
@@ -45,5 +47,7 @@ namespace Mistelix.DataModel
 				                     ThumbnailSizeManager.Current.Height); 
 			}
 		}
+
+		public virtual ThumbnailCollection GetThumbnailRepresentations (int width, int height) { return null; }
 	}
 }
diff --git a/src/Dialogs/ButtonPropertiesDialog.cs b/src/Dialogs/ButtonPropertiesDialog.cs
index e4bc3fd..d6a5a14 100644
--- a/src/Dialogs/ButtonPropertiesDialog.cs
+++ b/src/Dialogs/ButtonPropertiesDialog.cs
@@ -24,6 +24,9 @@
 using System;
 using Glade;
 using Gtk;
+using Gdk;
+using System.Collections.Generic;
+using System.ComponentModel;
 
 using Mistelix.Widgets;
 using Mistelix.DataModel;
@@ -36,10 +39,20 @@ namespace Mistelix.Dialogs
 	{
 		Project project;
 		int button_id;
+		ThumbnailCollection thumbnails;
+		ListStore store;
+		Pixbuf def_image;
+		Dictionary <ThumbnailCollection.ItemTask, Gtk.TreeIter> mapping;
+		BackgroundWorker thumbnailing;
+
 		[Glade.Widget] Gtk.Entry element_entry;
 		[Glade.Widget] Gtk.RadioButton image_radio;
 		[Glade.Widget] Gtk.RadioButton text_radio;
 		[Glade.Widget] Gtk.Button ok_button;
+		[Glade.Widget] Gtk.IconView thumbnails_iconview;
+
+		const int COL_PIXBUF = 0;
+		const int COL_INDEX = 1;
 
 		public ButtonPropertiesDialog (Project project, int button_id) : base ("buttonproperties")
 		{
@@ -60,6 +73,103 @@ namespace Mistelix.Dialogs
 
 			image_radio.Toggled += new EventHandler (OnCheckToggled);
 			element_entry.Changed += OnChangedText;
+
+			CreateDefaultImage ();
+			
+			thumbnails_iconview.PixbufColumn = 0;
+			mapping = new Dictionary <ThumbnailCollection.ItemTask, Gtk.TreeIter> ();
+			thumbnails_iconview.Model = store = CreateStore ();
+
+			thumbnailing = new BackgroundWorker ();
+			thumbnailing.WorkerSupportsCancellation = true;
+			thumbnailing.DoWork += new DoWorkEventHandler (DoWork);
+
+			LoadThumbnails ();
+		}
+
+		void CreateDefaultImage ()
+		{
+			const int channels = 4;
+			Cairo.ImageSurface image;
+			int thumbnail_width, thumbnail_height;
+
+			thumbnail_width = ThumbnailSizeManager.List [project.Details.ButtonThumbnailSize].Width;
+			thumbnail_height = ThumbnailSizeManager.List [project.Details.ButtonThumbnailSize].Height;
+
+			image = Utils.CreateNoPreviewImage (thumbnail_width, thumbnail_height);
+			byte[] data = DataImageSurface.FromCairo32ToPixBuf (image.Data, thumbnail_width, thumbnail_height, channels);
+			def_image = new Pixbuf (data, true, 8, thumbnail_width, thumbnail_height, thumbnail_width * channels, null);
+			image.Destroy ();
+		}
+
+		ListStore CreateStore ()
+		{
+			// Pixbuf
+			return new ListStore (typeof (Pixbuf), typeof (int));
+		}
+
+		void DoWork (object sender, DoWorkEventArgs e)
+        	{
+			Task task;
+
+			while (true) {
+
+				if (thumbnailing.CancellationPending)
+					break;
+
+				task = thumbnails.Dispatcher.GetNextTask ();
+
+				if (task == null)
+					break;
+
+				task.Do ();
+			}
+		}
+
+		void LoadThumbnails ()
+		{
+			VisibleProjectElement element;
+			Resolution size;
+			Gtk.TreeIter selected_iter = Gtk.TreeIter.Zero;
+
+			size = ThumbnailSizeManager.List [project.Details.ButtonThumbnailSize];
+
+			element = project.ElementFromID (project.Buttons[button_id].LinkedId);
+			thumbnails = element.GetThumbnailRepresentations (size.Width, size.Height);
+
+			foreach (ThumbnailCollection.ItemTask task in thumbnails)
+			{
+				Gtk.TreeIter iter;
+
+				iter = store.AppendValues (def_image, task.ThumbnailIndex);
+				mapping.Add (task, iter);
+
+				if (task.ThumbnailIndex == project.Buttons[button_id].ThumbnailIndex)
+					selected_iter = iter;
+
+				task.CompletedEventHandler += delegate (object obj, EventArgs args)
+				{
+					ThumbnailCollection.ItemTask itemtask = (ThumbnailCollection.ItemTask) obj;
+					Gtk.TreeIter tree_iter;
+
+					try
+					{
+						mapping.TryGetValue (itemtask, out tree_iter);
+					}
+					catch (KeyNotFoundException)
+					{
+					    	Logger.Error ("ButtonPropertiesDialog.LoadThumbnails. Cannot find key");
+						return;
+					}
+
+					Application.Invoke (delegate { store.SetValue (tree_iter, COL_PIXBUF, itemtask.Pixbuf); });
+				};
+			}
+
+			TreePath path = store.GetPath (selected_iter);
+			thumbnails_iconview.SelectPath (path);
+
+			thumbnailing.RunWorkerAsync (store);
 		}
 
 		void OnChangedText (object sender, EventArgs args)
@@ -84,12 +194,20 @@ namespace Mistelix.Dialogs
 		
 		void OnOK (object sender, EventArgs args)
 		{
-			if (image_radio.Active)
-				project.Buttons[button_id].ShowAs = ButtonShowAs.Thumbnail;
-			else {
+			if (image_radio.Active == false) { // Text
 				project.Buttons[button_id].ShowAs = ButtonShowAs.Text;
 				project.Buttons[button_id].Text = element_entry.Text;
+				return;
 			}
+
+			project.Buttons[button_id].ShowAs = ButtonShowAs.Thumbnail;
+			TreeIter iter;
+			int idx;
+			TreePath[] items = thumbnails_iconview.SelectedItems;
+
+			store.GetIter (out iter, items [0]);
+			idx = (int) store.GetValue (iter, COL_INDEX);
+			project.Buttons[button_id].ThumbnailIndex = idx;
 		}
 
 		void OnCheckToggled (object obj, EventArgs args)
@@ -97,5 +215,23 @@ namespace Mistelix.Dialogs
 			element_entry.Sensitive = text_radio.Active;
 			EnableOKButton ();
 		}
+
+		public override void Destroy ()
+		{
+			thumbnailing.CancelAsync ();
+			thumbnailing.Dispose ();
+
+			store.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter)
+			{
+				Gdk.Pixbuf im = (Gdk.Pixbuf) store.GetValue (iter, COL_PIXBUF);
+				if (def_image != im)
+					im.Dispose ();
+
+				return false;
+			});
+
+			def_image.Dispose ();
+			base.Destroy ();
+		}
 	}
 }
diff --git a/src/Makefile.am b/src/Makefile.am
index 15ffd23..7c29c46 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -82,7 +82,8 @@ MISTELIX_CSDISTFILES =				\
 	$(srcdir)/Backends/ThumbnailCache/None.cs \
 	$(srcdir)/Widgets/DataMenuItem.cs \
 	$(srcdir)/DataModel/Task.cs \
-	$(srcdir)/Core/TaskDispatcher.cs
+	$(srcdir)/Core/TaskDispatcher.cs \
+	$(srcdir)/DataModel/ThumbnailCollection.cs
 
 ASSEMBLIES = \
 	 $(MISTELIX_LIBS)    		\
diff --git a/src/Widgets/VideosFileView.cs b/src/Widgets/VideosFileView.cs
index 620abbe..31ab999 100644
--- a/src/Widgets/VideosFileView.cs
+++ b/src/Widgets/VideosFileView.cs
@@ -39,6 +39,7 @@ namespace Mistelix.Widgets
 	//
 	public class VideosFileView : FileView
 	{
+		const int SECOND_FOR_SHOOT = 5;
 		public VideosFileView ()
 		{
 		}
@@ -76,7 +77,7 @@ namespace Mistelix.Widgets
 				Gdk.Pixbuf im;
 
 				// TODO: This should be at VideoProjectElement.GetThumbnail
-				im = Backends.GStreamer.Thumbnail.VideoScreenshot (file);
+				im = Backends.GStreamer.Thumbnail.VideoScreenshot (file, SECOND_FOR_SHOOT);
 
 				int max = Math.Max (im.Width, im.Height);
 				Gdk.Pixbuf scaled = im.ScaleSimple (thumbnail_width * im.Width / max, thumbnail_height * im.Height / max, InterpType.Nearest);
diff --git a/src/mistelix.glade b/src/mistelix.glade
index 63d0d3f..459ce0c 100644
--- a/src/mistelix.glade
+++ b/src/mistelix.glade
@@ -2808,6 +2808,8 @@
 </widget>
 
 <widget class="GtkDialog" id="buttonproperties">
+  <property name="width_request">500</property>
+  <property name="height_request">400</property>
   <property name="visible">True</property>
   <property name="title" translatable="yes">Button Properties</property>
   <property name="type">GTK_WINDOW_TOPLEVEL</property>
@@ -2927,6 +2929,45 @@
 	      </child>
 
 	      <child>
+		<widget class="GtkHBox" id="thumbanails_vbox">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">0</property>
+
+		  <child>
+		    <widget class="GtkScrolledWindow" id="thumbanails_scrolled">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		      <property name="shadow_type">GTK_SHADOW_IN</property>
+		      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+		      <child>
+			<widget class="GtkIconView" id="thumbnails_iconview">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="selection_mode">GTK_SELECTION_SINGLE</property>
+			  <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
+			  <property name="reorderable">False</property>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">15</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
 		<widget class="GtkRadioButton" id="text_radio">
 		  <property name="visible">True</property>
 		  <property name="can_focus">True</property>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]