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