brasero r923 - in branches/video: . src



Author: philippr
Date: Fri Jun 27 13:29:09 2008
New Revision: 923
URL: http://svn.gnome.org/viewvc/brasero?rev=923&view=rev

Log:
	Modify the display widget for video project and split it between view and model
	Added features to this widget:
	- rename (not very useful ATM)
	- directory search for video files
	- DND
	- projects support

	* src/Makefile.am:
	* src/brasero-disc.h:
	* src/brasero-io.c (brasero_io_load_directory_thread):
	* src/brasero-marshal.list:
	* src/brasero-project.c (brasero_project_set_uri),
	(_read_audio_track), (_get_tracks), (brasero_project_open_project),
	(brasero_project_load_session), (brasero_project_save_project_xml):
	* src/brasero-video-disc.c
	(brasero_video_disc_name_editing_started_cb),
	(brasero_video_disc_name_editing_canceled_cb),
	(brasero_video_disc_name_edited_cb),
	(brasero_video_disc_vfs_activity_changed),
	(brasero_video_disc_directory_dialog),
	(brasero_video_disc_unreadable_uri_dialog),
	(brasero_video_disc_not_video_dialog),
	(brasero_video_disc_add_uri_real),
	(brasero_video_disc_delete_selected),
	(brasero_video_disc_get_selected_uri),
	(brasero_video_disc_selection_function),
	(brasero_video_disc_row_deleted_cb),
	(brasero_video_disc_row_inserted_cb),
	(brasero_video_disc_row_changed_cb),
	(brasero_video_disc_size_changed_cb), (brasero_video_disc_init),
	(brasero_video_disc_reset_real), (brasero_video_disc_clear),
	(brasero_video_disc_reset), (brasero_video_disc_finalize),
	(brasero_video_disc_get_status),
	(brasero_video_disc_set_session_param),
	(brasero_video_disc_set_session_contents),
	(brasero_video_disc_get_track), (brasero_video_disc_load_track),
	(brasero_video_disc_iface_disc_init):
	* src/brasero-video-project.c (brasero_video_project_file_signal),
	(brasero_video_project_reference_new),
	(brasero_video_project_reference_free),
	(brasero_video_project_reference_get),
	(brasero_video_project_reference_remove_children_cb),
	(brasero_video_project_reference_invalidate),
	(brasero_video_project_rename), (brasero_video_project_move),
	(brasero_video_file_free),
	(brasero_video_project_foreach_monitor_cancel_cb),
	(brasero_video_project_remove_file), (brasero_video_project_reset),
	(brasero_video_project_add_video_file),
	(brasero_video_project_set_file_information),
	(brasero_video_project_vfs_operation_finished),
	(brasero_video_project_add_directory_contents_result),
	(brasero_video_project_add_directory_contents),
	(brasero_video_project_result_cb), (brasero_video_project_add_uri),
	(brasero_video_project_get_size),
	(brasero_video_project_get_file_num),
	(brasero_video_project_get_nth_item),
	(brasero_video_project_get_item_index),
	(brasero_video_project_get_status),
	(brasero_video_project_get_contents), (brasero_video_project_init),
	(brasero_video_project_finalize),
	(brasero_video_project_file_renamed),
	(brasero_video_project_file_moved),
	(brasero_video_project_file_removed),
	(brasero_video_project_file_modified),
	(brasero_video_project_class_init):
	* src/brasero-video-project.h:
	* src/brasero-video-tree-model.c
	(brasero_video_tree_model_iter_parent),
	(brasero_video_tree_model_iter_nth_child),
	(brasero_video_tree_model_iter_n_children),
	(brasero_video_tree_model_iter_has_child),
	(brasero_video_tree_model_iter_children),
	(brasero_video_tree_model_get_value),
	(brasero_video_tree_model_file_to_path),
	(brasero_video_tree_model_get_path),
	(brasero_video_tree_model_path_to_file),
	(brasero_video_tree_model_get_iter),
	(brasero_video_tree_model_iter_next),
	(brasero_video_tree_model_get_column_type),
	(brasero_video_tree_model_get_n_columns),
	(brasero_video_tree_model_get_flags),
	(brasero_video_tree_model_multi_row_draggable),
	(brasero_video_tree_model_multi_drag_data_get),
	(brasero_video_tree_model_multi_drag_data_delete),
	(brasero_video_tree_model_drag_data_received),
	(brasero_video_tree_model_row_drop_possible),
	(brasero_video_tree_model_drag_data_delete),
	(brasero_video_tree_model_clear), (brasero_video_tree_model_reset),
	(brasero_video_tree_model_file_added),
	(brasero_video_tree_model_file_removed),
	(brasero_video_tree_model_file_changed),
	(brasero_video_tree_model_init),
	(brasero_video_tree_model_finalize),
	(brasero_video_tree_model_iface_init),
	(brasero_video_tree_model_multi_drag_source_iface_init),
	(brasero_video_tree_model_drag_source_iface_init),
	(brasero_video_tree_model_drag_dest_iface_init),
	(brasero_video_tree_model_class_init),
	(brasero_video_tree_model_new):
	* src/brasero-video-tree-model.h:

Added:
   branches/video/src/brasero-video-project.c   (contents, props changed)
   branches/video/src/brasero-video-project.h   (contents, props changed)
   branches/video/src/brasero-video-tree-model.c   (contents, props changed)
   branches/video/src/brasero-video-tree-model.h   (contents, props changed)
Modified:
   branches/video/ChangeLog
   branches/video/src/Makefile.am
   branches/video/src/brasero-disc.h
   branches/video/src/brasero-io.c
   branches/video/src/brasero-marshal.list
   branches/video/src/brasero-project.c
   branches/video/src/brasero-video-disc.c

Modified: branches/video/src/Makefile.am
==============================================================================
--- branches/video/src/Makefile.am	(original)
+++ branches/video/src/Makefile.am	Fri Jun 27 13:29:09 2008
@@ -262,7 +262,11 @@
 	burn-volume-source.c         \
 	burn-volume-source.h         \
 	brasero-video-disc.c         \
-	brasero-video-disc.h         
+	brasero-video-disc.h         \
+	brasero-video-project.h         \
+	brasero-video-project.c         \
+	brasero-video-tree-model.c         \
+	brasero-video-tree-model.h
 
 if BUILD_INOTIFY
 brasero_SOURCES += brasero-file-monitor.c brasero-file-monitor.h

Modified: branches/video/src/brasero-disc.h
==============================================================================
--- branches/video/src/brasero-disc.h	(original)
+++ branches/video/src/brasero-disc.h	Fri Jun 27 13:29:09 2008
@@ -63,8 +63,9 @@
 } BraseroDiscResult;
 
 typedef enum {
-	BRASERO_DISC_TRACK_NONE,
+	BRASERO_DISC_TRACK_NONE = 0,
 	BRASERO_DISC_TRACK_AUDIO,
+	BRASERO_DISC_TRACK_VIDEO,
 	BRASERO_DISC_TRACK_DATA,
 } BraseroDiscTrackType;
 

Modified: branches/video/src/brasero-io.c
==============================================================================
--- branches/video/src/brasero-io.c	(original)
+++ branches/video/src/brasero-io.c	Fri Jun 27 13:29:09 2008
@@ -1634,7 +1634,7 @@
 		g_object_unref (child);
 	}
 
-	if (data->job.callback_data->ref < 2) {
+	if (data->job.callback_data && data->job.callback_data->ref < 2) {
 		/* No result was returned so we need to return a dummy one to 
 		 * clean the callback_data in the main loop. */
 		brasero_io_return_result (BRASERO_IO (manager),

Modified: branches/video/src/brasero-marshal.list
==============================================================================
--- branches/video/src/brasero-marshal.list	(original)
+++ branches/video/src/brasero-marshal.list	Fri Jun 27 13:29:09 2008
@@ -7,6 +7,7 @@
 VOID:DOUBLE,STRING
 VOID:INT64
 VOID:INT,STRING
+VOID:POINTER,STRING
 VOID:DOUBLE,DOUBLE,LONG
 INT:OBJECT,INT,INT
 BOOLEAN:STRING

Modified: branches/video/src/brasero-project.c
==============================================================================
--- branches/video/src/brasero-project.c	(original)
+++ branches/video/src/brasero-project.c	Fri Jun 27 13:29:09 2008
@@ -1742,9 +1742,11 @@
     	BRASERO_GET_BASENAME_FOR_DISPLAY (uri, name);
 	if (type == BRASERO_PROJECT_TYPE_DATA)
 		title = g_strdup_printf (_("Brasero - %s (data disc)"), name);
-	else
+	else if (type == BRASERO_PROJECT_TYPE_AUDIO)
 		title = g_strdup_printf (_("Brasero - %s (audio disc)"), name);
-    	g_free (name);
+	else if (type == BRASERO_PROJECT_TYPE_AUDIO)
+		title = g_strdup_printf (_("Brasero - %s (Video Disc)"), name);
+	g_free (name);
 
 	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (project));
 	gtk_window_set_title (GTK_WINDOW (toplevel), title);
@@ -1901,8 +1903,6 @@
 	BraseroDiscSong *song;
 
 	track = g_new0 (BraseroDiscTrack, 1);
-	track->type = BRASERO_DISC_TRACK_AUDIO;
-
 	song = NULL;
 
 	while (uris) {
@@ -2006,6 +2006,8 @@
 						      track_node->xmlChildrenNode);
 			if (!newtrack)
 				goto error;
+
+			newtrack->type = BRASERO_DISC_TRACK_AUDIO;
 		}
 		else if (!xmlStrcmp (track_node->name, (const xmlChar *) "data")) {
 			if (newtrack)
@@ -2017,6 +2019,18 @@
 			if (!newtrack)
 				goto error;
 		}
+		else if (!xmlStrcmp (track_node->name, (const xmlChar *) "video")) {
+			if (newtrack)
+				goto error;
+
+			newtrack = _read_audio_track (project,
+						      track_node->xmlChildrenNode);
+
+			if (!newtrack)
+				goto error;
+
+			newtrack->type = BRASERO_DISC_TRACK_VIDEO;
+		}
 		else if (track_node->type == XML_ELEMENT_NODE)
 			goto error;
 
@@ -2129,19 +2143,19 @@
 	brasero_project_size_set_sectors (BRASERO_PROJECT_SIZE (project->priv->size_display),
 					  0);
 
-	if (track->type == BRASERO_DISC_TRACK_AUDIO) {
-		brasero_project_switch (project, TRUE);
+	if (track->type == BRASERO_DISC_TRACK_AUDIO)
 		type = BRASERO_PROJECT_TYPE_AUDIO;
-	}
-	else if (track->type == BRASERO_DISC_TRACK_DATA) {
-		brasero_project_switch (project, FALSE);
+	else if (track->type == BRASERO_DISC_TRACK_DATA)
 		type = BRASERO_PROJECT_TYPE_DATA;
-	}
+	else if (track->type == BRASERO_DISC_TRACK_VIDEO)
+		type = BRASERO_PROJECT_TYPE_VIDEO;
 	else {
 		brasero_track_free (track);
 		return BRASERO_PROJECT_TYPE_INVALID;
 	}
 
+	brasero_project_switch (project, type);
+
 	brasero_disc_load_track (project->priv->current, track);
 	brasero_track_free (track);
 
@@ -2247,19 +2261,19 @@
 	if (!brasero_project_open_project_xml (project, uri, &track, FALSE))
 		return BRASERO_PROJECT_TYPE_INVALID;
 
-	if (track->type == BRASERO_DISC_TRACK_AUDIO) {
-		brasero_project_switch (project, TRUE);
+	if (track->type == BRASERO_DISC_TRACK_AUDIO)
 		type = BRASERO_PROJECT_TYPE_AUDIO;
-	}
-	else if (track->type == BRASERO_DISC_TRACK_DATA) {
-		brasero_project_switch (project, FALSE);
+	else if (track->type == BRASERO_DISC_TRACK_DATA)
 		type = BRASERO_PROJECT_TYPE_DATA;
-	}
+	else if (track->type == BRASERO_DISC_TRACK_VIDEO)
+		type = BRASERO_PROJECT_TYPE_VIDEO;
 	else {
 	    	brasero_track_free (track);
 		return BRASERO_PROJECT_TYPE_INVALID;
 	}
 
+	brasero_project_switch (project, type);
+
 	brasero_disc_load_track (project->priv->current, track);
 	brasero_track_free (track);
 
@@ -2542,7 +2556,20 @@
 		if (success < 0)
 			goto error;
 	}
-	else 
+	else  if (track->type == BRASERO_DISC_TRACK_VIDEO) {
+		success = xmlTextWriterStartElement (project, (xmlChar *) "video");
+		if (success < 0)
+			goto error;
+
+		retval = _save_audio_track_xml (project, track);
+		if (!retval)
+			goto error;
+
+		success = xmlTextWriterEndElement (project); /* audio */
+		if (success < 0)
+			goto error;
+	}
+	else
 		retval = FALSE;
 
 	success = xmlTextWriterEndElement (project); /* track */

Modified: branches/video/src/brasero-video-disc.c
==============================================================================
--- branches/video/src/brasero-video-disc.c	(original)
+++ branches/video/src/brasero-video-disc.c	Fri Jun 27 13:29:09 2008
@@ -28,10 +28,14 @@
 
 #include <gtk/gtk.h>
 
+#include "eggtreemultidnd.h"
+
 #include "brasero-disc.h"
 #include "brasero-io.h"
 #include "brasero-utils.h"
 #include "brasero-video-disc.h"
+#include "brasero-video-project.h"
+#include "brasero-video-tree-model.h"
 
 typedef struct _BraseroVideoDiscPrivate BraseroVideoDiscPrivate;
 struct _BraseroVideoDiscPrivate
@@ -43,14 +47,8 @@
 	GtkUIManager *manager;
 	GtkActionGroup *disc_group;
 
-	BraseroIO *io;
-	BraseroIOJobBase *add_uri;
-
-	gint64 sectors;
-
-	guint activity;
-
 	guint reject_files:1;
+	guint editing:1;
 	guint loading:1;
 };
 
@@ -66,66 +64,150 @@
 					        brasero_video_disc_iface_disc_init));
 
 enum {
-	NAME_COL,
-	URI_COL,
-	ICON_COL,
-	SIZE_COL,
-	START_COL,
-	END_COL,
-	EDITABLE_COL,
-	NUM_COL
+	PROP_NONE,
+	PROP_REJECT_FILE,
 };
 
 enum {
-	PROP_NONE,
-	PROP_REJECT_FILE,
+	TREE_MODEL_ROW		= 150,
+	TARGET_URIS_LIST,
+};
+
+static GtkTargetEntry ntables_cd [] = {
+	{BRASERO_DND_TARGET_SELF_FILE_NODES, GTK_TARGET_SAME_WIDGET, TREE_MODEL_ROW},
+	{"text/uri-list", 0, TARGET_URIS_LIST}
+};
+static guint nb_targets_cd = sizeof (ntables_cd) / sizeof (ntables_cd[0]);
+
+static GtkTargetEntry ntables_source [] = {
+	{BRASERO_DND_TARGET_SELF_FILE_NODES, GTK_TARGET_SAME_WIDGET, TREE_MODEL_ROW},
 };
 
+static guint nb_targets_source = sizeof (ntables_source) / sizeof (ntables_source[0]);
+
+
+/**
+ * Row name edition
+ */
 
 static void
-brasero_video_disc_increase_activity_counter (BraseroVideoDisc *self)
+brasero_video_disc_name_editing_started_cb (GtkCellRenderer *renderer,
+					    GtkCellEditable *editable,
+					    gchar *path,
+					    BraseroVideoDisc *disc)
 {
-	GdkCursor *cursor;
 	BraseroVideoDiscPrivate *priv;
 
-	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
+	priv = BRASERO_VIDEO_DISC_PRIVATE (disc);
+	priv->editing = 1;
+}
 
-	if (priv->activity == 0 && GTK_WIDGET (self)->window) {
-		cursor = gdk_cursor_new (GDK_WATCH);
-		gdk_window_set_cursor (GTK_WIDGET (self)->window, cursor);
-		gdk_cursor_unref (cursor);
-	}
+static void
+brasero_video_disc_name_editing_canceled_cb (GtkCellRenderer *renderer,
+					     BraseroVideoDisc *disc)
+{
+	BraseroVideoDiscPrivate *priv;
 
-	priv->activity++;
+	priv = BRASERO_VIDEO_DISC_PRIVATE (disc);
+	priv->editing = 0;
 }
 
 static void
-brasero_video_disc_decrease_activity_counter (BraseroVideoDisc *self)
+brasero_video_disc_name_edited_cb (GtkCellRendererText *cellrenderertext,
+				   gchar *path_string,
+				   gchar *text,
+				   BraseroVideoDisc *self)
 {
 	BraseroVideoDiscPrivate *priv;
+	BraseroVideoProject *project;
+	BraseroVideoFile *file;
+	GtkTreePath *path;
+	GtkTreeIter row;
 
 	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
 
-	if (priv->activity == 1 && GTK_WIDGET (self)->window)
-		gdk_window_set_cursor (GTK_WIDGET (self)->window, NULL);
+	priv->editing = 0;
+
+	path = gtk_tree_path_new_from_string (path_string);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
+
+	/* see if this is still a valid path. It can happen a user removes it
+	 * while the name of the row is being edited */
+	if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (project), &row, path)) {
+		gtk_tree_path_free (path);
+		return;
+	}
+
+	file = brasero_video_tree_model_path_to_file (BRASERO_VIDEO_TREE_MODEL (project), path);
+	gtk_tree_path_free (path);
 
-	priv->activity--;
+	brasero_video_project_rename (project, file, text);
 }
 
 static void
-brasero_video_disc_io_operation_finished (GObject *object,
-					  gboolean cancelled,
-					  gpointer null_data)
+brasero_video_disc_vfs_activity_changed (BraseroVideoProject *project,
+					 gboolean activity,
+					 BraseroVideoDisc *self)
 {
-	BraseroVideoDisc *self = BRASERO_VIDEO_DISC (object);
+	GdkCursor *cursor;
+	BraseroVideoDiscPrivate *priv;
 
-	brasero_video_disc_decrease_activity_counter (self);
+	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
+
+	if (!GTK_WIDGET (self)->window)
+		return;
+
+	if (activity) {
+		cursor = gdk_cursor_new (GDK_WATCH);
+		gdk_window_set_cursor (GTK_WIDGET (self)->window, cursor);
+		gdk_cursor_unref (cursor);
+	}
+	else
+		gdk_window_set_cursor (GTK_WIDGET (self)->window, NULL);
+}
+
+static gboolean
+brasero_video_disc_directory_dialog (BraseroVideoProject *project,
+				     const gchar *uri,
+				     BraseroVideoDisc *self)
+{
+	gint answer;
+	GtkWidget *dialog;
+	GtkWidget *toplevel;
+
+	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+	dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel),
+					 GTK_DIALOG_DESTROY_WITH_PARENT |
+					 GTK_DIALOG_MODAL,
+					 GTK_MESSAGE_WARNING,
+					 GTK_BUTTONS_NONE,
+					 _("Do you want to search for video files inside the directory?"));
+
+	gtk_window_set_title (GTK_WINDOW (dialog), _("Directory Search"));
+
+	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+						  _("Directories can't be added to video disc."));
+
+	gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+				_("Search directory"), GTK_RESPONSE_OK,
+				NULL);
+
+	gtk_widget_show_all (dialog);
+	answer = gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+
+	if (answer != GTK_RESPONSE_OK)
+		return FALSE;
+
+	return TRUE;
 }
 
 static void
-brasero_video_disc_unreadable_dialog (BraseroVideoDisc *self,
-				      const gchar *uri,
-				      GError *error)
+brasero_video_disc_unreadable_uri_dialog (BraseroVideoProject *project,
+					  GError *error,
+					  const gchar *uri,
+					  BraseroVideoDisc *self)
 {
 	GtkWidget *dialog, *toplevel;
 	gchar *name;
@@ -158,8 +240,9 @@
 }
 
 static void
-brasero_video_disc_file_not_video_dialog (BraseroVideoDisc *self,
-					  const gchar *uri)
+brasero_video_disc_not_video_dialog (BraseroVideoProject *project,
+				     const gchar *uri,
+				     BraseroVideoDisc *self)
 {
 	GtkWidget *dialog, *toplevel;
 	gchar *name;
@@ -189,94 +272,6 @@
 	gtk_widget_destroy (dialog);
 }
 
-static void
-brasero_video_disc_new_row_cb (GObject *obj,
-			       GError *error,
-			       const gchar *uri,
-			       GFileInfo *info,
-			       gpointer user_data)
-{
-	gint64 len;
-	gchar *size_str;
-	GtkTreeIter iter;
-	const gchar *title;
-	GdkPixbuf *snapshot;
-	GtkTreeModel *model;
-	GtkTreePath *treepath;
-	GtkTreeRowReference *ref = user_data;
-	BraseroVideoDisc *self = BRASERO_VIDEO_DISC (obj);
-	BraseroVideoDiscPrivate *priv = BRASERO_VIDEO_DISC_PRIVATE (self);
-
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree));
-	treepath = gtk_tree_row_reference_get_path (ref);
-	gtk_tree_row_reference_free (ref);
-	if (!treepath)
-		return;
-
-	gtk_tree_model_get_iter (model, &iter, treepath);
-	gtk_tree_path_free (treepath);
-
-	if (error) {
-		brasero_video_disc_unreadable_dialog (self, uri, error);
-		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-		return;
-	}
-
-	if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
-//		brasero_video_disc_add_dir (self, uri);
-		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-		return;
-	}
-
-	if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR
-	|| !g_file_info_get_attribute_boolean (info, BRASERO_IO_HAS_VIDEO)) {
-		brasero_video_disc_file_not_video_dialog (self, uri);
-		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-		return;
-	}
-
-	if (g_file_info_get_is_symlink (info)) {
-		uri = g_strconcat ("file://", g_file_info_get_symlink_target (info), NULL);
-		gtk_list_store_set (GTK_LIST_STORE (model), &iter,
-				    URI_COL, uri, -1);
-	}
-
-	/* set the snapshot */
-	snapshot = GDK_PIXBUF (g_file_info_get_attribute_object (info, BRASERO_IO_SNAPSHOT));
-	if (snapshot) {
-		GdkPixbuf *scaled;
-
-		scaled = gdk_pixbuf_scale_simple (snapshot,
-						  96 * gdk_pixbuf_get_width (snapshot) / gdk_pixbuf_get_height (snapshot),
-						  96,
-						  GDK_INTERP_BILINEAR);
-		gtk_list_store_set (GTK_LIST_STORE (model), &iter,
-				    ICON_COL, scaled,
-				    -1);
-		g_object_unref (scaled);
-	}
-
-	/* */
-	len = g_file_info_get_attribute_uint64 (info, BRASERO_IO_LEN);
-	size_str = brasero_utils_get_time_string (len, TRUE, FALSE);
-	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
-			    END_COL, len,
-			    SIZE_COL, size_str,
-			    -1);
-	g_free (size_str);
-
-	/* */
-	title = g_file_info_get_attribute_string (info, BRASERO_IO_TITLE);
-	if (title)
-		gtk_list_store_set (GTK_LIST_STORE (model), &iter,
-				    NAME_COL, title,
-				    -1);
-
-	/* FIXME: duration to sectors is not correct here, that's not audio... */
-	priv->sectors += BRASERO_DURATION_TO_SECTORS (len);
-	brasero_disc_size_changed (BRASERO_DISC (self), priv->sectors);
-}
-
 static BraseroDiscResult
 brasero_video_disc_add_uri_real (BraseroVideoDisc *self,
 				 const gchar *uri,
@@ -285,91 +280,35 @@
 				 gint64 end,
 				 GtkTreePath **path_return)
 {
+	BraseroVideoFile *file;
+	BraseroVideoProject *project;
 	BraseroVideoDiscPrivate *priv;
-	GtkTreeRowReference *ref;
-	GtkTreePath *treepath;
-	GtkTreeModel *store;
-	GtkTreeIter iter;
-	gchar *markup;
-	gchar *name;
-
-	g_return_val_if_fail (uri != NULL, BRASERO_DISC_ERROR_UNKNOWN);
+	BraseroVideoFile *sibling = NULL;
 
 	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
 	if (priv->reject_files)
 		return BRASERO_DISC_NOT_READY;
 
-	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 1);
-
-	store = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree));
-	if (pos > -1)
-		gtk_list_store_insert (GTK_LIST_STORE (store), &iter, pos);
-	else
-		gtk_list_store_append (GTK_LIST_STORE (store), &iter);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
 
-	BRASERO_GET_BASENAME_FOR_DISPLAY (uri, name);
-	markup = g_markup_escape_text (name, -1);
-	g_free (name);
+	if (pos > 0) {
+		GtkTreePath *treepath;
 
-    	gtk_list_store_set (GTK_LIST_STORE (store), &iter,
-			    NAME_COL, markup,
-			    URI_COL, uri,
-			    -1);
-	g_free (markup);
-
-	/* set size message */
-	start = start > 0 ? start:0;
-	if (end > 0 && end > start) {
-		gchar *string;
-		gint64 length;
-
-		/* update global size */
-		length = BRASERO_AUDIO_TRACK_LENGTH (start, end);
-		priv->sectors += BRASERO_DURATION_TO_SECTORS (length);
-		brasero_disc_size_changed (BRASERO_DISC (self), priv->sectors);
-
-		string = brasero_utils_get_time_string (length, TRUE, FALSE);
-		gtk_list_store_set (GTK_LIST_STORE (store), &iter,
-				    START_COL, start,
-				    END_COL, end,
-				    SIZE_COL, string,
-				    -1);
-		g_free (string);
+		treepath = gtk_tree_path_new ();
+		gtk_tree_path_append_index (treepath, pos);
+		sibling = brasero_video_tree_model_path_to_file (BRASERO_VIDEO_TREE_MODEL (project), treepath);
+		gtk_tree_path_free (treepath);
 	}
-	else
-		gtk_list_store_set (GTK_LIST_STORE (store), &iter,
-				    SIZE_COL, _("loading"),
-				    -1);
-
-	/* Now load */
-	treepath = gtk_tree_model_get_path (store, &iter);
-	ref = gtk_tree_row_reference_new (store, treepath);
 
-	if (path_return)
-		*path_return = treepath;
-	else
-		gtk_tree_path_free (treepath);
+	file = brasero_video_project_add_uri (project,
+					      uri,
+					      sibling,
+					      start,
+					      end);
+	if (path_return && file)
+		*path_return = brasero_video_tree_model_file_to_path (BRASERO_VIDEO_TREE_MODEL (project), file);
 
-	/* get info async for the file */
-	if (!priv->io)
-		priv->io = brasero_io_get_default ();
-
-	if (!priv->add_uri)
-		priv->add_uri = brasero_io_register (G_OBJECT (self),
-						     brasero_video_disc_new_row_cb,
-						     brasero_video_disc_io_operation_finished,
-						     NULL);
-
-	brasero_video_disc_increase_activity_counter (self);
-	brasero_io_get_file_info (priv->io,
-				  uri,
-				  priv->add_uri,
-				  BRASERO_IO_INFO_PERM|
-				  BRASERO_IO_INFO_MIME|
-				  BRASERO_IO_INFO_METADATA|
-				  BRASERO_IO_INFO_METADATA_MISSING_CODEC|
-				  BRASERO_IO_INFO_METADATA_SNAPSHOT,
-				  ref);
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 1);
 
 	return BRASERO_DISC_OK;
 }
@@ -419,26 +358,20 @@
 	selected = gtk_tree_selection_get_selected_rows (selection, &model);
 	selected = g_list_reverse (selected);
 	for (iter = selected; iter; iter = iter->next) {
+		BraseroVideoFile *file;
 		GtkTreePath *treepath;
-		GtkTreeIter tree_iter;
-		gint64 start;
-		gint64 end;
 
 		treepath = iter->data;
 
-		gtk_tree_model_get_iter (model, &tree_iter, treepath);
-		gtk_tree_model_get (model, &tree_iter,
-				    START_COL, &start,
-				    END_COL, &end,
-				    -1);
-		priv->sectors -= BRASERO_DURATION_TO_SECTORS (end - start);
-
-		gtk_list_store_remove (GTK_LIST_STORE (model), &tree_iter);
+		file = brasero_video_tree_model_path_to_file (BRASERO_VIDEO_TREE_MODEL (model), treepath);
 		gtk_tree_path_free (treepath);
+
+		if (!file)
+			continue;
+
+		brasero_video_project_remove_file (BRASERO_VIDEO_PROJECT (model), file);
 	}
 	g_list_free (selected);
-
-	brasero_disc_size_changed (BRASERO_DISC (self), priv->sectors);
 }
 
 static gboolean
@@ -458,14 +391,15 @@
 		return FALSE;
 
 	if (uri) {
-		GtkTreeIter iter;
+		BraseroVideoFile *file;
 		GtkTreePath *treepath;
 
 		treepath = selected->data;
-		gtk_tree_model_get_iter (model, &iter, treepath);
-		gtk_tree_model_get (model, &iter,
-				    URI_COL, uri,
-				    -1);
+		file = brasero_video_tree_model_path_to_file (BRASERO_VIDEO_TREE_MODEL (model), treepath);
+		if (file)
+			*uri = g_strdup (file->uri);
+		else
+			*uri = NULL;
 	}
 
 	g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
@@ -481,6 +415,22 @@
 	brasero_disc_selection_changed (BRASERO_DISC (self));
 }
 
+static gboolean
+brasero_video_disc_selection_function (GtkTreeSelection *selection,
+				       GtkTreeModel *model,
+				       GtkTreePath *treepath,
+				       gboolean path_currently_selected,
+				       gpointer NULL_data)
+{
+	BraseroVideoFile *file;
+
+	file = brasero_video_tree_model_path_to_file (BRASERO_VIDEO_TREE_MODEL (model), treepath);
+	if (file)
+		file->editable = !path_currently_selected;
+
+	return TRUE;
+}
+
 static guint
 brasero_video_disc_add_ui (BraseroDisc *disc,
 			   GtkUIManager *manager,
@@ -544,8 +494,13 @@
 				   GtkTreePath *path,
 				   BraseroVideoDisc *self)
 {
+	BraseroVideoProject *project;
+	BraseroVideoDiscPrivate *priv;
+
+	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
 	brasero_disc_contents_changed (BRASERO_DISC (self),
-				       gtk_tree_model_iter_n_children (model, NULL));
+				       brasero_video_project_get_file_num (BRASERO_VIDEO_PROJECT (model)));
 }
 
 static void
@@ -554,8 +509,13 @@
 				    GtkTreeIter *iter,
 				    BraseroVideoDisc *self)
 {
+	BraseroVideoProject *project;
+	BraseroVideoDiscPrivate *priv;
+
+	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
 	brasero_disc_contents_changed (BRASERO_DISC (self),
-				       gtk_tree_model_iter_n_children (model, NULL));
+				       brasero_video_project_get_file_num (BRASERO_VIDEO_PROJECT (model)));
 }
 
 static void
@@ -564,8 +524,20 @@
 				   GtkTreeIter *iter,
 				   BraseroVideoDisc *self)
 {
+	BraseroVideoProject *project;
+	BraseroVideoDiscPrivate *priv;
+
+	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
 	brasero_disc_contents_changed (BRASERO_DISC (self),
-				       gtk_tree_model_iter_n_children (model, NULL));
+				       brasero_video_project_get_file_num (BRASERO_VIDEO_PROJECT (model)));
+}
+
+static void
+brasero_video_disc_size_changed_cb (BraseroVideoProject *project,
+				    BraseroVideoDisc *self)
+{
+	brasero_disc_size_changed (BRASERO_DISC (self), brasero_video_project_get_size (project));
 }
 
 static void
@@ -592,14 +564,8 @@
 	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
 
 	/* Tree */
-	model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COL,
-						    G_TYPE_STRING,
-						    G_TYPE_STRING,
-						    GDK_TYPE_PIXBUF,
-						    G_TYPE_STRING,
-						    G_TYPE_INT64,
-						    G_TYPE_INT64,
-						    G_TYPE_BOOLEAN));
+	model = GTK_TREE_MODEL (brasero_video_tree_model_new ());
+
 	g_signal_connect (G_OBJECT (model),
 			  "row-deleted",
 			  G_CALLBACK (brasero_video_disc_row_deleted_cb),
@@ -613,7 +579,29 @@
 			  G_CALLBACK (brasero_video_disc_row_changed_cb),
 			  object);
 
+	g_signal_connect (G_OBJECT (model),
+			  "size-changed",
+			  G_CALLBACK (brasero_video_disc_size_changed_cb),
+			  object);
+	g_signal_connect (G_OBJECT (model),
+			  "not-video-uri",
+			  G_CALLBACK (brasero_video_disc_not_video_dialog),
+			  object);
+	g_signal_connect (G_OBJECT (model),
+			  "directory-uri",
+			  G_CALLBACK (brasero_video_disc_directory_dialog),
+			  object);
+	g_signal_connect (G_OBJECT (model),
+			  "unreadable-uri",
+			  G_CALLBACK (brasero_video_disc_unreadable_uri_dialog),
+			  object);
+	g_signal_connect (G_OBJECT (model),
+			  "vfs-activity",
+			  G_CALLBACK (brasero_video_disc_vfs_activity_changed),
+			  object);
+
 	priv->tree = gtk_tree_view_new_with_model (model);
+	egg_tree_multi_drag_add_drag_support (GTK_TREE_VIEW (priv->tree));
 	g_object_unref (G_OBJECT (model));
 	gtk_widget_show (priv->tree);
 
@@ -628,9 +616,16 @@
 	renderer = gtk_cell_renderer_pixbuf_new ();
 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
 	gtk_tree_view_column_add_attribute (column, renderer,
-					    "pixbuf", ICON_COL);
+					    "pixbuf", BRASERO_VIDEO_TREE_MODEL_MIME_ICON);
 
 	renderer = gtk_cell_renderer_text_new ();
+	g_signal_connect (G_OBJECT (renderer), "edited",
+			  G_CALLBACK (brasero_video_disc_name_edited_cb), object);
+	g_signal_connect (G_OBJECT (renderer), "editing-started",
+			  G_CALLBACK (brasero_video_disc_name_editing_started_cb), object);
+	g_signal_connect (G_OBJECT (renderer), "editing-canceled",
+			  G_CALLBACK (brasero_video_disc_name_editing_canceled_cb), object);
+
 	g_object_set (G_OBJECT (renderer),
 		      "mode", GTK_CELL_RENDERER_MODE_EDITABLE,
 		      "ellipsize-set", TRUE,
@@ -639,9 +634,9 @@
 
 	gtk_tree_view_column_pack_end (column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute (column, renderer,
-					    "markup", NAME_COL);
+					    "markup", BRASERO_VIDEO_TREE_MODEL_NAME);
 	gtk_tree_view_column_add_attribute (column, renderer,
-					    "editable", EDITABLE_COL);
+					    "editable", BRASERO_VIDEO_TREE_MODEL_EDITABLE);
 	gtk_tree_view_column_set_title (column, _("Title"));
 	g_object_set (G_OBJECT (column),
 		      "expand", TRUE,
@@ -659,13 +654,12 @@
 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
 
 	gtk_tree_view_column_add_attribute (column, renderer,
-					    "text", SIZE_COL);
+					    "text", BRASERO_VIDEO_TREE_MODEL_SIZE);
 	gtk_tree_view_column_set_title (column, _("Size"));
 
 	gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree), column);
 	gtk_tree_view_column_set_resizable (column, TRUE);
 	gtk_tree_view_column_set_expand (column, FALSE);
-	gtk_tree_view_column_set_sort_column_id (column, NAME_COL);
 
 	/* selection */
 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree));
@@ -674,6 +668,10 @@
 			  "changed",
 			  G_CALLBACK (brasero_video_disc_selection_changed_cb),
 			  object);
+	gtk_tree_selection_set_select_function (selection,
+						brasero_video_disc_selection_function,
+						NULL,
+						NULL);
 
 	/* scroll */
 	scroll = gtk_scrolled_window_new (NULL, NULL);
@@ -685,37 +683,42 @@
 					GTK_POLICY_AUTOMATIC);
 	gtk_container_add (GTK_CONTAINER (scroll), priv->tree);
 	gtk_box_pack_start (GTK_BOX (mainbox), scroll, TRUE, TRUE, 0);
+
+	/* dnd */
+	gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW
+					      (priv->tree),
+					      ntables_cd, nb_targets_cd,
+					      GDK_ACTION_COPY |
+					      GDK_ACTION_MOVE);
+
+	gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (priv->tree),
+						GDK_BUTTON1_MASK,
+						ntables_source,
+						nb_targets_source,
+						GDK_ACTION_MOVE);
 }
 
 static void
 brasero_video_disc_reset_real (BraseroVideoDisc *self)
 {
+	BraseroVideoProject *project;
 	BraseroVideoDiscPrivate *priv;
 
 	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
-
-	if (priv->io)
-		brasero_io_cancel_by_base (priv->io, priv->add_uri);
-
-	priv->sectors = 0;
-
-	priv->activity = 1;
-	brasero_video_disc_decrease_activity_counter (self);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
+	brasero_video_project_reset (project);
+	brasero_video_disc_vfs_activity_changed (project, FALSE, self);
 }
 
 static void
 brasero_video_disc_clear (BraseroDisc *disc)
 {
 	BraseroVideoDiscPrivate *priv;
-	GtkTreeModel *model;
 
 	priv = BRASERO_VIDEO_DISC_PRIVATE (disc);
 
 	brasero_video_disc_reset_real (BRASERO_VIDEO_DISC (disc));
 
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree));
-	gtk_list_store_clear (GTK_LIST_STORE (model));
-
 	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
 	brasero_disc_size_changed (disc, 0);
 }
@@ -723,27 +726,12 @@
 static void
 brasero_video_disc_reset (BraseroDisc *disc)
 {
-	brasero_video_disc_clear (disc);
+	brasero_video_disc_reset_real (BRASERO_VIDEO_DISC (disc));
 }
 
 static void
 brasero_video_disc_finalize (GObject *object)
 {
-	BraseroVideoDiscPrivate *priv;
-
-	priv = BRASERO_VIDEO_DISC_PRIVATE (object);
-	
-	brasero_video_disc_reset_real (BRASERO_VIDEO_DISC (object));
-	
-	if (priv->io) {
-		brasero_io_cancel_by_base (priv->io, priv->add_uri);
-		g_free (priv->add_uri);
-		priv->add_uri = NULL;
-
-		g_object_unref (priv->io);
-		priv->io = NULL;
-	}
-
 	G_OBJECT_CLASS (brasero_video_disc_parent_class)->finalize (object);
 }
 
@@ -788,28 +776,18 @@
 }
 
 static BraseroDiscResult
-brasero_video_disc_get_status (BraseroDisc *disc)
+brasero_video_disc_get_status (BraseroDisc *self)
 {
-	GtkTreeModel *model;
+	BraseroVideoProject *project;
 	BraseroVideoDiscPrivate *priv;
 
-	priv = BRASERO_VIDEO_DISC_PRIVATE (disc);
-
-	if (priv->loading)
-		return BRASERO_DISC_LOADING;
-
-	if (priv->activity)
-		return BRASERO_DISC_NOT_READY;
-
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree));
-	if (!gtk_tree_model_iter_n_children (model, NULL))
-		return BRASERO_DISC_ERROR_EMPTY_SELECTION;
-
-	return BRASERO_DISC_OK;
+	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
+	return brasero_video_project_get_status (project);
 }
 
-static BraseroDiscResult
-brasero_video_disc_set_session_param (BraseroDisc *disc,
+BraseroDiscResult
+brasero_video_disc_set_session_param (BraseroDisc *self,
 				      BraseroBurnSession *session)
 {
 	BraseroTrackType type;
@@ -820,53 +798,100 @@
 	return BRASERO_BURN_OK;
 }
 
-static BraseroDiscResult
-brasero_video_disc_set_session_contents (BraseroDisc *disc,
+BraseroDiscResult
+brasero_video_disc_set_session_contents (BraseroDisc *self,
 					 BraseroBurnSession *session)
 {
-	GtkTreeIter iter;
-	GtkTreeModel *model;
-	BraseroTrack *track;
+	GSList *tracks, *iter;
+	BraseroVideoProject *project;
 	BraseroVideoDiscPrivate *priv;
 
-	priv = BRASERO_VIDEO_DISC_PRIVATE (disc);
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree));
-	if (!gtk_tree_model_get_iter_first (model, &iter))
+	priv = BRASERO_VIDEO_DISC_PRIVATE (self);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
+	tracks = brasero_video_project_get_contents (project);
+
+	if (!tracks)
 		return BRASERO_DISC_ERROR_EMPTY_SELECTION;
 
-	track = NULL;
-	do {
-		gchar *uri;
-		gint64 end;
-		gint64 start;
-		gchar *title;
-		BraseroSongInfo *info;
-
-		gtk_tree_model_get (model, &iter,
-				    URI_COL, &uri,
-				    NAME_COL, &title,
-				    START_COL, &start,
-				    END_COL, &end,
-				    -1);
-
-		info = g_new0 (BraseroSongInfo, 1);
-		info->title = title;
-
-		track = brasero_track_new (BRASERO_TRACK_TYPE_AUDIO);
-		brasero_track_set_audio_source (track,
-						uri,
-						BRASERO_AUDIO_FORMAT_UNDEFINED|
-						BRASERO_VIDEO_FORMAT_UNDEFINED);
+	for (iter = tracks; iter; iter = iter->next) {
+		BraseroTrack *track;
 
-		brasero_track_set_audio_boundaries (track, start, end, -1);
-		brasero_track_set_audio_info (track, info);
+		track = iter->data;
 		brasero_burn_session_add_track (session, track);
 
 		/* It's good practice to unref the track afterwards as we don't
 		 * need it anymore. BraseroBurnSession refs it. */
 		brasero_track_unref (track);
 
-	} while (gtk_tree_model_iter_next (model, &iter));
+	}
+	g_slist_free (tracks);
+	return BRASERO_DISC_OK;
+}
+
+static BraseroDiscResult
+brasero_video_disc_get_track (BraseroDisc *disc,
+			      BraseroDiscTrack *disc_track)
+{
+	GSList *iter;
+	GSList *tracks;
+	BraseroVideoProject *project;
+	BraseroVideoDiscPrivate *priv;
+
+	disc_track->type = BRASERO_DISC_TRACK_VIDEO;
+
+	priv = BRASERO_VIDEO_DISC_PRIVATE (disc);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
+	tracks = brasero_video_project_get_contents (project);
+
+	for (iter = tracks; iter; iter = iter->next) {
+		BraseroDiscSong *song;
+		BraseroTrack *track;
+
+		track = iter->data;
+
+		song = g_new0 (BraseroDiscSong, 1);
+		song->uri = brasero_track_get_audio_source (track, TRUE);;
+		song->start = brasero_track_get_audio_start (track);
+		song->end = brasero_track_get_audio_end (track);
+		song->info = brasero_song_info_copy (brasero_track_get_audio_info (track));
+		disc_track->contents.tracks = g_slist_append (disc_track->contents.tracks, song);
+	}
+
+	g_slist_foreach (tracks, (GFunc) brasero_track_unref, NULL);
+	g_slist_free (tracks);
+
+	return BRASERO_DISC_OK;
+}
+
+static BraseroDiscResult
+brasero_video_disc_load_track (BraseroDisc *disc,
+			       BraseroDiscTrack *track)
+{
+	GSList *iter;
+	BraseroVideoProject *project;
+	BraseroVideoDiscPrivate *priv;
+
+	g_return_val_if_fail (track->type == BRASERO_DISC_TRACK_VIDEO, FALSE);
+
+	if (track->contents.tracks == NULL)
+		return BRASERO_DISC_ERROR_EMPTY_SELECTION;
+
+	priv = BRASERO_VIDEO_DISC_PRIVATE (disc);
+	project = BRASERO_VIDEO_PROJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree)));
+	priv->loading = g_slist_length (track->contents.tracks);
+
+	for (iter = track->contents.tracks; iter; iter = iter->next) {
+		BraseroDiscSong *song;
+
+		song = iter->data;
+
+		brasero_video_project_add_uri (BRASERO_VIDEO_PROJECT (project),
+					       song->uri,
+					       NULL,
+					       song->start,
+					       song->end);
+	}
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 1);
 
 	return BRASERO_DISC_OK;
 }
@@ -883,10 +908,9 @@
 	iface->set_session_param = brasero_video_disc_set_session_param;
 	iface->set_session_contents = brasero_video_disc_set_session_contents;
 
-/*
-	iface->get_track = brasero_data_disc_get_track;
-	iface->load_track = brasero_data_disc_load_track;
-*/
+	iface->get_track = brasero_video_disc_get_track;
+	iface->load_track = brasero_video_disc_load_track;
+
 	iface->get_selected_uri = brasero_video_disc_get_selected_uri;
 	iface->add_ui = brasero_video_disc_add_ui;
 }

Added: branches/video/src/brasero-video-project.c
==============================================================================
--- (empty file)
+++ branches/video/src/brasero-video-project.c	Fri Jun 27 13:29:09 2008
@@ -0,0 +1,1129 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * brasero
+ * Copyright (C) Philippe Rouquier 2008 <bonfire-app wanadoo fr>
+ * 
+ * brasero is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * brasero is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "brasero-video-project.h"
+#include "brasero-file-monitor.h"
+#include "brasero-io.h"
+#include "brasero-marshal.h"
+
+typedef struct _BraseroVideoProjectPrivate BraseroVideoProjectPrivate;
+struct _BraseroVideoProjectPrivate
+{
+	guint ref_count;
+	GHashTable *references;
+
+	BraseroIO *io;
+	BraseroIOJobBase *load_uri;
+	BraseroIOJobBase *load_dir;
+
+	BraseroVideoFile *first;
+
+	guint loading;
+};
+
+#define BRASERO_VIDEO_PROJECT_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_VIDEO_PROJECT, BraseroVideoProjectPrivate))
+
+#ifdef BUILD_INOTIFY
+
+#include "brasero-file-monitor.h"
+
+G_DEFINE_TYPE (BraseroVideoProject, brasero_video_project, BRASERO_TYPE_FILE_MONITOR);
+
+#else
+
+G_DEFINE_TYPE (BraseroVideoProject, brasero_video_project, G_TYPE_OBJECT);
+
+#endif
+
+enum {
+	PROJECT_LOADED_SIGNAL,
+	SIZE_CHANGED_SIGNAL,
+	DIRECTORY_URI_SIGNAL,
+	UNREADABLE_SIGNAL,
+	NOT_VIDEO_SIGNAL,
+	ACTIVITY_SIGNAL,
+	LAST_SIGNAL
+};
+
+static guint brasero_video_project_signals [LAST_SIGNAL] = {0};
+
+/**
+ * Used to send signals with a default answer
+ */
+
+static gboolean
+brasero_video_project_file_signal (BraseroVideoProject *self,
+				  guint signal,
+				  const gchar *name)
+{
+	GValue instance_and_params [2];
+	GValue return_value;
+	GValue *params;
+
+	/* object which signalled */
+	instance_and_params->g_type = 0;
+	g_value_init (instance_and_params, G_TYPE_FROM_INSTANCE (self));
+	g_value_set_instance (instance_and_params, self);
+
+	/* arguments of signal (name) */
+	params = instance_and_params + 1;
+	params->g_type = 0;
+	g_value_init (params, G_TYPE_STRING);
+	g_value_set_string (params, name);
+
+	/* default to FALSE */
+	return_value.g_type = 0;
+	g_value_init (&return_value, G_TYPE_BOOLEAN);
+	g_value_set_boolean (&return_value, FALSE);
+
+	g_signal_emitv (instance_and_params,
+			brasero_video_project_signals [signal],
+			0,
+			&return_value);
+
+	g_value_unset (instance_and_params);
+	g_value_unset (params);
+
+	return g_value_get_boolean (&return_value);
+}
+
+/**
+ * Manages the references to a node
+ */
+
+guint
+brasero_video_project_reference_new (BraseroVideoProject *self,
+				    BraseroVideoFile *node)
+{
+	BraseroVideoProjectPrivate *priv;
+	guint retval;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	if (!priv->references)
+		priv->references = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+	retval = priv->ref_count;
+	while (g_hash_table_lookup (priv->references, GINT_TO_POINTER (retval))) {
+		retval ++;
+
+		if (retval == G_MAXINT)
+			retval = 1;
+
+		/* this means there is no more room for reference */
+		if (retval == priv->ref_count)
+			return 0;
+	}
+
+	g_hash_table_insert (priv->references,
+			     GINT_TO_POINTER (retval),
+			     node);
+	priv->ref_count = retval + 1;
+	if (priv->ref_count == G_MAXINT)
+		priv->ref_count = 1;
+
+	return retval;
+}
+
+void
+brasero_video_project_reference_free (BraseroVideoProject *self,
+				     guint reference)
+{
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+	g_hash_table_remove (priv->references, GINT_TO_POINTER (reference));
+}
+
+BraseroVideoFile *
+brasero_video_project_reference_get (BraseroVideoProject *self,
+				    guint reference)
+{
+	BraseroVideoProjectPrivate *priv;
+
+	/* if it was invalidated then the node returned is NULL */
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+	return g_hash_table_lookup (priv->references, GINT_TO_POINTER (reference));
+}
+
+static gboolean
+brasero_video_project_reference_remove_children_cb (gpointer key,
+						   gpointer data,
+						   gpointer callback_data)
+{
+	BraseroVideoFile *node = data;
+	BraseroVideoFile *removable = callback_data;
+
+	if (node == removable)
+		return TRUE;
+
+	return FALSE;
+}
+
+static void
+brasero_video_project_reference_invalidate (BraseroVideoProject *self,
+					   BraseroVideoFile *node)
+{
+	BraseroVideoProjectPrivate *priv;
+
+	/* used internally to invalidate reference whose node was removed */
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+	g_hash_table_foreach_remove (priv->references,
+				     (GHRFunc) brasero_video_project_reference_remove_children_cb,
+				     node);
+}
+
+/**
+ * Move functions
+ */
+
+void
+brasero_video_project_rename (BraseroVideoProject *self,
+			      BraseroVideoFile *file,
+			      const gchar *name)
+{
+	gchar *tmp;
+	BraseroVideoProjectClass *klass;
+
+	tmp = file->name;
+	file->name = g_strdup (name);
+	g_free (tmp);
+
+	klass = BRASERO_VIDEO_PROJECT_GET_CLASS (self);
+	if (klass->node_changed)
+		klass->node_changed (self, file);
+}
+
+void
+brasero_video_project_move (BraseroVideoProject *self,
+			    BraseroVideoFile *file,
+			    BraseroVideoFile *next_file)
+{
+	BraseroVideoFile *prev, *next;
+	BraseroVideoProjectClass *klass;
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+	if (!file)
+		return;
+
+	if (file == next_file)
+		return;
+
+	/* unlink it */
+	prev = file->prev;
+	next = file->next;
+
+	if (next)
+		next->prev = prev;
+
+	if (prev)
+		prev->next = next;
+	else
+		priv->first = next;
+
+	/* tell the model */
+	klass = BRASERO_VIDEO_PROJECT_GET_CLASS (self);
+	if (klass->node_removed)
+		klass->node_removed (self, file);
+
+	/* relink it */
+	if (next_file) {
+		file->next = next_file;
+		file->prev = next_file->prev;
+		next_file->prev = file;
+
+		if (file->prev)
+			file->prev->next = file;
+		else
+			priv->first = file;
+	}
+	else if (priv->first) {
+		BraseroVideoFile *last;
+
+		/* Put it at the end */
+		last = priv->first;
+		while (last->next) last = last->next;
+
+		file->next = NULL;
+		file->prev = last;
+		last->next = file;
+	}
+	else {
+		priv->first = file;
+		file->next = NULL;
+		file->prev = NULL;
+	}
+
+	/* tell the model */
+	if (klass->node_added)
+		klass->node_added (self, file);
+}
+
+/**
+ * Remove functions
+ */
+
+void
+brasero_video_file_free (BraseroVideoFile *file)
+{
+	if (file->uri)
+		g_free (file->uri);
+
+	if (file->snapshot)
+		g_object_unref (file->snapshot);
+
+	if (file->info)
+		brasero_song_info_free (file->info);
+
+	g_free (file);
+}
+
+static gboolean
+brasero_video_project_foreach_monitor_cancel_cb (gpointer data,
+						 gpointer user_data)
+{
+	BraseroVideoFile *node = data;
+	BraseroVideoFile *file = user_data;
+
+	if (node == file)
+		return TRUE;
+
+	return FALSE;
+}
+
+void
+brasero_video_project_remove_file (BraseroVideoProject *self,
+				   BraseroVideoFile *file)
+{
+	BraseroVideoFile *prev, *next;
+	BraseroVideoProjectClass *klass;
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	if (!file)
+		return;
+
+	/* Unlink it */
+	prev = file->prev;
+	next = file->next;
+
+	if (next)
+		next->prev = prev;
+
+	if (prev)
+		prev->next = next;
+	else
+		priv->first = next;
+
+	klass = BRASERO_VIDEO_PROJECT_GET_CLASS (self);
+	if (klass->node_removed)
+		klass->node_removed (self, file);
+
+	brasero_video_project_reference_invalidate (self, file);
+
+#ifdef BUILD_INOTIFY
+
+	/* Stop monitoring */
+	if (file->is_monitored)
+		brasero_file_monitor_foreach_cancel (BRASERO_FILE_MONITOR (self),
+						     brasero_video_project_foreach_monitor_cancel_cb,
+						     file);
+
+#endif
+
+	/* Free data */
+	brasero_video_file_free (file);
+
+	g_signal_emit (self,
+		       brasero_video_project_signals [SIZE_CHANGED_SIGNAL],
+		       0);
+}
+
+void
+brasero_video_project_reset (BraseroVideoProject *self)
+{
+	BraseroVideoProjectPrivate *priv;
+	BraseroVideoProjectClass *klass;
+	BraseroVideoFile *iter, *next;
+	guint num_nodes = 0;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	/* cancel all VFS operations */
+	if (priv->io) {
+		brasero_io_cancel_by_base (priv->io, priv->load_uri);
+		brasero_io_cancel_by_base (priv->io, priv->load_dir);
+
+		g_free (priv->load_uri);
+		priv->load_uri = NULL;
+
+		g_free (priv->load_dir);
+		priv->load_dir = NULL;
+	}
+
+	/* destroy all references */
+	if (priv->references) {
+		g_hash_table_destroy (priv->references);
+		priv->references = g_hash_table_new (g_direct_hash, g_direct_equal);
+	}
+
+#ifdef BUILD_INOTIFY
+
+	brasero_file_monitor_reset (BRASERO_FILE_MONITOR (self));
+
+#endif
+
+	/* empty tree */
+	for (iter = priv->first; iter; iter = next) {
+		next = iter->next;
+		brasero_video_project_remove_file (self, iter);
+	}
+	priv->first = NULL;
+
+	priv->loading = 0;
+
+	klass = BRASERO_VIDEO_PROJECT_GET_CLASS (self);
+	if (klass->reset)
+		klass->reset (self, num_nodes);
+}
+
+/**
+ * Add functions
+ */
+
+static BraseroVideoFile *
+brasero_video_project_add_video_file (BraseroVideoProject *self,
+				      const gchar *uri,
+				      BraseroVideoFile *sibling,
+				      guint64 start,
+				      guint64 end)
+{
+	BraseroVideoProjectPrivate *priv;
+	BraseroVideoFile *file;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	/* create new file and insert it */
+	file = g_new0 (BraseroVideoFile, 1);
+	file->uri = g_strdup (uri);
+
+	if (start > -1)
+		file->start = start;
+
+	if (end > -1)
+		file->end = end;
+
+	if (sibling) {
+		file->next = sibling;
+		file->prev = sibling->prev;
+
+		if (sibling->prev)
+			sibling->prev->next = file;
+		else
+			priv->first = file;
+
+		sibling->prev = file;
+	}
+	else if (priv->first) {
+		BraseroVideoFile *last;
+
+		/* Put it at the end */
+		last = priv->first;
+		while (last->next) last = last->next;
+
+		file->prev = last;
+		file->next = NULL;
+		last->next = file;
+	}
+	else {
+		priv->first = file;
+		file->next = NULL;
+		file->prev = NULL;
+	}
+
+	return file;
+}
+
+static void
+brasero_video_project_set_file_information (BraseroVideoProject *self,
+					    BraseroVideoFile *file,
+					    GFileInfo *info)
+{
+	guint64 len;
+	GdkPixbuf *snapshot;
+	BraseroSongInfo *song;
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	/* For reloading files no need to go further, we just want to check that
+	 * they are still readable and still holds video. */
+	if (file->is_reloading) {
+		file->is_reloading = FALSE;
+		return;
+	}
+
+	file->is_loading = FALSE;
+
+	if (g_file_info_get_is_symlink (info)) {
+		gchar *sym_uri;
+
+		sym_uri = g_strconcat ("file://", g_file_info_get_symlink_target (info), NULL);
+		g_free (file->uri);
+
+		file->uri = sym_uri;
+	}
+
+	/* Set the snapshot */
+	snapshot = GDK_PIXBUF (g_file_info_get_attribute_object (info, BRASERO_IO_SNAPSHOT));
+	if (snapshot) {
+		GdkPixbuf *scaled;
+
+		scaled = gdk_pixbuf_scale_simple (snapshot,
+						  96 * gdk_pixbuf_get_width (snapshot) / gdk_pixbuf_get_height (snapshot),
+						  96,
+						  GDK_INTERP_BILINEAR);
+		file->snapshot = scaled;
+	}
+
+	/* size */
+	len = g_file_info_get_attribute_uint64 (info, BRASERO_IO_LEN);
+	if (file->end > len)
+		file->end = len;
+	else if (file->end <= 0)
+		file->end = len;
+
+	/* Get the song info */
+	song = g_new0 (BraseroSongInfo, 1);
+	song->title = g_strdup (g_file_info_get_attribute_string (info, BRASERO_IO_TITLE));
+	song->artist = g_strdup (g_file_info_get_attribute_string (info, BRASERO_IO_ARTIST));
+	song->composer = g_strdup (g_file_info_get_attribute_string (info, BRASERO_IO_COMPOSER));
+	song->isrc = g_file_info_get_attribute_int32 (info, BRASERO_IO_ISRC);
+	file->info = song;
+
+#ifdef BUILD_INOTIFY
+
+	/* Start monitoring */
+	file->is_monitored = TRUE;
+	brasero_file_monitor_single_file (BRASERO_FILE_MONITOR (self),
+					  file->uri,
+					  file);
+
+#endif
+}
+
+static void
+brasero_video_project_vfs_operation_finished (GObject *object,
+					      gboolean cancelled,
+					      gpointer null_data)
+{
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (object);
+
+	priv->loading --;
+	g_signal_emit (object,
+		       brasero_video_project_signals [ACTIVITY_SIGNAL],
+		       0,
+		       priv->loading > 0);
+}
+
+static void
+brasero_video_project_add_directory_contents_result (GObject *obj,
+						     GError *error,
+						     const gchar *uri,
+						     GFileInfo *info,
+						     gpointer user_data)
+{
+	BraseroVideoFile *file;
+	BraseroVideoFile *sibling;
+	BraseroVideoProjectClass *klass;
+	guint ref = GPOINTER_TO_INT (user_data);
+
+	/* Check the return status for this file */
+	if (error)
+		return;
+
+	if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR
+	|| !g_file_info_get_attribute_boolean (info, BRASERO_IO_HAS_VIDEO))
+		return;
+
+	sibling = brasero_video_project_reference_get (BRASERO_VIDEO_PROJECT (obj), ref);
+
+	/* Add a video file and set all information */
+	file = brasero_video_project_add_video_file (BRASERO_VIDEO_PROJECT (obj),
+						     uri,
+						     sibling,
+						     -1,
+						     -1);
+						     
+	brasero_video_project_set_file_information (BRASERO_VIDEO_PROJECT (obj),
+						    file,
+						    info);
+
+	/* Tell model we added a node */
+	klass = BRASERO_VIDEO_PROJECT_GET_CLASS (obj);
+	if (klass->node_added)
+		klass->node_added (BRASERO_VIDEO_PROJECT (obj), file);
+
+	/* update size */
+	g_signal_emit (BRASERO_VIDEO_PROJECT (obj),
+		       brasero_video_project_signals [SIZE_CHANGED_SIGNAL],
+		       0,
+		       error,
+		       uri);
+}
+
+static void
+brasero_video_project_add_directory_contents (BraseroVideoProject *self,
+					      const gchar *uri,
+					      BraseroVideoFile *sibling)
+{
+	BraseroVideoProjectPrivate *priv;
+	guint ref;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	if (!priv->io)
+		priv->io = brasero_io_get_default ();
+
+	if (!priv->load_dir)
+		priv->load_dir = brasero_io_register (G_OBJECT (self),
+						      brasero_video_project_add_directory_contents_result,
+						      brasero_video_project_vfs_operation_finished,
+						      NULL);
+
+	priv->loading ++;
+	g_signal_emit (self,
+		       brasero_video_project_signals [ACTIVITY_SIGNAL],
+		       0,
+		       priv->loading != 0);
+
+	ref = brasero_video_project_reference_new (self, sibling);
+
+	brasero_io_load_directory (priv->io,
+				   uri,
+				   priv->load_dir,
+				   BRASERO_IO_INFO_MIME|
+				   BRASERO_IO_INFO_PERM|
+				   BRASERO_IO_INFO_METADATA|
+				   BRASERO_IO_INFO_METADATA_MISSING_CODEC|
+				   BRASERO_IO_INFO_RECURSIVE|
+				   BRASERO_IO_INFO_METADATA_SNAPSHOT,
+				   GINT_TO_POINTER (ref));
+}
+
+static void
+brasero_video_project_result_cb (GObject *obj,
+				 GError *error,
+				 const gchar *uri,
+				 GFileInfo *info,
+				 gpointer user_data)
+{
+	BraseroVideoFile *file;
+	BraseroVideoProject *self;
+	BraseroVideoProjectClass *klass;
+	BraseroVideoProjectPrivate *priv;
+	guint ref = GPOINTER_TO_INT (user_data);
+
+	self = BRASERO_VIDEO_PROJECT (obj);
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (obj);
+
+	/* Get the reference for the node */
+	file = brasero_video_project_reference_get (self, ref);
+	if (!file)
+		return;
+
+	/* Check the return status for this file */
+	if (error) {
+		g_signal_emit (self,
+			       brasero_video_project_signals [UNREADABLE_SIGNAL],
+			       0,
+			       error,
+			       uri);
+
+		brasero_video_project_remove_file (self, file);
+		return;
+	}
+
+	if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+		gboolean result;
+
+		/* Ask the user */
+		result = brasero_video_project_file_signal (self,
+							   DIRECTORY_URI_SIGNAL,
+							   uri);
+
+		/* NOTE: we need to pass a sibling here even if that the file
+		 * that's going to be deleted just after. */
+		if (result)
+			brasero_video_project_add_directory_contents (self,
+								      uri,
+								      file->next?file->next:file);
+
+		/* remove the file */
+		brasero_video_project_remove_file (self, file);
+		return;
+	}
+
+	if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR
+	|| !g_file_info_get_attribute_boolean (info, BRASERO_IO_HAS_VIDEO)) {
+		g_signal_emit (self,
+			       brasero_video_project_signals [NOT_VIDEO_SIGNAL],
+			       0,
+			       error,
+			       uri);
+
+		brasero_video_project_remove_file (self, file);
+		return;
+	}
+
+	brasero_video_project_set_file_information (BRASERO_VIDEO_PROJECT (obj),
+						    file,
+						    info);
+
+	/* Tell upper object that the node status and information changed */
+	klass = BRASERO_VIDEO_PROJECT_GET_CLASS (self);
+	if (klass->node_changed)
+		klass->node_changed (self, file);
+
+	/* update size */
+	g_signal_emit (self,
+		       brasero_video_project_signals [SIZE_CHANGED_SIGNAL],
+		       0,
+		       error,
+		       uri);
+}
+
+BraseroVideoFile *
+brasero_video_project_add_uri (BraseroVideoProject *self,
+			       const gchar *uri,
+			       BraseroVideoFile *sibling,
+			       gint64 start,
+			       gint64 end)
+{
+	BraseroVideoProjectPrivate *priv;
+	BraseroVideoProjectClass *klass;
+	BraseroVideoFile *file;
+	guint ref;
+
+	g_return_val_if_fail (uri != NULL, NULL);
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	/* create new file and insert it */
+	file = g_new0 (BraseroVideoFile, 1);
+	file->uri = g_strdup (uri);
+
+	if (start > -1)
+		file->start = start;
+
+	if (end > -1)
+		file->end = end;
+
+	if (sibling) {
+		file->next = sibling;
+		file->prev = sibling->prev;
+
+		if (sibling->prev)
+			sibling->prev->next = file;
+		else
+			priv->first = file;
+
+		sibling->prev = file;
+	}
+	else if (priv->first) {
+		BraseroVideoFile *last;
+
+		/* Put it at the end */
+		last = priv->first;
+		while (last->next) last = last->next;
+
+		file->prev = last;
+		file->next = NULL;
+		last->next = file;
+	}
+	else {
+		priv->first = file;
+		file->next = NULL;
+		file->prev = NULL;
+	}
+
+	/* Tell model we added a node */
+	klass = BRASERO_VIDEO_PROJECT_GET_CLASS (self);
+	if (klass->node_added)
+		klass->node_added (self, file);
+
+	/* get info async for the file */
+	if (!priv->io)
+		priv->io = brasero_io_get_default ();
+
+	if (!priv->load_uri)
+		priv->load_uri = brasero_io_register (G_OBJECT (self),
+						      brasero_video_project_result_cb,
+						      brasero_video_project_vfs_operation_finished,
+						      NULL);
+
+	file->is_loading = 1;
+	priv->loading ++;
+
+	ref = brasero_video_project_reference_new (self, file);
+	brasero_io_get_file_info (priv->io,
+				  uri,
+				  priv->load_uri,
+				  BRASERO_IO_INFO_PERM|
+				  BRASERO_IO_INFO_MIME|
+				  BRASERO_IO_INFO_METADATA|
+				  BRASERO_IO_INFO_METADATA_MISSING_CODEC|
+				  BRASERO_IO_INFO_METADATA_SNAPSHOT,
+				  GINT_TO_POINTER (ref));
+
+	g_signal_emit (self,
+		       brasero_video_project_signals [ACTIVITY_SIGNAL],
+		       0,
+		       (priv->loading > 0));
+
+	return file;
+}
+
+guint64
+brasero_video_project_get_size (BraseroVideoProject *self)
+{
+	BraseroVideoProjectPrivate *priv;
+	BraseroVideoFile *iter;
+	guint size = 0;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	/* FIXME: duration to sectors is not correct here, that's not audio... */
+	for (iter = priv->first; iter; iter = iter->next)
+		size += BRASERO_DURATION_TO_SECTORS (iter->end - iter->start);
+
+	return size;
+}
+
+guint
+brasero_video_project_get_file_num (BraseroVideoProject *self)
+{
+	BraseroVideoProjectPrivate *priv;
+	BraseroVideoFile *item;
+	guint num = 0;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+	for (item = priv->first; item; item = item->next)
+		num ++;
+
+	return num;
+}
+
+BraseroVideoFile *
+brasero_video_project_get_nth_item (BraseroVideoProject *self,
+				    guint nth)
+{
+	BraseroVideoFile *item;
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+	if (!nth)
+		return priv->first;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+	for (item = priv->first; item; item = item->next) {
+		if (nth <= 0)
+			return item;
+
+		nth --;
+	}
+
+	return NULL;
+}
+
+guint
+brasero_video_project_get_item_index (BraseroVideoProject *self,
+				      BraseroVideoFile *file)
+{
+	guint nth = 0;
+	BraseroVideoFile *item;
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	for (item = priv->first; item; item = item->next) {
+		if (item == file)
+			return nth;
+
+		nth ++;
+	}
+
+	return nth;
+}
+
+BraseroDiscResult
+brasero_video_project_get_status (BraseroVideoProject *self)
+{
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+
+	if (priv->loading)
+		return BRASERO_DISC_LOADING;
+
+	if (!priv->first)
+		return BRASERO_DISC_ERROR_EMPTY_SELECTION;
+
+	return BRASERO_DISC_OK;
+}
+
+GSList *
+brasero_video_project_get_contents (BraseroVideoProject *self)
+{
+	GSList *tracks = NULL;
+	BraseroVideoFile *file;
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (self);
+	if (!priv->first)
+		return NULL;
+
+	for (file = priv->first; file; file = file->next) {
+		BraseroSongInfo *info = NULL;
+		BraseroTrack *track;
+
+		if (file->info)
+			info = brasero_song_info_copy (file->info);
+		else
+			info = NULL;
+
+		track = brasero_track_new (BRASERO_TRACK_TYPE_AUDIO);
+		brasero_track_set_audio_source (track,
+						file->uri,
+						BRASERO_AUDIO_FORMAT_UNDEFINED|
+						BRASERO_VIDEO_FORMAT_UNDEFINED);
+
+		brasero_track_set_audio_boundaries (track, file->start, file->end, -1);
+		brasero_track_set_audio_info (track, info);
+		tracks = g_slist_prepend (tracks, track);
+	}
+
+	return tracks;
+}
+
+static void
+brasero_video_project_init (BraseroVideoProject *object)
+{
+	BraseroVideoProjectPrivate *priv;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (object);
+	priv->ref_count = 1;
+}
+
+static void
+brasero_video_project_finalize (GObject *object)
+{
+	BraseroVideoProjectPrivate *priv;
+	BraseroVideoFile *iter, *next;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (object);
+
+	for (iter = priv->first; iter; iter = next) {
+		next = iter->next;
+		g_free (iter->uri);
+		brasero_song_info_free (iter->info);
+		g_free (iter);
+	}
+
+	if (priv->references) {
+		g_hash_table_destroy (priv->references);
+		priv->references = NULL;
+	}
+
+	G_OBJECT_CLASS (brasero_video_project_parent_class)->finalize (object);
+}
+/**
+ * Callbacks for inotify backend
+ */
+
+#ifdef BUILD_INOTIFY
+
+static void
+brasero_video_project_file_renamed (BraseroFileMonitor *monitor,
+				    BraseroFileMonitorType type,
+				    gpointer callback_data,
+				    const gchar *old_name,
+				    const gchar *new_name)
+{
+	brasero_video_project_rename (BRASERO_VIDEO_PROJECT (monitor),
+				      callback_data,
+				      new_name);
+}
+
+static void
+brasero_video_project_file_moved (BraseroFileMonitor *monitor,
+				  BraseroFileMonitorType type,
+				  gpointer callback_src,
+				  const gchar *name_src,
+				  gpointer callback_dest,
+				  const gchar *name_dest)
+{
+	/* This is a file removed since we won't monitor all folders to get its
+	 * new path */
+	brasero_video_project_remove_file (BRASERO_VIDEO_PROJECT (monitor),
+					   callback_src);
+}
+
+static void
+brasero_video_project_file_removed (BraseroFileMonitor *monitor,
+				    BraseroFileMonitorType type,
+				    gpointer callback_data,
+				    const gchar *name)
+{
+	brasero_video_project_remove_file (BRASERO_VIDEO_PROJECT (monitor),
+					   callback_data);
+}
+
+static void
+brasero_video_project_file_modified (BraseroFileMonitor *monitor,
+				     gpointer callback_data,
+				     const gchar *name)
+{
+	BraseroVideoProjectPrivate *priv;
+	BraseroVideoFile *file;
+	guint ref;
+
+	priv = BRASERO_VIDEO_PROJECT_PRIVATE (monitor);
+
+	/* priv->load_uri has already been initialized otherwise the tree would
+	 * be empty. But who knows... */
+	if (!priv->io || !priv->load_uri)
+		return;
+
+	file = callback_data;
+	file->is_reloading = TRUE;
+
+	ref = brasero_video_project_reference_new (BRASERO_VIDEO_PROJECT (monitor), file);
+	brasero_io_get_file_info (priv->io,
+				  file->uri,
+				  priv->load_uri,
+				  BRASERO_IO_INFO_PERM|
+				  BRASERO_IO_INFO_MIME|
+				  BRASERO_IO_INFO_METADATA|
+				  BRASERO_IO_INFO_METADATA_MISSING_CODEC|
+				  BRASERO_IO_INFO_METADATA_SNAPSHOT,
+				  GINT_TO_POINTER (ref));
+}
+
+#endif
+
+static void
+brasero_video_project_class_init (BraseroVideoProjectClass *klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (BraseroVideoProjectPrivate));
+
+	object_class->finalize = brasero_video_project_finalize;
+
+	brasero_video_project_signals [SIZE_CHANGED_SIGNAL] = 
+	    g_signal_new ("size_changed",
+			  G_TYPE_FROM_CLASS (klass),
+			  G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
+			  0,
+			  NULL, NULL,
+			  g_cclosure_marshal_VOID__VOID,
+			  G_TYPE_NONE,
+			  0,
+			  G_TYPE_NONE);
+
+	brasero_video_project_signals [PROJECT_LOADED_SIGNAL] = 
+	    g_signal_new ("project-loaded",
+			  G_TYPE_FROM_CLASS (klass),
+			  G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
+			  0,
+			  NULL, NULL,
+			  g_cclosure_marshal_VOID__INT,
+			  G_TYPE_NONE,
+			  1,
+			  G_TYPE_INT);
+
+	brasero_video_project_signals [UNREADABLE_SIGNAL] = 
+	    g_signal_new ("unreadable_uri",
+			  G_TYPE_FROM_CLASS (klass),
+			  G_SIGNAL_RUN_FIRST,
+			  0,
+			  NULL, NULL,
+			  brasero_marshal_VOID__POINTER_STRING,
+			  G_TYPE_NONE,
+			  2,
+			  G_TYPE_POINTER,
+			  G_TYPE_STRING);
+
+	brasero_video_project_signals [NOT_VIDEO_SIGNAL] = 
+	    g_signal_new ("not_video_uri",
+			  G_TYPE_FROM_CLASS (klass),
+			  G_SIGNAL_RUN_FIRST,
+			  0,
+			  NULL, NULL,
+			  g_cclosure_marshal_VOID__STRING,
+			  G_TYPE_NONE,
+			  1,
+			  G_TYPE_STRING);
+
+	brasero_video_project_signals [DIRECTORY_URI_SIGNAL] = 
+	    g_signal_new ("directory_uri",
+			  G_TYPE_FROM_CLASS (klass),
+			  G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
+			  0,
+			  NULL, NULL,
+			  brasero_marshal_BOOLEAN__STRING,
+			  G_TYPE_BOOLEAN,
+			  1,
+			  G_TYPE_STRING);
+
+	brasero_video_project_signals [ACTIVITY_SIGNAL] = 
+	    g_signal_new ("vfs_activity",
+			  G_TYPE_FROM_CLASS (klass),
+			  G_SIGNAL_RUN_FIRST|G_SIGNAL_NO_RECURSE,
+			  0,
+			  NULL, NULL,
+			  g_cclosure_marshal_VOID__BOOLEAN,
+			  G_TYPE_NONE,
+			  1,
+			  G_TYPE_BOOLEAN);
+
+#ifdef BUILD_INOTIFY
+
+	BraseroFileMonitorClass *monitor_class = BRASERO_FILE_MONITOR_CLASS (klass);
+
+	/* NOTE: file_added is not needed here since there aren't any directory */
+	monitor_class->file_moved = brasero_video_project_file_moved;
+	monitor_class->file_removed = brasero_video_project_file_removed;
+	monitor_class->file_renamed = brasero_video_project_file_renamed;
+	monitor_class->file_modified = brasero_video_project_file_modified;
+
+#endif
+}

Added: branches/video/src/brasero-video-project.h
==============================================================================
--- (empty file)
+++ branches/video/src/brasero-video-project.h	Fri Jun 27 13:29:09 2008
@@ -0,0 +1,170 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * brasero
+ * Copyright (C) Philippe Rouquier 2008 <bonfire-app wanadoo fr>
+ * 
+ * brasero is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * brasero is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifndef _BRASERO_VIDEO_PROJECT_H_
+#define _BRASERO_VIDEO_PROJECT_H_
+
+#include <glib-object.h>
+#include <gdk/gdk.h>
+
+#ifdef BUILD_INOTIFY
+
+#include "brasero-file-monitor.h"
+
+#endif
+
+#include "burn-track.h"
+#include "brasero-disc.h"
+
+G_BEGIN_DECLS
+
+#define BRASERO_TYPE_VIDEO_PROJECT             (brasero_video_project_get_type ())
+#define BRASERO_VIDEO_PROJECT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), BRASERO_TYPE_VIDEO_PROJECT, BraseroVideoProject))
+#define BRASERO_VIDEO_PROJECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), BRASERO_TYPE_VIDEO_PROJECT, BraseroVideoProjectClass))
+#define BRASERO_IS_VIDEO_PROJECT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BRASERO_TYPE_VIDEO_PROJECT))
+#define BRASERO_IS_VIDEO_PROJECT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), BRASERO_TYPE_VIDEO_PROJECT))
+#define BRASERO_VIDEO_PROJECT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), BRASERO_TYPE_VIDEO_PROJECT, BraseroVideoProjectClass))
+
+typedef struct _BraseroVideoProjectClass BraseroVideoProjectClass;
+typedef struct _BraseroVideoProject BraseroVideoProject;
+
+typedef struct _BraseroVideoFile BraseroVideoFile;
+struct _BraseroVideoFile {
+	BraseroVideoFile *prev;
+	BraseroVideoFile *next;
+
+	gchar *name;
+	gchar *uri;
+
+	BraseroSongInfo *info;
+
+	guint64 start;
+	guint64 end;
+
+	GdkPixbuf *snapshot;
+
+	guint editable:1;
+	guint is_loading:1;
+	guint is_reloading:1;
+	guint is_monitored:1;
+};
+
+struct _BraseroVideoProjectClass
+{
+#ifdef BUILD_INOTIFY
+	BraseroFileMonitorClass parent_class;
+#else
+	GObjectClass parent_class;
+#endif
+
+	/* virtual functions */
+
+	/**
+	 * num_nodes is the number of nodes that were at the root of the 
+	 * project.
+	 */
+	void		(*reset)		(BraseroVideoProject *project,
+						 guint num_nodes);
+
+	/* NOTE: node_added is also called when there is a moved node;
+	 * in this case a node_removed is first called and then the
+	 * following function is called (mostly to match GtkTreeModel
+	 * API). To detect such a case look at uri which will then be
+	 * set to NULL.
+	 * NULL uri can also happen when it's a created directory.
+	 * if return value is FALSE, node was invalidated during call */
+	gboolean	(*node_added)		(BraseroVideoProject *project,
+						 BraseroVideoFile *node);
+
+	/* This is more an unparent signal. It shouldn't be assumed that the
+	 * node was destroyed or not destroyed. Like the above function, it is
+	 * also called when a node is moved. */
+	void		(*node_removed)		(BraseroVideoProject *project,
+						 BraseroVideoFile *node);
+
+	void		(*node_changed)		(BraseroVideoProject *project,
+						 BraseroVideoFile *node);
+
+	/* NOTE: there is no node reordered as this list order cannot be changed */
+};
+
+struct _BraseroVideoProject
+{
+#ifdef BUILD_INOTIFY
+	BraseroFileMonitor parent_instance;
+#else
+	GObject parent_instance;
+#endif
+};
+
+GType brasero_video_project_get_type (void) G_GNUC_CONST;
+
+void
+brasero_video_file_free (BraseroVideoFile *file);
+
+guint64
+brasero_video_project_get_size (BraseroVideoProject *project);
+
+guint
+brasero_video_project_get_file_num (BraseroVideoProject *project);
+
+void
+brasero_video_project_reset (BraseroVideoProject *project);
+
+void
+brasero_video_project_move (BraseroVideoProject *project,
+			    BraseroVideoFile *file,
+			    BraseroVideoFile *next);
+
+void
+brasero_video_project_rename (BraseroVideoProject *project,
+			      BraseroVideoFile *file,
+			      const gchar *name);
+
+void
+brasero_video_project_remove_file (BraseroVideoProject *project,
+				   BraseroVideoFile *file);
+
+BraseroVideoFile *
+brasero_video_project_add_uri (BraseroVideoProject *project,
+			       const gchar *uri,
+			       BraseroVideoFile *sibling,
+			       gint64 start,
+			       gint64 end);
+
+BraseroDiscResult
+brasero_video_project_get_status (BraseroVideoProject *project);
+
+GSList *
+brasero_video_project_get_contents (BraseroVideoProject *project);
+
+BraseroVideoFile *
+brasero_video_project_get_nth_item (BraseroVideoProject *project,
+				    guint nth);
+guint
+brasero_video_project_get_item_index (BraseroVideoProject *project,
+				      BraseroVideoFile *file);
+
+G_END_DECLS
+
+#endif /* _BRASERO_VIDEO_PROJECT_H_ */

Added: branches/video/src/brasero-video-tree-model.c
==============================================================================
--- (empty file)
+++ branches/video/src/brasero-video-tree-model.c	Fri Jun 27 13:29:09 2008
@@ -0,0 +1,727 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * brasero
+ * Copyright (C) Philippe Rouquier 2007 <bonfire-app wanadoo fr>
+ * 
+ * brasero is free software.
+ * 
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * brasero is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with brasero.  If not, write to:
+ * 	The Free Software Foundation, Inc.,
+ * 	51 Franklin Street, Fifth Floor
+ * 	Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtktreednd.h>
+#include <gtk/gtkicontheme.h>
+
+#include "burn-basics.h"
+#include "brasero-utils.h"
+#include "brasero-video-project.h"
+#include "brasero-video-tree-model.h"
+
+#include "eggtreemultidnd.h"
+
+typedef struct _BraseroVideoTreeModelPrivate BraseroVideoTreeModelPrivate;
+struct _BraseroVideoTreeModelPrivate
+{
+	guint stamp;
+	GtkIconTheme *theme;
+};
+
+#define BRASERO_VIDEO_TREE_MODEL_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_VIDEO_TREE_MODEL, BraseroVideoTreeModelPrivate))
+
+static void
+brasero_video_tree_model_multi_drag_source_iface_init (gpointer g_iface, gpointer data);
+static void
+brasero_video_tree_model_drag_source_iface_init (gpointer g_iface, gpointer data);
+static void
+brasero_video_tree_model_drag_dest_iface_init (gpointer g_iface, gpointer data);
+static void
+brasero_video_tree_model_iface_init (gpointer g_iface, gpointer data);
+
+G_DEFINE_TYPE_WITH_CODE (BraseroVideoTreeModel,
+			 brasero_video_tree_model,
+			 BRASERO_TYPE_VIDEO_PROJECT,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+					        brasero_video_tree_model_iface_init)
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
+					        brasero_video_tree_model_drag_dest_iface_init)
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+					        brasero_video_tree_model_drag_source_iface_init)
+			 G_IMPLEMENT_INTERFACE (EGG_TYPE_TREE_MULTI_DRAG_SOURCE,
+					        brasero_video_tree_model_multi_drag_source_iface_init));
+
+
+/**
+ * This is mainly a list so the following functions are not implemented.
+ * But we may need them for AUDIO models when we display GAPs
+ */
+static gboolean
+brasero_video_tree_model_iter_parent (GtkTreeModel *model,
+				      GtkTreeIter *iter,
+				      GtkTreeIter *child)
+{
+	return FALSE;
+}
+
+static gboolean
+brasero_video_tree_model_iter_nth_child (GtkTreeModel *model,
+					 GtkTreeIter *iter,
+					 GtkTreeIter *parent,
+					 gint n)
+{
+	return FALSE;
+}
+
+static gint
+brasero_video_tree_model_iter_n_children (GtkTreeModel *model,
+					  GtkTreeIter *iter)
+{
+	return 0;
+}
+
+static gboolean
+brasero_video_tree_model_iter_has_child (GtkTreeModel *model,
+					 GtkTreeIter *iter)
+{
+	return FALSE;
+}
+
+static gboolean
+brasero_video_tree_model_iter_children (GtkTreeModel *model,
+				        GtkTreeIter *iter,
+				        GtkTreeIter *parent)
+{
+	return FALSE;
+}
+
+static void
+brasero_video_tree_model_get_value (GtkTreeModel *model,
+				    GtkTreeIter *iter,
+				    gint column,
+				    GValue *value)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	BraseroVideoTreeModel *self;
+	BraseroVideoFile *file;
+	GdkPixbuf *pixbuf;
+	gchar *text;
+
+	self = BRASERO_VIDEO_TREE_MODEL (model);
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (model);
+
+	/* make sure that iter comes from us */
+	g_return_if_fail (priv->stamp == iter->stamp);
+	g_return_if_fail (iter->user_data != NULL);
+
+	file = iter->user_data;
+
+	switch (column) {
+	case BRASERO_VIDEO_TREE_MODEL_NAME:
+		g_value_init (value, G_TYPE_STRING);
+
+		if (file->name)
+			g_value_set_string (value, file->name);
+		else if (file->info && file->info->title)
+			g_value_set_string (value, file->info->title);
+		else {
+			gchar *name;
+			gchar *path;
+			gchar *unescaped;
+
+			unescaped = g_uri_unescape_string (file->uri, NULL);
+			path = g_filename_from_uri (unescaped, NULL, NULL);
+			g_free (unescaped);
+
+			name = g_path_get_basename (path);
+			g_free (path);
+
+			g_value_set_string (value, name);
+			g_free (name);
+		}
+
+		return;
+
+	case BRASERO_VIDEO_TREE_MODEL_MIME_ICON:
+		g_value_init (value, GDK_TYPE_PIXBUF);
+
+		if (file->snapshot) {
+			pixbuf = file->snapshot;
+			g_object_ref (file->snapshot);
+		}
+		else if (file->is_loading) {
+			pixbuf = gtk_icon_theme_load_icon (priv->theme,
+							   "image-loading",
+							   96,
+							   0,
+							   NULL);
+		}
+		else {
+			pixbuf = gtk_icon_theme_load_icon (priv->theme,
+							   "image-missing",
+							   96,
+							   0,
+							   NULL);
+		}
+
+		g_value_set_object (value, pixbuf);
+		g_object_unref (pixbuf);
+
+		return;
+
+	case BRASERO_VIDEO_TREE_MODEL_SIZE:
+		g_value_init (value, G_TYPE_STRING);
+
+		if (!file->is_loading) {
+			text = brasero_utils_get_time_string (file->end - file->start, TRUE, FALSE);
+			g_value_set_string (value, text);
+			g_free (text);
+		}
+		else
+			g_value_set_string (value, _("loading"));
+
+		return;
+
+	case BRASERO_VIDEO_TREE_MODEL_EDITABLE:
+		g_value_init (value, G_TYPE_BOOLEAN);
+		g_value_set_boolean (value, file->editable);
+
+		return;
+
+	default:
+		return;
+	}
+
+	return;
+}
+
+GtkTreePath *
+brasero_video_tree_model_file_to_path (BraseroVideoTreeModel *self,
+				       BraseroVideoFile *file)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	GtkTreePath *path;
+	guint nth;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (self);
+
+	path = gtk_tree_path_new ();
+	nth = brasero_video_project_get_item_index (BRASERO_VIDEO_PROJECT (self), file);
+	gtk_tree_path_prepend_index (path, nth);
+
+	return path;
+}
+
+static GtkTreePath *
+brasero_video_tree_model_get_path (GtkTreeModel *model,
+				   GtkTreeIter *iter)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	BraseroVideoFile *file;
+	GtkTreePath *path;
+	guint nth;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (model);
+
+	/* make sure that iter comes from us */
+	g_return_val_if_fail (priv->stamp == iter->stamp, NULL);
+	g_return_val_if_fail (iter->user_data != NULL, NULL);
+
+	file = iter->user_data;
+
+	/* NOTE: there is only one single file without a name: root */
+	path = gtk_tree_path_new ();
+	nth = brasero_video_project_get_item_index (BRASERO_VIDEO_PROJECT (model), file);
+	gtk_tree_path_prepend_index (path, nth);
+
+	return path;
+}
+
+BraseroVideoFile *
+brasero_video_tree_model_path_to_file (BraseroVideoTreeModel *self,
+				       GtkTreePath *path)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	const gint *indices;
+	guint depth;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (self);
+
+	indices = gtk_tree_path_get_indices (path);
+	depth = gtk_tree_path_get_depth (path);
+
+	/* NOTE: it can happen that paths are depth 2 when there is DND but then
+	 * only the first index is relevant. */
+	if (depth > 2)
+		return NULL;
+
+	return brasero_video_project_get_nth_item (BRASERO_VIDEO_PROJECT (self), indices  [0]);
+}
+
+static gboolean
+brasero_video_tree_model_get_iter (GtkTreeModel *model,
+				   GtkTreeIter *iter,
+				   GtkTreePath *path)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	BraseroVideoFile *file;
+	const gint *indices;
+	guint depth;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (model);
+
+	depth = gtk_tree_path_get_depth (path);
+	if (depth > 2)
+		return FALSE;
+
+	indices = gtk_tree_path_get_indices (path);
+	file = brasero_video_project_get_nth_item (BRASERO_VIDEO_PROJECT (model),
+						   indices [0]);
+	if (!file)
+		return FALSE;
+
+	iter->user_data = file;
+	iter->stamp = priv->stamp;
+
+	return TRUE;
+}
+
+static gboolean
+brasero_video_tree_model_iter_next (GtkTreeModel *model,
+				    GtkTreeIter *iter)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	BraseroVideoFile *file;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (model);
+
+	/* make sure that iter comes from us */
+	g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);
+	g_return_val_if_fail (iter->user_data != NULL, FALSE);
+
+	file = iter->user_data;
+	if (!file || !file->next)
+		return FALSE;
+
+	iter->user_data = file->next;
+	return TRUE;
+}
+
+static GType
+brasero_video_tree_model_get_column_type (GtkTreeModel *model,
+					 gint index)
+{
+	switch (index) {
+	case BRASERO_VIDEO_TREE_MODEL_NAME:
+		return G_TYPE_STRING;
+
+	case BRASERO_VIDEO_TREE_MODEL_MIME_ICON:
+		return GDK_TYPE_PIXBUF;
+
+	case BRASERO_VIDEO_TREE_MODEL_SIZE:
+		return G_TYPE_STRING;
+
+	case BRASERO_VIDEO_TREE_MODEL_EDITABLE:
+		return G_TYPE_BOOLEAN;
+
+	default:
+		break;
+	}
+
+	return G_TYPE_INVALID;
+}
+
+static gint
+brasero_video_tree_model_get_n_columns (GtkTreeModel *model)
+{
+	return BRASERO_VIDEO_TREE_MODEL_COL_NUM;
+}
+
+static GtkTreeModelFlags
+brasero_video_tree_model_get_flags (GtkTreeModel *model)
+{
+	return GTK_TREE_MODEL_LIST_ONLY;
+}
+
+static gboolean
+brasero_video_tree_model_multi_row_draggable (EggTreeMultiDragSource *drag_source,
+					      GList *path_list)
+{
+	/* All rows are draggable so return TRUE */
+	return TRUE;
+}
+
+static gboolean
+brasero_video_tree_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source,
+					      GList *path_list,
+					      GtkSelectionData *selection_data)
+{
+	if (selection_data->target == gdk_atom_intern (BRASERO_DND_TARGET_SELF_FILE_NODES, TRUE)) {
+		BraseroDNDVideoContext context;
+
+		context.model = GTK_TREE_MODEL (drag_source);
+		context.references = path_list;
+
+		gtk_selection_data_set (selection_data,
+					gdk_atom_intern_static_string (BRASERO_DND_TARGET_SELF_FILE_NODES),
+					8,
+					(void *) &context,
+					sizeof (context));
+	}
+	else
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+brasero_video_tree_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source,
+						 GList *path_list)
+{
+	/* NOTE: it's not the data in the selection_data here that should be
+	 * deleted but rather the rows selected when there is a move. FALSE
+	 * here means that we didn't delete anything. */
+	/* return TRUE to stop other handlers */
+	return TRUE;
+}
+
+static gboolean
+brasero_video_tree_model_drag_data_received (GtkTreeDragDest *drag_dest,
+					     GtkTreePath *dest_path,
+					     GtkSelectionData *selection_data)
+{
+	BraseroVideoFile *file;
+	BraseroVideoFile *sibling;
+	BraseroVideoTreeModel *self;
+
+	self = BRASERO_VIDEO_TREE_MODEL (drag_dest);
+
+	/* The new row(s) must be before dest_path but after our sibling */
+	sibling = brasero_video_tree_model_path_to_file (BRASERO_VIDEO_TREE_MODEL (drag_dest), dest_path);
+		
+	/* Received data: see where it comes from:
+	 * - from us, then that's a simple move
+	 * - from another widget then it's going to be URIS and we add
+	 *   them to VideoProject */
+	if (selection_data->target == gdk_atom_intern (BRASERO_DND_TARGET_SELF_FILE_NODES, TRUE)) {
+		BraseroDNDVideoContext *context;
+		GList *iter;
+
+		context = (BraseroDNDVideoContext *) selection_data->data;
+		if (context->model != GTK_TREE_MODEL (drag_dest))
+			return TRUE;
+
+		/* That's us: move the row and its children. */
+		for (iter = context->references; iter; iter = iter->next) {
+			GtkTreeRowReference *reference;
+			GtkTreePath *treepath;
+
+			reference = iter->data;
+			treepath = gtk_tree_row_reference_get_path (reference);
+
+			file = brasero_video_tree_model_path_to_file (BRASERO_VIDEO_TREE_MODEL (drag_dest), treepath);
+			gtk_tree_path_free (treepath);
+
+			brasero_video_project_move (BRASERO_VIDEO_PROJECT (self), file, sibling);
+		}
+	}
+	else if (selection_data->target == gdk_atom_intern ("text/uri-list", TRUE)) {
+		gint i;
+		gchar **uris;
+		gboolean success = FALSE;
+
+		/* NOTE: there can be many URIs at the same time. One
+		 * success is enough to return TRUE. */
+		success = FALSE;
+		uris = gtk_selection_data_get_uris (selection_data);
+		if (!uris)
+			return TRUE;
+
+		for (i = 0; uris [i]; i ++) {
+			/* Add the URIs to the project */
+			brasero_video_project_add_uri (BRASERO_VIDEO_PROJECT (self),
+						       uris [i],
+						       sibling,
+						       -1,
+						       -1);
+		}
+		g_strfreev (uris);
+	}
+	else
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+brasero_video_tree_model_row_drop_possible (GtkTreeDragDest *drag_dest,
+					    GtkTreePath *dest_path,
+					    GtkSelectionData *selection_data)
+{
+	/* It's always possible */
+	return TRUE;
+}
+
+static gboolean
+brasero_video_tree_model_drag_data_delete (GtkTreeDragSource *source,
+					   GtkTreePath *treepath)
+{
+	return TRUE;
+}
+
+static void
+brasero_video_tree_model_clear (BraseroVideoTreeModel *self,
+				guint num_files)
+{
+	GtkTreePath *treepath;
+	BraseroVideoTreeModelPrivate *priv;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (self);
+
+	/* NOTE: no need to move to the next row since previous one was deleted */
+	treepath = gtk_tree_path_new_first ();
+	while (num_files > 0) {
+		num_files --;
+		gtk_tree_model_row_deleted (GTK_TREE_MODEL (self), treepath);
+	}
+	gtk_tree_path_free (treepath);
+}
+
+static void
+brasero_video_tree_model_reset (BraseroVideoProject *project,
+				guint num_files)
+{
+	brasero_video_tree_model_clear (BRASERO_VIDEO_TREE_MODEL (project), num_files);
+
+	/* chain up this function except if we invalidated the file */
+	if (BRASERO_VIDEO_PROJECT_CLASS (brasero_video_tree_model_parent_class)->reset)
+		BRASERO_VIDEO_PROJECT_CLASS (brasero_video_tree_model_parent_class)->reset (project, num_files);
+}
+
+static gboolean
+brasero_video_tree_model_file_added (BraseroVideoProject *project,
+				     BraseroVideoFile *file)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (project);
+
+	iter.stamp = priv->stamp;
+	iter.user_data = file;
+
+	path = brasero_video_tree_model_file_to_path (BRASERO_VIDEO_TREE_MODEL (project), file);
+
+	/* if the file is reloading (because of a file system change or because
+	 * it was a file that was a tmp folder) then no need to signal an added
+	 * signal but a changed one */
+	if (file->is_reloading) {
+		gtk_tree_model_row_changed (GTK_TREE_MODEL (project),
+					    path,
+					    &iter);
+		gtk_tree_path_free (path);
+		goto end;
+	}
+
+	/* Add the row itself */
+	gtk_tree_model_row_inserted (GTK_TREE_MODEL (project),
+				     path,
+				     &iter);
+	gtk_tree_path_free (path);
+
+end:
+	/* chain up this function */
+	if (BRASERO_VIDEO_PROJECT_CLASS (brasero_video_tree_model_parent_class)->node_added)
+		return BRASERO_VIDEO_PROJECT_CLASS (brasero_video_tree_model_parent_class)->node_added (project,
+													file);
+
+	return TRUE;
+}
+
+static void
+brasero_video_tree_model_file_removed (BraseroVideoProject *project,
+				       BraseroVideoFile *file)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	BraseroVideoFile *next;
+	GtkTreePath *path;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (project);
+
+	/* remove the file. */
+	next = file->next;
+	path = brasero_video_tree_model_file_to_path (BRASERO_VIDEO_TREE_MODEL (project), next);
+	gtk_tree_model_row_deleted (GTK_TREE_MODEL (project), path);
+	gtk_tree_path_free (path);
+
+	/* chain up this function */
+	if (BRASERO_VIDEO_PROJECT_CLASS (brasero_video_tree_model_parent_class)->node_removed)
+		BRASERO_VIDEO_PROJECT_CLASS (brasero_video_tree_model_parent_class)->node_removed (project,
+												   file);
+}
+
+static void
+brasero_video_tree_model_file_changed (BraseroVideoProject *project,
+				       BraseroVideoFile *file)
+{
+	BraseroVideoTreeModelPrivate *priv;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (project);
+
+	/* Get the iter for the file */
+	iter.stamp = priv->stamp;
+	iter.user_data = file;
+
+	path = brasero_video_tree_model_file_to_path (BRASERO_VIDEO_TREE_MODEL (project), file);
+	gtk_tree_model_row_changed (GTK_TREE_MODEL (project),
+				    path,
+				    &iter);
+	gtk_tree_path_free (path);
+
+	/* chain up this function */
+	if (BRASERO_VIDEO_PROJECT_CLASS (brasero_video_tree_model_parent_class)->node_changed)
+		BRASERO_VIDEO_PROJECT_CLASS (brasero_video_tree_model_parent_class)->node_changed (project, file);
+}
+
+static void
+brasero_video_tree_model_init (BraseroVideoTreeModel *object)
+{
+	BraseroVideoTreeModelPrivate *priv;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (object);
+
+	priv->theme = gtk_icon_theme_get_default ();
+
+	do {
+		priv->stamp = g_random_int ();
+	} while (!priv->stamp);
+}
+
+static void
+brasero_video_tree_model_finalize (GObject *object)
+{
+	BraseroVideoTreeModelPrivate *priv;
+
+	priv = BRASERO_VIDEO_TREE_MODEL_PRIVATE (object);
+
+	if (priv->theme) {
+		g_object_unref (priv->theme);
+		priv->theme = NULL;
+	}
+
+	G_OBJECT_CLASS (brasero_video_tree_model_parent_class)->finalize (object);
+}
+
+static void
+brasero_video_tree_model_iface_init (gpointer g_iface, gpointer data)
+{
+	GtkTreeModelIface *iface = g_iface;
+	static gboolean initialized = FALSE;
+
+	if (initialized)
+		return;
+
+	initialized = TRUE;
+
+	iface->get_flags = brasero_video_tree_model_get_flags;
+	iface->get_n_columns = brasero_video_tree_model_get_n_columns;
+	iface->get_column_type = brasero_video_tree_model_get_column_type;
+	iface->get_iter = brasero_video_tree_model_get_iter;
+	iface->get_path = brasero_video_tree_model_get_path;
+	iface->get_value = brasero_video_tree_model_get_value;
+	iface->iter_next = brasero_video_tree_model_iter_next;
+	iface->iter_children = brasero_video_tree_model_iter_children;
+	iface->iter_has_child = brasero_video_tree_model_iter_has_child;
+	iface->iter_n_children = brasero_video_tree_model_iter_n_children;
+	iface->iter_nth_child = brasero_video_tree_model_iter_nth_child;
+	iface->iter_parent = brasero_video_tree_model_iter_parent;
+}
+
+static void
+brasero_video_tree_model_multi_drag_source_iface_init (gpointer g_iface, gpointer data)
+{
+	EggTreeMultiDragSourceIface *iface = g_iface;
+	static gboolean initialized = FALSE;
+
+	if (initialized)
+		return;
+
+	initialized = TRUE;
+
+	iface->row_draggable = brasero_video_tree_model_multi_row_draggable;
+	iface->drag_data_get = brasero_video_tree_model_multi_drag_data_get;
+	iface->drag_data_delete = brasero_video_tree_model_multi_drag_data_delete;
+}
+
+static void
+brasero_video_tree_model_drag_source_iface_init (gpointer g_iface, gpointer data)
+{
+	GtkTreeDragSourceIface *iface = g_iface;
+	static gboolean initialized = FALSE;
+
+	if (initialized)
+		return;
+
+	initialized = TRUE;
+
+	iface->drag_data_delete = brasero_video_tree_model_drag_data_delete;
+}
+
+static void
+brasero_video_tree_model_drag_dest_iface_init (gpointer g_iface, gpointer data)
+{
+	GtkTreeDragDestIface *iface = g_iface;
+	static gboolean initialized = FALSE;
+
+	if (initialized)
+		return;
+
+	initialized = TRUE;
+
+	iface->drag_data_received = brasero_video_tree_model_drag_data_received;
+	iface->row_drop_possible = brasero_video_tree_model_row_drop_possible;
+}
+
+static void
+brasero_video_tree_model_class_init (BraseroVideoTreeModelClass *klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+	BraseroVideoProjectClass *video_class = BRASERO_VIDEO_PROJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (BraseroVideoTreeModelPrivate));
+
+	object_class->finalize = brasero_video_tree_model_finalize;
+
+	video_class->reset = brasero_video_tree_model_reset;
+	video_class->node_added = brasero_video_tree_model_file_added;
+	video_class->node_removed = brasero_video_tree_model_file_removed;
+	video_class->node_changed = brasero_video_tree_model_file_changed;
+}
+
+BraseroVideoTreeModel *
+brasero_video_tree_model_new (void)
+{
+	return g_object_new (BRASERO_TYPE_VIDEO_TREE_MODEL, NULL);
+}

Added: branches/video/src/brasero-video-tree-model.h
==============================================================================
--- (empty file)
+++ branches/video/src/brasero-video-tree-model.h	Fri Jun 27 13:29:09 2008
@@ -0,0 +1,83 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * brasero
+ * Copyright (C) Philippe Rouquier 2007 <bonfire-app wanadoo fr>
+ * 
+ * brasero is free software.
+ * 
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * brasero is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with brasero.  If not, write to:
+ * 	The Free Software Foundation, Inc.,
+ * 	51 Franklin Street, Fifth Floor
+ * 	Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _BRASERO_VIDEO_TREE_MODEL_H_
+#define _BRASERO_VIDEO_TREE_MODEL_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* This DND target when moving nodes inside ourselves */
+#define BRASERO_DND_TARGET_SELF_FILE_NODES	"GTK_TREE_MODEL_ROW"
+
+struct _BraseroDNDVideoContext {
+	GtkTreeModel *model;
+	GList *references;
+};
+typedef struct _BraseroDNDVideoContext BraseroDNDVideoContext;
+
+typedef enum {
+	BRASERO_VIDEO_TREE_MODEL_NAME		= 0,
+	BRASERO_VIDEO_TREE_MODEL_MIME_ICON,
+	BRASERO_VIDEO_TREE_MODEL_SIZE,
+	BRASERO_VIDEO_TREE_MODEL_EDITABLE,
+	BRASERO_VIDEO_TREE_MODEL_COL_NUM
+} BraseroVideoProjectColumn;
+
+#define BRASERO_TYPE_VIDEO_TREE_MODEL             (brasero_video_tree_model_get_type ())
+#define BRASERO_VIDEO_TREE_MODEL(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), BRASERO_TYPE_VIDEO_TREE_MODEL, BraseroVideoTreeModel))
+#define BRASERO_VIDEO_TREE_MODEL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), BRASERO_TYPE_VIDEO_TREE_MODEL, BraseroVideoTreeModelClass))
+#define BRASERO_IS_VIDEO_TREE_MODEL(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BRASERO_TYPE_VIDEO_TREE_MODEL))
+#define BRASERO_IS_VIDEO_TREE_MODEL_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), BRASERO_TYPE_VIDEO_TREE_MODEL))
+#define BRASERO_VIDEO_TREE_MODEL_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), BRASERO_TYPE_VIDEO_TREE_MODEL, BraseroVideoTreeModelClass))
+
+typedef struct _BraseroVideoTreeModelClass BraseroVideoTreeModelClass;
+typedef struct _BraseroVideoTreeModel BraseroVideoTreeModel;
+
+struct _BraseroVideoTreeModelClass
+{
+	BraseroVideoProjectClass parent_class;
+};
+
+struct _BraseroVideoTreeModel
+{
+	BraseroVideoProject parent_instance;
+};
+
+GType brasero_video_tree_model_get_type (void) G_GNUC_CONST;
+
+BraseroVideoTreeModel *
+brasero_video_tree_model_new (void);
+
+BraseroVideoFile *
+brasero_video_tree_model_path_to_file (BraseroVideoTreeModel *self,
+				       GtkTreePath *path);
+GtkTreePath *
+brasero_video_tree_model_file_to_path (BraseroVideoTreeModel *self,
+				       BraseroVideoFile *file);
+
+G_END_DECLS
+
+#endif /* _BRASERO_VIDEO_TREE_MODEL_H_ */



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