Blob Blame History Raw
diff -ru planner-0.14.4.orig/src/planner-task-tree.c planner-0.14.4/src/planner-task-tree.c
--- planner-0.14.4.orig/src/planner-task-tree.c	2010-01-18 14:00:58.000000000 +0000
+++ planner-0.14.4/src/planner-task-tree.c	2010-01-21 12:39:49.000000000 +0000
@@ -48,6 +48,9 @@
 	SELECTION_CHANGED,
 	RELATION_ADDED,
 	RELATION_REMOVED,
+	CUT_CLIPBOARD,
+	COPY_CLIPBOARD,
+	PASTE_CLIPBOARD,
 	LAST_SIGNAL
 };
 
@@ -69,6 +72,13 @@
 	 */
 	GHashTable     *task_dialogs;
 	GtkTreePath    *anchor;
+
+	GtkTargetList  *copy_target_list;
+	GtkTargetEntry *copy_target_entries;
+	gint            n_copy_target_entries;
+
+	guchar	       *copy_data;
+	guint           copy_length;
 };
 
 typedef struct {
@@ -178,6 +188,10 @@
 static MrpProject *task_tree_get_project               (PlannerTaskTree      *tree);
 static MrpTask *   task_tree_get_task_from_path        (PlannerTaskTree      *tree,
                                                         GtkTreePath          *path);
+static void        planner_task_tree_free_target_lists (PlannerTaskTree      *tree);
+static void        planner_task_tree_cut_clipboard     (PlannerTaskTree      *tree);
+static void        planner_task_tree_copy_clipboard    (PlannerTaskTree      *tree);
+static void        planner_task_tree_paste_clipboard   (PlannerTaskTree      *tree);
 
 
 static GtkTreeViewClass *parent_class = NULL;
@@ -554,16 +568,17 @@
 }
 
 static PlannerCmd *
-task_cmd_remove (PlannerTaskTree *tree,
+task_cmd_remove_with_undo_strings (PlannerTaskTree *tree,
 		 GtkTreePath     *path,
-		 MrpTask         *task)
+		 MrpTask         *task,
+		 const char      *undostr)
 {
 	PlannerTaskTreePriv *priv = tree->priv;
 	PlannerCmd          *cmd_base;
 	TaskCmdRemove       *cmd;
 
 	cmd_base = planner_cmd_new (TaskCmdRemove,
-				    _("Remove task"),
+				    undostr,
 				    task_cmd_remove_do,
 				    task_cmd_remove_undo,
 				    task_cmd_remove_free);
@@ -1005,6 +1020,7 @@
 task_tree_class_init (PlannerTaskTreeClass *klass)
 {
 	GObjectClass *o_class;
+	GtkBindingSet *binding_set;
 
 	parent_class = g_type_class_peek_parent (klass);
 
@@ -1037,6 +1053,54 @@
 			      planner_marshal_VOID__OBJECT_OBJECT,
 			      G_TYPE_NONE,
 			      2, MRP_TYPE_TASK, MRP_TYPE_RELATION);
+
+	klass->cut_clipboard = planner_task_tree_cut_clipboard;
+	klass->copy_clipboard = planner_task_tree_copy_clipboard;
+	klass->paste_clipboard = planner_task_tree_paste_clipboard;
+
+	signals[CUT_CLIPBOARD] =
+		g_signal_new ("cut-clipboard",
+			      G_OBJECT_CLASS_TYPE (o_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+			      G_STRUCT_OFFSET (PlannerTaskTreeClass, cut_clipboard),
+			      NULL, NULL,
+			      planner_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+	signals[COPY_CLIPBOARD] =
+		g_signal_new ("copy-clipboard",
+			      G_OBJECT_CLASS_TYPE (o_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+			      G_STRUCT_OFFSET (PlannerTaskTreeClass, copy_clipboard),
+			      NULL, NULL,
+			      planner_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+	signals[PASTE_CLIPBOARD] =
+		g_signal_new ("paste-clipboard",
+			      G_OBJECT_CLASS_TYPE (o_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+			      G_STRUCT_OFFSET (PlannerTaskTreeClass, paste_clipboard),
+			      NULL, NULL,
+			      planner_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);
+
+
+	binding_set = gtk_binding_set_by_class (klass);
+
+	gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
+				      "cut-clipboard", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
+				      "copy-clipboard", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
+				      "paste-clipboard", 0);
+
+	gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_SHIFT_MASK,
+				      "cut-clipboard", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_CONTROL_MASK,
+				      "copy-clipboard", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_SHIFT_MASK,
+				      "paste-clipboard", 0);
 }
 
 static void
@@ -1065,6 +1129,14 @@
 }
 
 static void
+planner_task_tree_free_selection (PlannerTaskTree *tree)
+{
+	g_free(tree->priv->copy_data);
+	tree->priv->copy_data = NULL;
+	tree->priv->copy_length = 0;
+}
+
+static void
 task_tree_finalize (GObject *object)
 {
 	PlannerTaskTree     *tree;
@@ -1073,6 +1145,9 @@
 	tree = PLANNER_TASK_TREE (object);
 	priv = tree->priv;
 
+	planner_task_tree_free_target_lists (tree);
+	planner_task_tree_free_selection (tree);
+
 	g_hash_table_destroy (priv->property_to_column);
 
 	planner_task_tree_set_anchor (tree, NULL);
@@ -2739,76 +2814,71 @@
 	g_list_free (list);
 }
 
-void
-planner_task_tree_insert_task (PlannerTaskTree *tree)
+static void
+planner_task_tree_create_new_position (PlannerTaskTree *tree, MrpTask **parent, gint *position, GtkTreePath **path)
 {
 	PlannerTaskTreePriv *priv;
-	GtkTreeView         *tree_view;
 	PlannerGanttModel   *model;
-	GtkTreePath         *path;
-	MrpTask             *parent;
 	GList               *list;
-	gint                 work;
-	gint                 position;
 	gint                 depth;
 
 	priv = tree->priv;
 
 	list = planner_task_tree_get_selected_tasks (tree);
 	if (list == NULL) {
-		parent = NULL;
-		position = -1;
+		*parent = NULL;
+		*position = -1;
 	} else {
-		parent = mrp_task_get_parent (list->data);
-		position = mrp_task_get_position (list->data) + 1;
+		*parent = mrp_task_get_parent (list->data);
+		*position = mrp_task_get_position (list->data) + 1;
 
-		if (mrp_task_get_parent (parent) == NULL) {
-			parent = NULL;
+		if (mrp_task_get_parent (*parent) == NULL) {
+			*parent = NULL;
 		}
 	}
+	g_list_free (list);
 
-	if (parent) {
+	if (*parent) {
 		model = PLANNER_GANTT_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (tree)));
 
-		path = planner_gantt_model_get_path_from_task (model, parent);
-		gtk_tree_path_append_index (path, position);
+		*path = planner_gantt_model_get_path_from_task (model, *parent);
+		gtk_tree_path_append_index (*path, *position);
 	} else {
-		path = gtk_tree_path_new ();
-		if (position != -1) {
-			gtk_tree_path_append_index (path, position);
+		*path = gtk_tree_path_new ();
+		if (*position != -1) {
+			gtk_tree_path_append_index (*path, *position);
 		} else {
 			MrpTask *root;
 
 			/* Put the new task at the end of end of the list. */
 			root = mrp_project_get_root_task (priv->project);
-			position = mrp_task_get_n_children (root);
+			*position = mrp_task_get_n_children (root);
 
-			gtk_tree_path_append_index (path, position);
+			gtk_tree_path_append_index (*path, *position);
 		}
 	}
 
-	work = mrp_calendar_day_get_total_work (
-		mrp_project_get_calendar (priv->project),
-		mrp_day_get_work ());
-
-	depth = gtk_tree_path_get_depth (path);
-	position = gtk_tree_path_get_indices (path)[depth - 1];
+	depth = gtk_tree_path_get_depth (*path);
+	*position = gtk_tree_path_get_indices (*path)[depth - 1];
 
 	if (depth > 1) {
 		GtkTreePath *parent_path;
 
-		parent_path = gtk_tree_path_copy (path);
+		parent_path = gtk_tree_path_copy (*path);
 		gtk_tree_path_up (parent_path);
 
-		parent = task_tree_get_task_from_path (tree, parent_path);
+		*parent = task_tree_get_task_from_path (tree, parent_path);
 
 		gtk_tree_path_free (parent_path);
 	} else {
-		parent = NULL;
+		*parent = NULL;
 	}
+}
 
-	planner_task_cmd_insert (tree->priv->main_window,
-				 parent, position, work, work, NULL);
+void
+planner_task_select_inserted_task (PlannerTaskTree *tree, GtkTreePath *path)
+{
+	GtkTreeView         *tree_view;
 
 	if (!GTK_WIDGET_HAS_FOCUS (tree)) {
 		gtk_widget_grab_focus (GTK_WIDGET (tree));
@@ -2822,12 +2892,35 @@
 				  TRUE);
 
 	planner_task_tree_set_anchor (tree, path);
-
-	g_list_free (list);
 }
 
+
 void
-planner_task_tree_remove_task (PlannerTaskTree *tree)
+planner_task_tree_insert_task (PlannerTaskTree *tree)
+{
+	PlannerTaskTreePriv *priv;
+	GtkTreePath         *path;
+	MrpTask             *parent;
+	gint                 work;
+	gint                 position;
+	PlannerCmd          *cmd;
+
+	priv = tree->priv;
+	
+	work = mrp_calendar_day_get_total_work (
+		mrp_project_get_calendar (priv->project),
+		mrp_day_get_work ());
+	
+	planner_task_tree_create_new_position (tree, &parent, &position, &path);
+
+	cmd = planner_task_cmd_insert (tree->priv->main_window, 
+				       parent, position, work, work, NULL);
+
+	planner_task_select_inserted_task(tree, path);
+}
+
+static void
+planner_task_tree_remove_task_with_undo_strings (PlannerTaskTree *tree, const char *multiundostr, const char *undostr)
 {
 	PlannerTaskTreePriv *priv;
 	GList               *list, *l;
@@ -2852,7 +2945,7 @@
 	if (many) {
 		planner_cmd_manager_begin_transaction (
 			planner_window_get_cmd_manager (priv->main_window),
-			_("Remove tasks"));
+			multiundostr);
 	}
 
 	for (l = list; l; l = l->next) {
@@ -2863,7 +2956,7 @@
 
 		/* Children are removed with the parent. */
 		if (path != NULL) {
-			task_cmd_remove (tree, path, task);
+			task_cmd_remove_with_undo_strings (tree, path, task, undostr);
 		}
 		gtk_tree_path_free (path);
 	}
@@ -2879,6 +2972,12 @@
 }
 
 void
+planner_task_tree_remove_task (PlannerTaskTree *tree)
+{
+	planner_task_tree_remove_task_with_undo_strings (tree, _("Remove tasks"), _("Remove task"));
+}
+
+void
 planner_task_tree_edit_task (PlannerTaskTree *tree, PlannerTaskDialogPage page)
 {
 	PlannerTaskTreePriv *priv;
@@ -3662,6 +3761,321 @@
 	return list;
 }
 
+static GtkTargetList *
+planner_task_tree_ensure_copy_target_list (PlannerTaskTree *tree)
+{
+	PlannerTaskTreePriv *priv;
+
+	priv = tree->priv;
+
+	if (! priv->copy_target_list)
+	{
+		priv->copy_target_list = gtk_target_list_new (NULL, 0);
+
+		gtk_target_list_add (priv->copy_target_list,
+				     gdk_atom_intern ("PLANNER_TASK_TREE", FALSE),
+				     0, 0xbeaf);
+
+		priv->copy_target_entries = gtk_target_table_new_from_list (priv->copy_target_list, &priv->n_copy_target_entries);
+	}
+
+	return priv->copy_target_list;
+}
+
+static void
+planner_task_tree_free_target_lists (PlannerTaskTree *tree)
+{
+	PlannerTaskTreePriv *priv;
+
+	priv = tree->priv;
+
+	if (priv->copy_target_list)
+	{
+		gtk_target_list_unref (priv->copy_target_list);
+		priv->copy_target_list = NULL;
+
+		gtk_target_table_free (priv->copy_target_entries,
+				       priv->n_copy_target_entries);
+		priv->copy_target_entries = NULL;
+		priv->n_copy_target_entries = 0;
+	}
+}
+
+static void
+planner_task_tree_get_selection (PlannerTaskTree *tree)
+{
+        GList             *tasks;
+        GList             *l;
+        GString           *output;
+
+	planner_task_tree_free_selection(tree);
+
+        tasks = planner_task_tree_get_selected_tasks (tree);
+
+	output = g_string_new("<tasks>");
+
+	for (l = tasks; l; l = l->next) {
+		gchar         *name=NULL;
+		gchar         *note=NULL;
+		gint          duration=0, work=0, complete=0;
+		gint          priority=0;
+		MrpConstraint *constraint=NULL;
+		MrpTaskType   type = MRP_TASK_TYPE_NORMAL;
+		MrpTaskType   sched = MRP_TASK_SCHED_FIXED_WORK;
+		MrpTask       *task = l->data;
+		int	      depth=0;
+		g_object_get (task,
+			      "name", &name,
+			      "note", &note,
+			      "duration", &duration,
+			      "work", &work,
+			      "percent-complete", &complete,
+			      "priority", &priority,
+			      "type", &type, 
+			      "sched", &sched,
+			      "constraint", &constraint,
+			      NULL);
+		g_string_append(output, "<task");
+
+		name = g_markup_escape_text(name, -1);
+		g_string_append_printf(output, " name=\"%s\"", name);
+		g_free(name);
+
+		note = g_markup_escape_text(note, -1);
+		g_string_append_printf(output, " note=\"%s\"", note);
+		g_free(note);
+
+		g_string_append_printf(output, " duration=\"%d\"", duration);
+		g_string_append_printf(output, " work=\"%d\"", work);
+		g_string_append_printf(output, " percent-complete=\"%d\"", complete);
+		g_string_append_printf(output, " priority=\"%d\"", priority);
+		g_string_append_printf(output, " type=\"%d\"", type);
+		g_string_append_printf(output, " sched=\"%d\"", sched);
+
+		g_string_append_printf(output, " constraint=\"%d\"", constraint->type);
+		g_string_append_printf(output, " time=\"%ld\"", constraint->time);
+		g_free (constraint);
+
+		while ((task = mrp_task_get_parent (task)))
+			++depth;
+
+		g_string_append_printf(output, " depth=\"%d\"", depth);
+
+		g_string_append(output, "/>");
+	}
+	g_string_append(output, "</tasks>");
+	g_list_free (tasks);
+
+	tree->priv->copy_length = output->len;
+	tree->priv->copy_data = (guchar*)g_string_free(output, FALSE);
+}
+
+static void
+clipboard_get_selection_cb (GtkClipboard     *clipboard,
+			    GtkSelectionData *selection_data,
+			    guint             info,
+			    gpointer          data)
+{
+        PlannerTaskTree   *tree = (PlannerTaskTree*)data;
+
+	if (info != 0xbeaf)
+		return;
+
+	gtk_selection_data_set (selection_data,
+				selection_data->target,
+				8, /* bytes */
+				tree->priv->copy_data,
+				tree->priv->copy_length);
+}
+
+static void
+planner_task_tree_copy_clipboard (PlannerTaskTree *tree)
+{
+	PlannerTaskTreePriv *priv;
+
+	planner_task_tree_ensure_copy_target_list(tree);
+	planner_task_tree_get_selection (tree);
+
+	priv = tree->priv;
+
+	gtk_clipboard_set_with_data (gtk_widget_get_clipboard (GTK_WIDGET (tree),
+							       GDK_SELECTION_CLIPBOARD),
+			             priv->copy_target_entries,
+			             priv->n_copy_target_entries,
+			             clipboard_get_selection_cb,
+			             NULL,
+			             tree);
+}
+
+static void
+planner_task_tree_cut_clipboard (PlannerTaskTree *tree)
+{
+	planner_task_tree_copy_clipboard (tree);
+	planner_task_tree_remove_task_with_undo_strings (tree, _("Cut"), _("Cut"));
+}
+
+typedef struct {
+	PlannerTaskTree *tree;
+	int depth;
+} PasteContext;
+
+static void
+start_element (GMarkupParseContext *context,
+	       const gchar         *element_name,
+	       const gchar        **attribute_names,
+	       const gchar        **attribute_values,
+	       gpointer             user_data,
+	       GError             **error)
+{
+	const gchar **name_cursor = attribute_names;
+	const gchar **value_cursor = attribute_values;
+	PasteContext *paste_ctx = (PasteContext*)user_data;
+	PlannerTaskTree *tree = paste_ctx->tree;
+	MrpTask *task;
+	gint work;
+	MrpTask *parent;
+	gint position;
+	int depth=1,inserted_depth=0,desired_depth, i;
+	GtkTreePath *path;
+	PlannerCmd *cmd;
+	MrpConstraint constraint;
+
+	if (strcmp (element_name, "task") != 0)
+		return;
+
+	task = g_object_new (MRP_TYPE_TASK, NULL);
+
+	while (*name_cursor)
+	{
+		if (strcmp (*name_cursor, "name") == 0)
+			g_object_set (task, "name", *value_cursor, NULL);
+		else if (strcmp (*name_cursor, "note") == 0)
+			g_object_set (task, "note", *value_cursor, NULL);
+		else if (strcmp (*name_cursor, "duration") == 0)
+		{
+			gint duration = g_ascii_strtoll(*value_cursor, NULL, 10);
+			g_object_set (task, "duration", duration, NULL);
+		}
+		else if (strcmp (*name_cursor, "work") == 0)
+		{
+			gint work = g_ascii_strtoll(*value_cursor, NULL, 10);
+			g_object_set (task, "work", work, NULL);
+		}
+		else if (strcmp (*name_cursor, "percent-complete") == 0)
+		{
+			gint complete = g_ascii_strtoll(*value_cursor, NULL, 10);
+			g_object_set (task, "percent-complete", complete, NULL);
+		}
+		else if (strcmp (*name_cursor, "priority") == 0)
+		{
+			gint priority = g_ascii_strtoll(*value_cursor, NULL, 10);
+			g_object_set (task, "priority", priority, NULL);
+		}
+		else if (strcmp (*name_cursor, "type") == 0)
+		{
+			MrpTaskType type = g_ascii_strtoll(*value_cursor, NULL, 10);
+			g_object_set (task, "type", type, NULL);
+		}
+		else if (strcmp (*name_cursor, "sched") == 0)
+		{
+			MrpTaskSched sched = g_ascii_strtoll(*value_cursor, NULL, 10);
+			g_object_set (task, "sched", sched, NULL);
+		}
+		else if (strcmp (*name_cursor, "constraint") == 0)
+			constraint.type = g_ascii_strtoll(*value_cursor, NULL, 10);
+		else if (strcmp (*name_cursor, "time") == 0)
+			constraint.time = g_ascii_strtoll(*value_cursor, NULL, 10);
+		else if (strcmp (*name_cursor, "depth") == 0)
+			depth = g_ascii_strtoll(*value_cursor, NULL, 10);
+		name_cursor++;
+		value_cursor++;
+	}
+
+	g_object_set (task, "constraint", &constraint, NULL);
+
+        work = mrp_calendar_day_get_total_work (
+                mrp_project_get_calendar (tree->priv->project),
+                mrp_day_get_work ());
+
+        planner_task_tree_create_new_position (tree, &parent, &position, &path);
+
+        cmd = planner_task_cmd_insert (tree->priv->main_window,
+                                       parent, position, work, work, task);
+
+        while ((task = mrp_task_get_parent (task)))
+        	++inserted_depth;
+
+	planner_task_select_inserted_task(tree, path);
+
+	desired_depth = paste_ctx->depth+depth-1;
+
+	for (i = inserted_depth; i > desired_depth; --i)
+		planner_task_tree_unindent_task (tree);
+
+	for (i = inserted_depth; i < desired_depth; ++i)
+		planner_task_tree_indent_task (tree);
+}
+
+static void
+clipboard_targets_received (GtkClipboard     *clipboard,
+			    GtkSelectionData *selection_data,
+			    gpointer          user_data)
+{
+	static GMarkupParser parser = {
+		start_element,
+		NULL,
+		NULL,
+		NULL,
+		NULL
+	};
+	GMarkupParseContext *context;
+	PlannerTaskTree *tree = (PlannerTaskTree*)user_data;
+	PasteContext paste_ctx;
+        GList *tasks;
+        MrpTask *task;
+
+	g_return_if_fail (selection_data != NULL);
+
+	if (selection_data->type != gdk_atom_intern ("PLANNER_TASK_TREE", FALSE))
+		return;
+
+	planner_cmd_manager_begin_transaction (
+        	planner_window_get_cmd_manager (tree->priv->main_window),
+		_("Paste"));
+
+        tasks = planner_task_tree_get_selected_tasks (tree);
+        if (!tasks || !tasks->data)
+		paste_ctx.depth = 1;
+	else
+	{
+		paste_ctx.depth = 0;
+	        task = tasks->data;
+	        while ((task = mrp_task_get_parent (task)))
+	        	++paste_ctx.depth;
+	}
+        g_list_free (tasks);
+	paste_ctx.tree = tree;
+
+	context = g_markup_parse_context_new ( &parser, 0, &paste_ctx, NULL);
+	g_markup_parse_context_parse (context, (gchar*)selection_data->data, selection_data->length, NULL);
+	g_markup_parse_context_free (context);
+
+	planner_cmd_manager_end_transaction (
+		planner_window_get_cmd_manager (tree->priv->main_window));
+}
+
+static void
+planner_task_tree_paste_clipboard (PlannerTaskTree *tree)
+{
+	planner_task_tree_ensure_copy_target_list(tree);
+
+	gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (tree),
+								  GDK_SELECTION_CLIPBOARD),
+					gdk_atom_intern ("PLANNER_TASK_TREE", FALSE),
+					clipboard_targets_received,
+					(gpointer) tree);
+}
+
 /* Returns TRUE if one or more of the tasks in the list have links. */
 gboolean
 planner_task_tree_has_relation (GList *list)
diff -ru planner-0.14.4.orig/src/planner-task-tree.h planner-0.14.4/src/planner-task-tree.h
--- planner-0.14.4.orig/src/planner-task-tree.h	2010-01-18 14:00:58.000000000 +0000
+++ planner-0.14.4/src/planner-task-tree.h	2010-01-18 14:55:57.000000000 +0000
@@ -49,6 +49,9 @@
 struct _PlannerTaskTreeClass
 {
 	GtkTreeViewClass  parent_class;
+	void (* cut_clipboard)      (PlannerTaskTree *entry);
+	void (* copy_clipboard)     (PlannerTaskTree *entry);
+	void (* paste_clipboard)    (PlannerTaskTree *entry);
 };