Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gnumeric/ChangeLog,v
retrieving revision 1.2424
diff -u -3 -p -r1.2424 ChangeLog
--- ChangeLog	23 Sep 2002 04:40:07 -0000	1.2424
+++ ChangeLog	23 Sep 2002 05:57:08 -0000
@@ -1,3 +1,10 @@
+2002-09-23  Lutz Müller <lutz users sourceforge net>
+
+	* src/workbook-control-gui.c: Implement reordering of sheets via
+	  drag & drop within workbooks.
+	* dialogs/dialog-sheet-order.c: Detect if the user changed the order
+	  of the sheets in the meanwhile.
+
 2002-09-23  Jody Goldberg <jody gnome org>
 
 	* src/sheet-view.c (sv_update) : see if there is a named expression that
Index: src/workbook-control-gui.c
===================================================================
RCS file: /cvs/gnome/gnumeric/src/workbook-control-gui.c,v
retrieving revision 1.340
diff -u -3 -p -r1.340 workbook-control-gui.c
--- src/workbook-control-gui.c	23 Sep 2002 04:40:15 -0000	1.340
+++ src/workbook-control-gui.c	23 Sep 2002 05:57:10 -0000
@@ -119,6 +119,11 @@ struct _CustomXmlUI {
 
 static GSList *registered_xml_uis = NULL;
 
+enum {
+	TARGET_URI_LIST,
+	TARGET_SHEET
+};
+
 gboolean
 wbcg_ui_update_begin (WorkbookControlGUI *wbcg)
 {
@@ -672,6 +677,185 @@ cb_sheet_label_button_press (GtkWidget *
 	return FALSE;
 }
 
+static gint
+gtk_notebook_page_num_by_label (GtkNotebook *notebook, GtkWidget *label)
+{
+        guint i;
+        GtkWidget *page, *l;
+
+        g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
+        g_return_val_if_fail (GTK_IS_WIDGET (label), -1);
+
+        for (i = 0; i < g_list_length (notebook->children); i++) {
+                page = gtk_notebook_get_nth_page (notebook, i);
+                l = gtk_notebook_get_tab_label (notebook, page);
+                if (label == l)
+                        return i;
+        }
+
+        return -1;
+}
+
+static void
+cb_sheet_label_drag_data_get (GtkWidget *widget, GdkDragContext *context,
+	GtkSelectionData *selection_data, guint info, guint time,
+	WorkbookControlGUI *wbcg)
+{
+	SheetControlGUI *scg;
+	gint n_source;
+	GtkWidget *p_source;
+
+	g_return_if_fail (IS_WORKBOOK_CONTROL_GUI (wbcg));
+
+	n_source = gtk_notebook_page_num_by_label (wbcg->notebook, widget);
+	p_source = gtk_notebook_get_nth_page (wbcg->notebook, n_source);
+	scg = g_object_get_data (G_OBJECT (p_source), SHEET_CONTROL_KEY);
+
+	gtk_selection_data_set (selection_data, selection_data->target,
+		8, (void *) scg, sizeof (scg));
+}
+
+static void
+cb_sheet_label_drag_data_received (GtkWidget *widget, GdkDragContext *context,
+	gint x, gint y, GtkSelectionData *data, guint info, guint time,
+	WorkbookControlGUI *wbcg)
+{
+	GtkWidget *w_source;
+	gint n_source, n_dest;
+	Sheet *sheet;
+	GSList *old_order= NULL, *new_order;
+	Workbook *wb;
+	guint n, i;
+
+	g_return_if_fail (IS_WORKBOOK_CONTROL_GUI (wbcg));
+
+	w_source = gtk_drag_get_source_widget (context);
+	n_source = gtk_notebook_page_num_by_label (wbcg->notebook, w_source);
+
+	/*
+	 * Is this a sheet of our workbook? If yes, we just reorder
+	 * the sheets.
+	 */
+	if (n_source >= 0) {
+
+		/* Make a list of the current order. */
+		wb = wb_control_workbook (WORKBOOK_CONTROL (wbcg));
+		n = workbook_sheet_count (wb);
+		for (i = 0; i < n; i++) {
+			sheet = workbook_sheet_by_index (wb, i);
+			old_order = g_slist_append (old_order, sheet);
+		}
+	
+		/* Make a list of the new order. */
+		new_order = g_slist_copy (old_order);
+		sheet = g_slist_nth_data (new_order, n_source);
+		new_order = g_slist_remove (new_order, sheet);
+		n_dest = gtk_notebook_page_num_by_label (wbcg->notebook,
+							 widget);
+		new_order = g_slist_insert (new_order, sheet, n_dest);
+
+		/* Reorder the sheets! */
+		cmd_reorganize_sheets (WORKBOOK_CONTROL (wbcg), old_order,
+			new_order, NULL, NULL, NULL, NULL, NULL, NULL,
+			NULL, NULL);
+	} else {
+
+		g_return_if_fail (IS_SHEET_CONTROL_GUI (data->data));
+
+		g_warning ("Not yet implemented!");
+	}
+}
+
+static const char *arrow_xpm[] = {
+	"13 14 2 1",
+	"       c None",
+	".      c #000000",
+	"     ...     ",
+	"     ...     ",
+	"     ...     ",
+	"     ...     ",
+	"     ...     ",
+	"     ...     ",
+	"     ...     ",
+	".............",
+	" ........... ",
+	"  .........  ",
+	"   .......   ",
+	"    .....    ",
+	"     ...     ",
+	"      .      "
+};
+
+static void
+cb_sheet_label_drag_begin (GtkWidget *widget, GdkDragContext *context,
+	WorkbookControlGUI *wbcg)
+{
+	GtkWidget *arrow, *image;
+	GdkPixbuf *pixbuf;
+	GdkBitmap *bitmap;
+
+	g_return_if_fail (IS_WORKBOOK_CONTROL_GUI (wbcg));
+
+	/* Create the arrow. */
+	arrow = gtk_window_new (GTK_WINDOW_POPUP);
+	gtk_widget_realize (arrow);
+	pixbuf = gdk_pixbuf_new_from_xpm_data (arrow_xpm);
+	image = gtk_image_new_from_pixbuf (pixbuf);
+	gtk_widget_show (image);
+	gtk_container_add (GTK_CONTAINER (arrow), image);
+	gdk_pixbuf_render_pixmap_and_mask (pixbuf, NULL, &bitmap, 128);
+	g_object_unref (G_OBJECT (pixbuf));
+	gtk_widget_shape_combine_mask (arrow, bitmap, 0, 0);
+	gdk_bitmap_unref (bitmap);
+	g_object_ref (G_OBJECT (arrow));
+	gtk_object_sink (GTK_OBJECT (arrow));
+	g_object_set_data (G_OBJECT (widget), "arrow", arrow);
+}
+
+static void
+cb_sheet_label_drag_end (GtkWidget *widget, GdkDragContext *context,
+			 WorkbookControlGUI *wbcg)
+{
+	GtkWidget *arrow;
+
+	g_return_if_fail (IS_WORKBOOK_CONTROL (wbcg));
+
+	/* Destroy the arrow. */
+	arrow = g_object_get_data (G_OBJECT (widget), "arrow");
+	gtk_object_destroy (GTK_OBJECT (arrow));
+	g_object_set_data (G_OBJECT (widget), "arrow", NULL);
+}
+
+static gboolean
+cb_sheet_label_drag_motion (GtkWidget *widget, GdkDragContext *context,
+	gint x, gint y, guint time, WorkbookControlGUI *wbcg)
+{
+	GtkWidget *w_source, *arrow, *window;
+	gint n_source, n_dest, root_x, root_y, pos_x, pos_y;
+
+	g_return_val_if_fail (IS_WORKBOOK_CONTROL_GUI (wbcg), FALSE);
+
+	/* Make sure we are really hovering over another label. */
+	w_source = gtk_drag_get_source_widget (context);
+	n_source = gtk_notebook_page_num_by_label (wbcg->notebook, w_source);
+	n_dest   = gtk_notebook_page_num_by_label (wbcg->notebook, widget);
+	if (n_source == n_dest)
+		return (FALSE);
+
+	/* Move the arrow to the correct position and show it. */
+	arrow = g_object_get_data (G_OBJECT (w_source), "arrow");
+	window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
+	gtk_window_get_position (GTK_WINDOW (window), &root_x, &root_y);
+	pos_x = root_x + widget->allocation.x;
+	pos_y = root_y + widget->allocation.y;
+	if (n_source < n_dest)
+		pos_x += widget->allocation.width;
+	gtk_window_move (GTK_WINDOW (arrow), pos_x, pos_y);
+	gtk_widget_show (arrow);
+
+	return (TRUE);
+}
+
 static void workbook_setup_sheets (WorkbookControlGUI *wbcg);
 static void wbcg_menu_state_sheet_count (WorkbookControl *wbc);
 
@@ -689,6 +873,9 @@ wbcg_sheet_add (WorkbookControl *wbc, Sh
 	SheetControl	*sc;
 	Sheet		*sheet;
 	GList *ptr;
+	static GtkTargetEntry const drag_types[] = {
+		{ (char *) "GNUMERIC_SHEET", 0, TARGET_SHEET }
+	};
 
 	g_return_if_fail (wbcg != NULL);
 
@@ -718,6 +905,24 @@ wbcg_sheet_add (WorkbookControl *wbc, Sh
 		"button_press_event",
 		G_CALLBACK (cb_sheet_label_button_press), scg->table);
 
+	/* Drag & Drop */
+	gtk_drag_source_set (scg->label, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+			drag_types, G_N_ELEMENTS (drag_types),
+			GDK_ACTION_MOVE);
+	gtk_drag_dest_set (scg->label, GTK_DEST_DEFAULT_ALL,
+			drag_types, G_N_ELEMENTS (drag_types),
+			GDK_ACTION_MOVE);
+	g_signal_connect (G_OBJECT (scg->label), "drag_begin",
+			G_CALLBACK (cb_sheet_label_drag_begin), wbcg);
+	g_signal_connect (G_OBJECT (scg->label), "drag_end",
+			G_CALLBACK (cb_sheet_label_drag_end), wbcg);
+	g_signal_connect (G_OBJECT (scg->label), "drag_data_get",
+			G_CALLBACK (cb_sheet_label_drag_data_get), wbcg);
+	g_signal_connect (G_OBJECT (scg->label), "drag_data_received",
+			G_CALLBACK (cb_sheet_label_drag_data_received), wbcg);
+	g_signal_connect (G_OBJECT (scg->label), "drag_motion",
+			G_CALLBACK (cb_sheet_label_drag_motion), wbcg);
+
 	gtk_widget_show (scg->label);
 	gtk_widget_show_all (GTK_WIDGET (scg->table));
 
@@ -4673,9 +4878,9 @@ workbook_control_gui_init (WorkbookContr
 			   WorkbookView *optional_view, Workbook *optional_wb)
 {
 	static GtkTargetEntry const drag_types[] = {
-		{ (char *)"text/uri-list", 0, 0 }
+		{ (char *) "text/uri-list", 0, TARGET_URI_LIST }
 	};
-
+	
 #ifdef WITH_BONOBO
 	BonoboUIContainer *ui_container;
 #endif
@@ -4831,9 +5036,8 @@ workbook_control_gui_init (WorkbookContr
 		G_CALLBACK (cb_realize), wbcg);
 	/* Setup a test of Drag and Drop */
 	gtk_drag_dest_set (GTK_WIDGET (wbcg_toplevel (wbcg)),
-			   GTK_DEST_DEFAULT_ALL,
-		drag_types, G_N_ELEMENTS (drag_types),
-			   GDK_ACTION_COPY);
+		GTK_DEST_DEFAULT_ALL, drag_types, G_N_ELEMENTS (drag_types),
+		GDK_ACTION_COPY);
 	g_signal_connect (G_OBJECT (wbcg_toplevel (wbcg)),
 		"drag_data_received",
 		G_CALLBACK (wbcg_filenames_dropped), wbcg);
Index: src/dialogs/dialog-sheet-order.c
===================================================================
RCS file: /cvs/gnome/gnumeric/src/dialogs/dialog-sheet-order.c,v
retrieving revision 1.37
diff -u -3 -p -r1.37 dialog-sheet-order.c
--- src/dialogs/dialog-sheet-order.c	18 Aug 2002 15:48:55 -0000	1.37
+++ src/dialogs/dialog-sheet-order.c	23 Sep 2002 05:57:10 -0000
@@ -527,7 +527,7 @@ cb_ok_clicked (GtkWidget *ignore, SheetM
 	Sheet *this_sheet;
 	char *old_name, *new_name;
 	GtkTreeIter this_iter;
-	gint n = 0;
+	gint i, n = 0;
 	GSList *this_new, *this_old, *list;
 	gboolean order_has_changed = FALSE;
 	gboolean is_deleted, is_locked;
@@ -606,38 +606,8 @@ cb_ok_clicked (GtkWidget *ignore, SheetM
 	if (deleted_sheets && 
 	    !gnumeric_dialog_question_yes_no (state->wbcg,
 					      _("Deletion of sheets is not undoable. "
-						"Do you want to proceed?"), FALSE)) {
-		/* clean-up */
-		g_slist_free (deleted_sheets);
-		deleted_sheets = NULL;
-		g_slist_free (new_order);
-		new_order = NULL;
-		g_slist_free (changed_names);
-		changed_names = NULL;
-		g_slist_free (old_order);
-		old_order = NULL;
-		e_free_string_slist (new_names);
-		new_names = NULL;
-
-		g_slist_free (color_changed);
-		color_changed = NULL;
-		for (list = new_colors_back; list != NULL; list = list->next)
-			if (list->data)
-				gdk_color_free ((GdkColor *)list->data);
-		g_slist_free (new_colors_back);
-		new_colors_back = NULL;
-		for (list = new_colors_fore; list != NULL; list = list->next)
-			if (list->data)
-				gdk_color_free ((GdkColor *)list->data);
-		g_slist_free (new_colors_fore);
-		new_colors_fore = NULL;
-
-		g_slist_free (protection_changed);
-		protection_changed = NULL;
-		g_slist_free (new_locks);
-		new_locks = NULL;
-		return;
-	}
+						"Do you want to proceed?"), FALSE))
+		goto clean_up;
 
 	this_new = new_order;
 	this_old = old_order;
@@ -655,6 +625,38 @@ cb_ok_clicked (GtkWidget *ignore, SheetM
 		new_order = NULL;
 		g_slist_free (old_order);
 		old_order = NULL;
+	} else {
+		Workbook *wb;
+		Sheet *sheet;
+		GSList *current_order = NULL, *this_current;
+
+		/*
+		 * The user wants to change the order. Make sure the order
+		 * of the sheets in the workbook is still the same as on
+		 * startup of this dialog.
+		 */
+		wb = wb_control_workbook (WORKBOOK_CONTROL (state->wbcg));
+		n = workbook_sheet_count (wb);
+		for (i = 0; i < n; i++) {
+			sheet = workbook_sheet_by_index (wb, i);
+			current_order = g_slist_append (current_order, sheet);
+		}
+		this_current = current_order;
+		this_old = old_order;
+		while (this_current != NULL && this_old != NULL) {
+			if (this_current->data != this_old->data)
+				break;
+			this_current = this_current->next;
+			this_old = this_old->next;
+			this_old->data = this_current->data;
+		}
+
+		if (this_current != NULL &&
+		    !gnumeric_dialog_question_yes_no (state->wbcg, 
+			    _("The order of the sheets has changed in the "
+			      "meanwhile. Do you want to override these "
+			      "changes and proceed?"), FALSE))
+			goto clean_up;
 	}
 
 	if ((new_order == NULL && changed_names == NULL && color_changed == NULL 
@@ -671,6 +673,40 @@ cb_ok_clicked (GtkWidget *ignore, SheetM
 		g_slist_free (deleted_sheets);
 		deleted_sheets = NULL;
 	}
+
+	return;
+
+clean_up:
+
+	/* clean-up */
+	g_slist_free (deleted_sheets);
+	deleted_sheets = NULL;
+	g_slist_free (new_order);
+	new_order = NULL;
+	g_slist_free (changed_names);
+	changed_names = NULL;
+	g_slist_free (old_order);
+	old_order = NULL;
+	e_free_string_slist (new_names);
+	new_names = NULL;
+
+	g_slist_free (color_changed);
+	color_changed = NULL;
+	for (list = new_colors_back; list != NULL; list = list->next)
+		if (list->data)
+			gdk_color_free ((GdkColor *)list->data);
+	g_slist_free (new_colors_back);
+	new_colors_back = NULL;
+	for (list = new_colors_fore; list != NULL; list = list->next)
+		if (list->data)
+			gdk_color_free ((GdkColor *)list->data);
+	g_slist_free (new_colors_fore);
+	new_colors_fore = NULL;
+
+	g_slist_free (protection_changed);
+	protection_changed = NULL;
+	g_slist_free (new_locks);
+	new_locks = NULL;
 }
 
 static void