Patch: changing % complete by dragging in gantt



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



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