Hi, I've attached a patch that allows you to change the % complete for a task by dragging it in the gantt chart. It usually has the same 'grabable' area as the task duration except when the ends of the % complete bar and the task bar are close together. Then both areas will shrink with the size of the overlap / 2, resulting in a minimum size of 3 for each when % complete is 100% (i.e. coincides with task end). The cursor is changed to GDK_HAND2 when over the area where you can start dragging %complete. Let me know what you think. Regards, Maurice. P.S.: Your software has already spared me a lot of MS-project-induced misery now that my manager regularly asks me for gantt charts containing the project planning. Thanks for that! -- Maurice van der Pot Gentoo Linux Developer griffon26 gentoo org http://www.gentoo.org Creator of BiteMe! griffon26 kfk4ever com http://www.kfk4ever.com
Index: src/planner-gantt-row.c =================================================================== RCS file: /cvs/gnome/planner/src/planner-gantt-row.c,v retrieving revision 1.30 diff -u -B -r1.30 planner-gantt-row.c --- src/planner-gantt-row.c 8 Jul 2006 16:20:36 -0000 1.30 +++ src/planner-gantt-row.c 4 Oct 2006 20:21:05 -0000 @@ -55,6 +55,8 @@ #define MILESTONE_SIZE 5 +#define FUZZ 3 + /* Minimum width for a task to keep it visible. */ #define MIN_WIDTH 2 @@ -97,10 +99,19 @@ STATE_NONE = 0, STATE_DRAG_LINK = 1 << 0, STATE_DRAG_DURATION = 1 << 1, + STATE_DRAG_COMPLETE = 1 << 2, - STATE_DRAG_ANY = STATE_DRAG_LINK | STATE_DRAG_DURATION + STATE_DRAG_ANY = STATE_DRAG_LINK | STATE_DRAG_DURATION | STATE_DRAG_COMPLETE } State; +typedef enum +{ + DRAG_NONE_SPOT, + DRAG_DURATION_SPOT, + DRAG_COMPLETE_SPOT, + DRAG_RELATION_SPOT +} DragSpot; + struct _PlannerGanttRowPriv { /* FIXME: Don't need those per gantt row. */ GdkGC *complete_gc; @@ -2303,13 +2314,48 @@ return TRUE; } -#define IN_DRAG_DURATION_SPOT(x,y,right,top,ymin,ymax) \ - ((abs (x - (right)) <= 3) && \ - (y > top + ymin) && (y < top + ymax)) - -#define IN_DRAG_RELATION_SPOT(x,y,right,top,ymin,ymax) \ - ((x <= right) && \ - (y > top + ymin) && (y < top + ymax)) +static DragSpot get_drag_spot(gdouble x, gdouble y, PlannerGanttRowPriv *priv) +{ + gdouble x2 = priv->x + priv->width; + if( (y > priv->y + priv->bar_top) && + (y < priv->y + priv->bar_bot) && + (x < x2 + FUZZ) ) { + gdouble complete_x2 = priv->x + floor(priv->width * (mrp_task_get_percent_complete (priv->task) / 100.0) + 0.5); + + /* if not way left of end of completion bar */ + if(x > complete_x2 - FUZZ) { + + /* if end of completion bar and end of task bar are not very close together */ + if(x2 - complete_x2 > 2 * FUZZ) { + + /* if not way right of the completion bar */ + if(x < complete_x2 + FUZZ) { + return DRAG_COMPLETE_SPOT; + } else { /* if way right of completion bar and ... */ + + /* if less than FUZZ from the end of the task bar */ + if(x > x2 - FUZZ) { + return DRAG_DURATION_SPOT; + } else { /* if more than FUZZ left of the end of the task bar */ + return DRAG_RELATION_SPOT; + } + } + } else { /* if DRAG_DURATION_SPOT and DRAG_COMPLETE_SPOT are connected and ... */ + + /* if closer to the end of the task bar than to the end of the completion bar */ + if(x > complete_x2 + (x2 - complete_x2) / 2) { + return DRAG_DURATION_SPOT; + } else { + return DRAG_COMPLETE_SPOT; + } + } + } else { /* if way left of end of completion bar */ + return DRAG_RELATION_SPOT; + } + } else { /* above, below or too far to the right of bar */ + return DRAG_NONE_SPOT; + } +} static gboolean gantt_row_event (GnomeCanvasItem *item, GdkEvent *event) @@ -2331,6 +2377,7 @@ gboolean summary; MrpTaskType type; gchar *message; + DragSpot drag_spot; row = PLANNER_GANTT_ROW (item); priv = row->priv; @@ -2343,9 +2390,9 @@ case GDK_BUTTON_PRESS: switch (event->button.button) { case 3: - if (IN_DRAG_RELATION_SPOT (event->button.x, event->button.y, - priv->x + priv->width, priv->y, - priv->bar_top, priv->bar_bot)) { + drag_spot = get_drag_spot(event->button.x, event->button.y, priv); + + if (drag_spot == DRAG_RELATION_SPOT) { PlannerGanttChart *chart; PlannerTaskTree *tree; GtkTreePath *path; @@ -2393,13 +2440,10 @@ break; } + drag_spot = get_drag_spot(event->button.x, event->button.y, priv); + if (type != MRP_TASK_TYPE_MILESTONE && - !summary && IN_DRAG_DURATION_SPOT (event->button.x, - event->button.y, - priv->x + priv->width, - priv->y, - priv->bar_top, - priv->bar_bot)) { + !summary && drag_spot == DRAG_DURATION_SPOT) { guint rgba; priv->state = STATE_DRAG_DURATION; @@ -2428,12 +2472,43 @@ NULL); gnome_canvas_item_hide (drag_item); } - } else if (IN_DRAG_RELATION_SPOT (event->button.x, - event->button.y, - priv->x + priv->width, - priv->y, - priv->bar_top, - priv->bar_bot)) { + } else if (type != MRP_TASK_TYPE_MILESTONE && + !summary && drag_spot == DRAG_COMPLETE_SPOT) { + guint rgba; + + priv->state = STATE_DRAG_COMPLETE; + + wx1 = priv->x; + wy1 = priv->y + priv->bar_top + 4; + wx2 = event->button.x; + wy2 = priv->y + priv->bar_bot - 4; + + gnome_canvas_item_i2w (item, &wx1, &wy1); + gnome_canvas_item_i2w (item, &wx2, &wy2); + + /* red green blue alpha */ + rgba = (0xb7 << 24) | (0xc3 << 16) | (0xc9 << 8) | (127 << 0); + + if (drag_item == NULL) { + drag_item = gnome_canvas_item_new (gnome_canvas_root (item->canvas), + EEL_TYPE_CANVAS_RECT, + "x1", wx1, + "y1", wy1, + "x2", wx2, + "y2", wy2, + "fill_color_rgba", rgba, + "outline_color_rgba", 0, + "width_pixels", 1, + NULL); + gnome_canvas_item_hide (drag_item); + } + + /* Start the autoscroll timeout. */ + priv->scroll_timeout_id = gtk_timeout_add ( + 50, + (GSourceFunc) gantt_row_scroll_timeout_cb, + row); + } else if (drag_spot == DRAG_RELATION_SPOT) { priv->state = STATE_DRAG_LINK; if (drag_points == NULL) { drag_points = gnome_canvas_points_new (2); @@ -2529,17 +2604,23 @@ } if (!(priv->state & STATE_DRAG_ANY)) { - if (type != MRP_TASK_TYPE_MILESTONE && - !summary && IN_DRAG_DURATION_SPOT (event->button.x, - event->button.y, - priv->x + priv->width, - priv->y, - priv->bar_top, - priv->bar_bot)) { - cursor = gdk_cursor_new (GDK_RIGHT_SIDE); - gdk_window_set_cursor (canvas_widget->window, cursor); - if (cursor) { - gdk_cursor_unref (cursor); + drag_spot = get_drag_spot(event->button.x, event->button.y, priv); + + if (type != MRP_TASK_TYPE_MILESTONE && !summary && + ( (drag_spot == DRAG_DURATION_SPOT) || (drag_spot == DRAG_COMPLETE_SPOT) ) ) { + if(drag_spot == DRAG_DURATION_SPOT) { + cursor = gdk_cursor_new (GDK_RIGHT_SIDE); + gdk_window_set_cursor (canvas_widget->window, cursor); + if (cursor) { + gdk_cursor_unref (cursor); + } + } + else { /* DRAG_COMPLETE_SPOT */ + cursor = gdk_cursor_new (GDK_HAND2); + gdk_window_set_cursor (canvas_widget->window, cursor); + if (cursor) { + gdk_cursor_unref (cursor); + } } } else { /* Mouse over resource names (or short_name) ? */ gint res_index; @@ -2623,6 +2704,24 @@ old_target_item = target_item; } + else if (priv->state == STATE_DRAG_COMPLETE) { + chart = g_object_get_data (G_OBJECT (item->canvas), "chart"); + + wx2 = MIN(event->motion.x, priv->x + priv->width); + wy2 = priv->y + priv->bar_bot - 4; + + gnome_canvas_item_i2w (item, &wx2, &wy2); + + gnome_canvas_item_set (drag_item, + "x2", wx2, + "y2", wy2, + NULL); + + gnome_canvas_item_raise_to_top (drag_item); + gnome_canvas_item_show (drag_item); + + planner_gantt_chart_status_updated (chart, NULL); + } else if (priv->state == STATE_DRAG_DURATION) { gint duration; gint work; @@ -2716,6 +2815,27 @@ planner_gantt_chart_status_updated (chart, NULL); } + else if (priv->state == STATE_DRAG_COMPLETE) { + GValue value = { 0 }; + gint percent_complete = floor((MIN(MAX(0, event->button.x - priv->x), priv->width) * 100.0) / priv->width + 0.5); + + chart = g_object_get_data (G_OBJECT (item->canvas), "chart"); + tree = planner_gantt_chart_get_view (chart); + + g_value_init (&value, G_TYPE_INT); + g_value_set_int (&value, percent_complete); + + task_cmd_edit_property (planner_task_tree_get_window (tree), + tree, + priv->task, + "percent_complete", + &value); + + gtk_object_destroy (GTK_OBJECT (drag_item)); + drag_item = NULL; + + planner_gantt_chart_status_updated (chart, NULL); + } else if (priv->state == STATE_DRAG_LINK) { if (old_target_item) { g_object_set (old_target_item, @@ -2833,9 +2953,8 @@ case GDK_2BUTTON_PRESS: if (event->button.button == 1) { - if (IN_DRAG_RELATION_SPOT (event->button.x, event->button.y, - priv->x + priv->width, priv->y, - priv->bar_top, priv->bar_bot)) { + drag_spot = get_drag_spot(event->button.x, event->button.y, priv); + if (drag_spot == DRAG_RELATION_SPOT) { PlannerTaskTree *tree; GtkTreePath *path; GtkTreeSelection *selection;
Attachment:
pgpQwskCTp5gB.pgp
Description: PGP signature