    Drag a document tab from one window to another
    
    This patch was written by the Tab Master, TK Soh. You can find it
    here:

    http://sourceforge.net/tracker/index.php?func=detail&aid=920929&group_id=11005&atid=311005
	    [ 920929 ] move tab by drag and drop
	    drag-move-v1_1.diff.gz 2004-03-23 21:52

    2006-07-15

    Updated to TK's newer version, on the same page:
	    drag-move-v1_2.diff 2005-11-29 05:36

diff -ur nedit_official nedit_mod
diff -ur nedit_official/source/menu.c nedit_mod/source/menu.c
--- nedit_official/source/menu.c	2006-08-15 23:16:01.000000000 +0200
+++ nedit_mod/source/menu.c	2006-10-03 02:20:31.476065600 +0200
@@ -80,6 +80,7 @@
 #include <Xm/Xm.h>
 #include <Xm/CascadeB.h>
 #include <Xm/PushB.h>
+#include <Xm/DragC.h>
 #include <Xm/ToggleB.h>
 #include <Xm/Separator.h>
 #include <Xm/RowColumn.h>
@@ -419,6 +420,8 @@
     Cardinal *nArgs);
 static void setLanguageModeAP(Widget w, XEvent *event, String *args,
     Cardinal *nArgs);
+static void updateTabDragAP(Widget w, XEvent *event, String *args,
+    Cardinal *nArgs);
 #ifdef SGI_CUSTOM
 static void shortMenusCB(Widget w, WindowInfo *window, caddr_t callData);
 static void addToToggleShortList(Widget w);
@@ -582,6 +585,7 @@
     {"set_em_tab_dist", setEmTabDistAP},
     {"set_use_tabs", setUseTabsAP},
     {"set_fonts", setFontsAP},
+    {"update_tab_drag", updateTabDragAP},
     {"set_language_mode", setLanguageModeAP}
 };
 
@@ -4279,6 +4283,31 @@
     }
 }
 
+/* 
+** clean up after a drag and drop operation.
+*/
+static void dragDropFinish(Widget w, XtPointer client_data,
+	XtPointer call_data)
+{
+    /* place holder only. Not sure if we need to do anything for now */
+}
+
+/*
+** action procedure to support drag-n-drop of tabs for moving
+** documents.
+*/
+static void updateTabDragAP(Widget w, XEvent *event, String *args,
+	Cardinal *nArgs)
+{
+    Widget dc;
+    
+    dc = XmGetDragContext(w, event->xbutton.time);
+    XtAddCallback(dc, XmNdragDropFinishCallback, dragDropFinish, NULL);
+
+    /* set the drag window pointer to the tab that is being dragged */
+    SetDraggedWindow(TabToWindow(w));
+}
+
 /*
 ** Same as AddSubMenu from libNUtil.a but 1) mnemonic is optional (NEdit
 ** users like to be able to re-arrange the mnemonics so they can set Alt
diff -ur nedit_official/source/window.c nedit_mod/source/window.c
--- nedit_official/source/window.c	2006-09-30 18:12:57.000000000 +0200
+++ nedit_mod/source/window.c	2006-10-03 02:42:59.544492800 +0200
@@ -107,6 +107,7 @@
 #include <Xm/PrimitiveP.h>
 #include <Xm/Frame.h>
 #include <Xm/CascadeB.h>
+#include <Xm/DragDrop.h>
 #ifdef EDITRES
 #include <X11/Xmu/Editres.h>
 /* extern void _XEditResCheckMessages(); */
@@ -150,6 +151,9 @@
 static void hideTooltip(Widget tab);
 static Pixmap createBitmapWithDepth(Widget w, char *data, unsigned int width,
 	unsigned int height);
+static void registerFileTabDropSite(Widget widget);
+static void addTabDragAction(Widget widget);
+static void moveTabToRightOf(WindowInfo *move, WindowInfo *rightOf);
 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
         int crossWin, int wrap);
 static Widget addTab(Widget folder, WindowInfo *window, const char *string);
@@ -199,6 +203,8 @@
 static int max(const int i1, const int i2);
 static void setGutterWidth(const WindowInfo* window, const unsigned reqCols);
 
+WindowInfo *draggedWindow, *droppedWindow;
+
 /*
 ** Create a new editor window
 */
@@ -578,6 +584,7 @@
 
     window->tabMenuPane = CreateTabContextMenu(window->tabBar, window);
     AddTabContextMenuAction(window->tabBar);
+    registerFileTabDropSite(window->tabBar);
     
     /* create an unmanaged composite widget to get the folder
        widget to hide the 3D shadow for the manager area.
@@ -781,6 +788,128 @@
 }
 
 /*
+** timer routine that takes care of moving tabs.
+*/
+static void moveTabProc(XtPointer clientData, XtIntervalId *id)
+{
+    if (droppedWindow->shell != draggedWindow->shell) {
+        WindowInfo *cloneWin = MoveDocument(droppedWindow, draggedWindow);
+        if (!GetPrefSortTabs())
+    	    moveTabToRightOf(cloneWin, droppedWindow);
+    }
+    else if (!GetPrefSortTabs()) {
+	moveTabToRightOf(draggedWindow, droppedWindow);
+    }
+}
+
+/*
+** find and return the right-most tab on the tab bar.
+*/
+WindowInfo *getRightMostTab(WindowInfo *window)
+{
+    int tabCount;
+    WidgetList tabList;
+    
+    XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList,
+       	    XmNtabCount, &tabCount, NULL);
+    
+    return tabCount? TabToWindow(tabList[tabCount-1]) : NULL;
+}
+
+void SetDraggedWindow(WindowInfo *win)
+{
+    draggedWindow = win;
+}
+
+/* 
+** initiate document movement when tabs are dragged into 
+** the text area, or another tab.
+*/ 
+static void tabDropProc(Widget widget, XtPointer client_data,
+	XtPointer call_data)
+{
+    XmDropProcCallback DropData;
+    Arg args[10];
+    int n;
+    Widget dc;
+    WindowInfo *GetDraggedWindow(void);
+
+    DropData = (XmDropProcCallback) call_data;
+    dc = DropData->dragContext;
+
+    n = 0;
+    if (!draggedWindow || (DropData->dropAction != XmDROP) || 
+        (DropData->operation != XmDROP_COPY)) {
+        XtSetArg(args[n], XmNtransferStatus, XmTRANSFER_FAILURE); n++;
+        XtSetArg(args[n], XmNnumDropTransfers, 0); n++;
+    }
+    else {
+        XtSetArg(args[n], XmNtransferStatus, XmTRANSFER_SUCCESS); n++;
+        XtSetArg(args[n], XmNnumDropTransfers, 0); n++;
+    }
+    XmDropTransferStart(dc, args, n);
+    
+    if (draggedWindow) {
+    	/* determine the target tab based on the drop-zone:
+	    1. text area or another tab: it's the one,
+	    2. tab bar gutter: the right-most tab. */
+      	if (XtIsSubclass(widget, xmPushButtonWidgetClass))
+	    droppedWindow = TabToWindow(widget);
+	else if (XtClass(widget) == xmlFolderWidgetClass)
+    	    droppedWindow = getRightMostTab(WidgetToWindow(widget));
+	else
+    	    droppedWindow = WidgetToWindow(widget);
+	    
+        if (droppedWindow != draggedWindow) {
+            /* moving document is a big operation, we delay it to avoid
+               any potential conflicts with the toolkits */
+            XtAppAddTimeOut(XtWidgetToApplicationContext(widget), 0,
+                moveTabProc, NULL);
+        }
+    }
+}
+
+/*
+** register a widget as drop site for tabs, for the purpose
+** of moving tabs within and across windows.
+*/
+static void registerFileTabDropSite(Widget widget)
+{
+#ifndef LESSTIF_VERSION
+    Arg args[5];
+    int n = 0;
+    
+    if (XtIsSubclass(widget, compositeWidgetClass))
+    	XtSetArg(args[n], XmNdropSiteType, XmDROP_SITE_COMPOSITE);
+    else
+    	XtSetArg(args[n], XmNdropSiteType, XmDROP_SITE_SIMPLE); 
+    n++;
+
+    XtSetArg (args[n], XmNnumImportTargets, 0); n++;
+    XtSetArg (args[n], XmNdropSiteOperations, XmDROP_COPY); n++;
+    XtSetArg (args[n], XmNdropProc, tabDropProc); n++;
+    XmDropSiteRegister (widget, args, n);
+#endif /* LESSTIF_VERSION */
+}
+
+/*
+** override translation for tab widget on drag [-and-drop].
+*/
+static void addTabDragAction(Widget widget)
+{
+#ifndef LESSTIF_VERSION
+    static XtTranslations table = NULL;
+
+    if (table == NULL) {
+	char *translations =
+	    "#override <Btn2Down>: ProcessDrag() update_tab_drag()";
+    	table = XtParseTranslationTable(translations);
+    }
+    XtOverrideTranslations(widget, table);
+#endif /* LESSTIF_VERSION */
+}
+
+/*
 ** ButtonPress event handler for tabs.
 */
 static void tabClickEH(Widget w, XtPointer clientData, XEvent *event)
@@ -841,6 +970,9 @@
     AddTabContextMenuAction(tab);
 #endif /* LESSTIF_VERSION */
 
+    addTabDragAction(tab);
+    registerFileTabDropSite(tab);
+    
     return tab;
 }
 	    
@@ -2288,6 +2420,8 @@
        operation and performance will be better without it) */
     TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, window->showStats);
    
+    registerFileTabDropSite(text);
+
     return text;
 }
 
@@ -3607,6 +3741,10 @@
 	    NULL);
     XmStringFree(s1);
     XmStringFree(tipString);
+
+    /* set tab as active */
+    if (IsTopDocument(win))
+        XmLFolderSetActiveTab(win->tabBar, getTabPosition(win->tab), False);
 }
 
 /*
@@ -4434,6 +4572,48 @@
 }
 
 /*
+** move a tab to one position to the right of another tab.
+*/
+static void moveTabToRightOf(WindowInfo *move, WindowInfo *rightOf)
+{
+    int tabCount, i;
+    WidgetList tabList;
+    WindowInfo *win;
+    int movePos, rightOfPos;
+    
+    if (move == rightOf || move->shell != rightOf->shell)
+    	return;
+    
+    movePos = getTabPosition(move->tab);
+    rightOfPos = getTabPosition(rightOf->tab);
+    if (rightOfPos == movePos-1)
+    	return;
+    
+    XtVaGetValues(rightOf->tabBar, XmNtabWidgetList, &tabList,
+            XmNtabCount, &tabCount, NULL);
+    if (movePos < rightOfPos) {
+    	/* clockwise rotate, rightOf-tab inclusive */
+	for (i=movePos; i<rightOfPos; i++) {
+	    win = TabToWindow(tabList[i+1]);
+	    win->tab = tabList[i];
+	    RefreshTabState(win);
+	}
+	move->tab = tabList[rightOfPos];
+	RefreshTabState(move);
+    }
+    else {
+    	/* anti-clockwise rotate */
+	for (i=movePos; i>rightOfPos+1; i--) {
+	    win = TabToWindow(tabList[i-1]);
+	    win->tab = tabList[i];
+	    RefreshTabState(win);
+	}
+	move->tab = tabList[rightOfPos+1];
+	RefreshTabState(move);
+    }
+}
+
+/*
 ** Move document to an other window.
 **
 ** the moving document will receive certain window settings from
@@ -4480,7 +4660,8 @@
     /* this should keep the new document window fresh */
     RaiseDocumentWindow(cloneWin);
     RefreshTabState(cloneWin);
-    SortTabBar(cloneWin);
+    if (GetPrefSortTabs())
+    	SortTabBar(cloneWin);
     
     return cloneWin;
 }
diff -ur nedit_official/source/window.h nedit_mod/source/window.h
--- nedit_official/source/window.h	2006-09-30 18:12:57.000000000 +0200
+++ nedit_mod/source/window.h	2006-10-03 02:20:31.526137600 +0200
@@ -109,4 +109,5 @@
         Boolean notify);
 void SetSensitive(WindowInfo *window, Widget w, Boolean sensitive);
 void CleanUpTabBarExposeQueue(WindowInfo *window);
+void SetDraggedWindow(WindowInfo *win);
 #endif /* NEDIT_WINDOW_H_INCLUDED */
