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", ¬e,
+ "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);
};