Index: trunk/src/helpers/cctl_tooltip.c
===================================================================
--- trunk/src/helpers/cctl_tooltip.c	(revision 64)
+++ trunk/src/helpers/cctl_tooltip.c	(revision 65)
@@ -96,5 +96,5 @@
 // linked list of all tools which were subclassed for tooltip
 HMTX        G_hmtxSubclassedTools = NULLHANDLE;
-PLINKLIST   G_pllSubclassedTools = NULL;      // linked list of SUBCLASSEDTOOL items
+LINKLIST    G_llSubclassedTools;        // linked list of SUBCLASSEDTOOL items
 
 /* ******************************************************************
@@ -126,4 +126,48 @@
 }
 
+/* ******************************************************************
+ *
+ *   Subclassing
+ *
+ ********************************************************************/
+
+/*
+ *@@ LockSubclassedTools:
+ *      locks the global list of subclassed tools.
+ *
+ *@@added V0.9.12 (2001-04-28) [umoeller]
+ */
+
+BOOL LockSubclassedTools(VOID)
+{
+    if (!G_hmtxSubclassedTools)
+    {
+        // first call:
+
+        // initialize the list
+        lstInit(&G_llSubclassedTools,
+                TRUE);      // auto-free
+
+        // create mutex and request it right away
+        return (!DosCreateMutexSem(NULL,
+                                   &G_hmtxSubclassedTools,
+                                   0,
+                                   TRUE));      // request!
+    }
+
+    return (!WinRequestMutexSem(G_hmtxSubclassedTools, SEM_INDEFINITE_WAIT));
+}
+
+/*
+ *@@ UnlockSubclassedTools:
+ *      unlocks the global list of subclassed tools.
+ *
+ *@@added V0.9.12 (2001-04-28) [umoeller]
+ */
+
+VOID UnlockSubclassedTools(VOID)
+{
+}
+
 /*
  *@@ SUBCLASSEDTOOL:
@@ -143,18 +187,18 @@
 
 /*
- *@@ ctl_fnwpSubclassedTool:
- *      window procedure for tools which were subclassed
- *      to support tooltips.
- *
- *@@added V0.9.0 [umoeller]
+ *@@ FindSubclassedTool:
+ *      returns the SUBCLASSEDTOOL struct from the
+ *      global list which matches hwndTool or NULL
+ *      if not found.
+ *
+ *      Preconditions: Caller must hold the subclassed
+ *      tools mutex.
+ *
+ *@@added V0.9.12 (2001-04-28) [umoeller]
  */
 
-MRESULT EXPENTRY ctl_fnwpSubclassedTool(HWND hwndTool, ULONG msg, MPARAM mp1, MPARAM mp2)
+PSUBCLASSEDTOOL FindSubclassedTool(HWND hwndTool)
 {
-    MRESULT mrc = 0;
-
-    PSUBCLASSEDTOOL pst = NULL;
-
-    PLISTNODE pNode = lstQueryFirstNode(G_pllSubclassedTools);
+    PLISTNODE pNode = lstQueryFirstNode(&G_llSubclassedTools);
     while (pNode)
     {
@@ -162,48 +206,68 @@
         if (pstThis->hwndTool == hwndTool)
         {
-            pst = pstThis;
-            break;
+            return (pstThis);
         }
         pNode = pNode->pNext;
     }
 
-    switch (msg)
+    return (NULL);
+}
+
+/*
+ *@@ ctl_fnwpSubclassedTool:
+ *      window procedure for tools which were subclassed
+ *      to support tooltips.
+ *
+ *@@added V0.9.0 [umoeller]
+ *@@changed V0.9.12 (2001-04-28) [umoeller]: added mutex protection
+ */
+
+MRESULT EXPENTRY ctl_fnwpSubclassedTool(HWND hwndTool, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+    MRESULT mrc = 0;
+
+    PFNWP           pfnwpOrig = NULL;
+
+    if (LockSubclassedTools())
     {
-        case WM_MOUSEMOVE:
-        case WM_BUTTON1DOWN:
-        case WM_BUTTON1UP:
-        case WM_BUTTON2DOWN:
-        case WM_BUTTON2UP:
-        case WM_BUTTON3DOWN:
-        case WM_BUTTON3UP:
+        PSUBCLASSEDTOOL pst = FindSubclassedTool(hwndTool);
+
+        switch (msg)
         {
-            QMSG qmsg;
-            qmsg.hwnd = hwndTool;
-            qmsg.msg = msg;
-            qmsg.mp1 = mp1;
-            qmsg.mp2 = mp2;
-            // _Pmpf((__FUNCTION__ ": sending TTM_RELAYEVENT"));
-            WinSendMsg(pst->hwndTooltip,
-                       TTM_RELAYEVENT,
-                       (MPARAM)0,
-                       (MPARAM)&qmsg);
-            mrc = (pst->pfnwpOrig)(hwndTool, msg, mp1, mp2);
-        break; }
-
-        case WM_DESTROY:
-            lstRemoveItem(G_pllSubclassedTools, pst);         // this frees the item
-            if (lstCountItems(G_pllSubclassedTools) == 0)
+            case WM_MOUSEMOVE:
+            case WM_BUTTON1DOWN:
+            case WM_BUTTON1UP:
+            case WM_BUTTON2DOWN:
+            case WM_BUTTON2UP:
+            case WM_BUTTON3DOWN:
+            case WM_BUTTON3UP:
             {
-                // last item: destroy list
-                lstFree(G_pllSubclassedTools);
-                G_pllSubclassedTools = NULL;
-                // _Pmpf((__FUNCTION__ ": removed hwnd 0x%lX", hwndTool));
-            }
-            mrc = (pst->pfnwpOrig)(hwndTool, msg, mp1, mp2);
-        break;
-
-        default:
-            mrc = (pst->pfnwpOrig)(hwndTool, msg, mp1, mp2);
+                QMSG qmsg;
+                qmsg.hwnd = hwndTool;
+                qmsg.msg = msg;
+                qmsg.mp1 = mp1;
+                qmsg.mp2 = mp2;
+                // _Pmpf((__FUNCTION__ ": sending TTM_RELAYEVENT"));
+                WinSendMsg(pst->hwndTooltip,
+                           TTM_RELAYEVENT,
+                           (MPARAM)0,
+                           (MPARAM)&qmsg);
+                pfnwpOrig = pst->pfnwpOrig;     // call default
+            break; }
+
+            case WM_DESTROY:
+                lstRemoveItem(&G_llSubclassedTools, pst);         // this frees the item
+                pfnwpOrig = pst->pfnwpOrig;     // call default
+            break;
+
+            default:
+                pfnwpOrig = pst->pfnwpOrig;     // call default
+        }
+
+        UnlockSubclassedTools();
     }
+
+    if (pfnwpOrig)
+        mrc = (pfnwpOrig)(hwndTool, msg, mp1, mp2);
 
     return (mrc);
@@ -211,37 +275,76 @@
 
 /*
- *@@ SubclassToolForToolinfo:
+ *@@ SubclassTool:
  *      this gets called from ctl_fnwpTooltip if a control
  *      is to be subclassed to support mouse messaging
  *      (TTF_SUBCLASS flag).
  *
+ *      Preconditions: Caller must hold the subclassed
+ *      tools mutex.
+ *
  *@@added V0.9.0 [umoeller]
+ *@@changed V0.9.12 (2001-04-28) [umoeller]: renamed from SubclassToolForToolInfo
  */
 
-BOOL SubclassToolForToolinfo(HWND hwndTooltip,
-                             HWND hwndTool)
+BOOL SubclassTool(HWND hwndTooltip,
+                  HWND hwndTool)
 {
     BOOL    brc = FALSE;
-    PFNWP   pfnwpOrig = WinSubclassWindow(hwndTool,
-                                          ctl_fnwpSubclassedTool);
-    if (pfnwpOrig)
+
+    // make sure the tool is not on the list yet
+    // V0.9.12 (2001-04-28) [umoeller]
+    if (!FindSubclassedTool(hwndTool))
     {
-        PSUBCLASSEDTOOL pst = (PSUBCLASSEDTOOL)malloc(sizeof(SUBCLASSEDTOOL));
-        if (pst)
+        PFNWP   pfnwpOrig = WinSubclassWindow(hwndTool,
+                                              ctl_fnwpSubclassedTool);
+        if (pfnwpOrig)
         {
-            pst->pfnwpOrig = pfnwpOrig;
-            pst->hwndTooltip = hwndTooltip;
-            pst->hwndTool = hwndTool;
-            pst->hab = WinQueryAnchorBlock(hwndTool);
-
-            if (G_pllSubclassedTools == NULL)
-                G_pllSubclassedTools = lstCreate(TRUE);   // auto-free items
-
-            lstAppendItem(G_pllSubclassedTools, pst);
-            // _Pmpf((__FUNCTION__ ": subclassed hwnd 0x%lX", hwndTool));
+            PSUBCLASSEDTOOL pst = (PSUBCLASSEDTOOL)malloc(sizeof(SUBCLASSEDTOOL));
+            if (pst)
+            {
+                pst->pfnwpOrig = pfnwpOrig;
+                pst->hwndTooltip = hwndTooltip;
+                pst->hwndTool = hwndTool;
+                pst->hab = WinQueryAnchorBlock(hwndTool);
+
+                brc = !!lstAppendItem(&G_llSubclassedTools, pst);
+            }
         }
     }
+
     return (brc);
 }
+
+/*
+ *@@ UnSubclassTool:
+ *      un-subclasses a tool previously subclassed by
+ *      SubclassToolForToolinfo.
+ *
+ *      Preconditions: Caller must hold the subclassed
+ *      tools mutex.
+ *
+ *@@added V0.9.12 (2001-04-28) [umoeller]
+ */
+
+BOOL UnSubclassTool(HWND hwndTool)
+{
+    PSUBCLASSEDTOOL pst = FindSubclassedTool(hwndTool);
+    if (pst)
+    {
+        WinSubclassWindow(hwndTool,
+                          pst->pfnwpOrig);
+                            // orig winproc == un-subclass
+        return (lstRemoveItem(&G_llSubclassedTools, pst));
+                    // this frees the item
+    }
+
+    return (FALSE);
+}
+
+/* ******************************************************************
+ *
+ *   Implementation
+ *
+ ********************************************************************/
 
 /*
@@ -654,5 +757,5 @@
  *
  *      To clarify: There is usually one tooltip control, which is hidden
- *      most of the time, for many tools (parts of a visible window).
+ *      most of the time, for many "tools" (parts of a visible window).
  *      When the user puts the cursor on a tool and leaves it there for
  *      approximately one-half second, the tooltip control is set up for
@@ -728,4 +831,5 @@
  *
  *@@added V0.9.0 [umoeller]
+ *@@changed V0.9.12 (2001-04-28) [umoeller]: various fixes WRT subclassing
  */
 
@@ -826,5 +930,26 @@
                 if (pttd->pszPaintText)
                     free(pttd->pszPaintText);
+
+                // un-subclass all tools that we subclassed
+                // V0.9.12 (2001-04-28) [umoeller]
+                if (LockSubclassedTools())
+                {
+                    PLISTNODE pNode;
+                    PSUBCLASSEDTOOL pst;
+                    for (pNode = lstQueryFirstNode(&pttd->llTools);
+                         pNode;
+                         pNode = pNode->pNext)
+                    {
+                        PTOOLINFO pti = (PTOOLINFO)pNode->pItemData;
+                        if (pst = FindSubclassedTool(pti->hwndTool))
+                            UnSubclassTool(pti->hwndTool);
+                    }
+
+                    UnlockSubclassedTools();
+                }
+                // end V0.9.12 (2001-04-28) [umoeller]
+
                 lstClear(&pttd->llTools);
+
                 free(pttd);
 
@@ -963,13 +1088,14 @@
              *      rectangular areas within a window's client area.
              *
-             *      --  When you add a tool implemented as a rectangular area, the
-             *          "hwndToolOwner" member of TOOLINFO must specify the handle
-             *          of the window that contains the area, and the "rect" member must
-             *          specify the client coordinates of the area's bounding
-             *          rectangle.
-             *
-             *      --  When you add a tool implemented as a window, the "hwndTool"
-             *          member of TOOLINFO must contain the window handle of the
-             *          tool. hwndToolOwner should be the owner of the tool.
+             *      --  When you add a tool implemented as a rectangular
+             *          area, the "hwndToolOwner" member of TOOLINFO must
+             *          specify the handle of the window that contains the
+             *          area, and the "rect" member must specify the client
+             *          coordinates of the area's bounding rectangle.
+             *
+             *      --  When you add a tool implemented as a window, the
+             *          "hwndTool" member of TOOLINFO must contain the
+             *          window handle of the tool. hwndToolOwner should be
+             *          the owner of the tool.
              *
              *      When you add a tool to a tooltip control, the "pszText"
@@ -1004,7 +1130,15 @@
                                       ptiNew);
 
-                        if (ptiPassed->ulFlags & TTF_SUBCLASS)
-                            SubclassToolForToolinfo(hwndTooltip,
-                                                    ptiPassed->hwndTool);
+                        if (    (ptiPassed->ulFlags & TTF_SUBCLASS)
+                             && (LockSubclassedTools()) // V0.9.12 (2001-04-28) [umoeller]
+                           )
+                        {
+                            // caller wants this tool to be subclassed:
+                            // well, do it then
+                            SubclassTool(hwndTooltip,
+                                         ptiPassed->hwndTool);
+
+                            UnlockSubclassedTools();
+                        }
 
                         mrc = (MPARAM)TRUE;
@@ -1040,5 +1174,18 @@
                         {
                             // found:
+
+                            // V0.9.12 (2001-04-28) [umoeller]
+                            // unsubclass if this was subclassed
+                            if (ptiThis->ulFlags & TTF_SUBCLASS)
+                            {
+                                if (LockSubclassedTools())
+                                {
+                                    UnSubclassTool(ptiSearch->hwndTool);
+                                }
+                            }
+
+                            // remove the tool from the list
                             lstRemoveNode(&pttd->llTools, pToolNode);
+
                             break;
                         }
@@ -1072,4 +1219,5 @@
 
             case TTM_NEWTOOLRECT:
+
             break;
 
@@ -1209,4 +1357,38 @@
 
             /*
+             *@@ TTM_GETDELAYTIME:
+             *      returns the current value of the specified
+             *      timeout value. See TTM_SETDELAYTIME.
+             *
+             *      Parameters:
+             *
+             *      -- USHORT mp1: timer value to query. One of:
+             *              -- TTDT_AUTOPOP
+             *              -- TTDT_INITIAL
+             *              -- TTDT_RESHOW
+             *
+             *      Returns: ULONG timeout value.
+             *
+             *@@added V0.9.12 (2001-04-28) [umoeller]
+             */
+
+            case TTM_GETDELAYTIME:
+                switch ((USHORT)mp1)
+                {
+                    case TTDT_AUTOPOP:
+                        mrc = (MRESULT)pttd->ulTimeoutAutopop;
+                    break;
+
+                    case TTDT_INITIAL:
+                        mrc = (MRESULT)pttd->ulTimeoutInitial;
+                    break;
+
+                    case TTDT_RESHOW:
+                        mrc = (MRESULT)pttd->ulTimeoutReshow;
+                    break;
+                }
+            break;
+
+            /*
              *@@ TTM_SETDELAYTIME:
              *      overrides a few default timeout values for the
@@ -1232,5 +1414,5 @@
              *      -- USHORT mp1: parameter selection. One of the following:
              *              -- TTDT_AUTOMATIC: automatically calculates the initial,
-             *                 reshow, and autopopup durations based on the value of iDelay.
+             *                 reshow, and autopopup durations based on the value of mp2.
              *              -- TTDT_AUTOPOP: sets the length of time before the tooltip
              *                  window is hidden if the cursor remains stationary
@@ -1279,8 +1461,9 @@
              *      Parameters:
              *      -- mp1: always 0
-             *      -- PTOOLINFO mp2: pointer to a TOOLINFO structure. When sending the
-             *          message, the hwnd and uId members identify a tool. If the tooltip
-             *          control includes the tool, the lpszText member receives the pointer
-             *          to the string.
+             *      -- PTOOLINFO mp2: pointer to a TOOLINFO structure.
+             *         When sending the message, the hwnd and uId members
+             *         identify a tool. If the tooltip control includes
+             *         the tool, the lpszText member receives the pointer
+             *         to the string.
              *
              *      Return value: 0 always.
@@ -1289,5 +1472,4 @@
              *      this sends the TTN_NEEDTEXT notification to TOOLINFO.hwnd.
              *
-             *@@todo add TTFMT_STRINGRES
              */
 
@@ -1322,5 +1504,5 @@
 
                         case TTFMT_STRINGRES:
-
+                                // @@todo
                         break;
                     }
@@ -1334,8 +1516,9 @@
              *      Parameters:
              *      -- mp1: always 0.
-             *      -- PTOOLINFO mp2: pointer to a TOOLINFO structure. The "hinst"
-             *          and "lpszText" members must specify the instance handle and
-             *          the pointer to the text.
-             *          The "hwnd" and "uId" members identify the tool to update.
+             *      -- PTOOLINFO mp2: pointer to a TOOLINFO structure.
+             *         The "hinst" and "lpszText" members must specify
+             *         the instance handle and the pointer to the text.
+             *         The "hwnd" and "uId" members identify the tool
+             *         to update.
              */
 
Index: trunk/src/helpers/dialog.c
===================================================================
--- trunk/src/helpers/dialog.c	(revision 64)
+++ trunk/src/helpers/dialog.c	(revision 65)
@@ -400,6 +400,6 @@
 
 VOID ProcessColumn(PCOLUMNDEF pColumnDef,
-                   PROWDEF pOwningRow,
-                   PROCESSMODE ProcessMode,
+                   PROWDEF pOwningRow,          // in: current row from ProcessRow
+                   PROCESSMODE ProcessMode,     // in: processing mode (see ProcessAll)
                    PLONG plX,                   // in/out: PROCESS_CALC_POSITIONS only
                    PDLGPRIVATE pDlgData)
@@ -673,11 +673,15 @@
 /*
  *@@ ProcessRow:
- *
+ *      level-3 procedure (called from ProcessTable),
+ *      which in turn calls ProcessColumn for each column
+ *      in the row.
+ *
+ *      See ProcessAll for the meaning of ProcessMode.
  */
 
 VOID ProcessRow(PROWDEF pRowDef,
-                PTABLEDEF pOwningTable,
-                PROCESSMODE ProcessMode,
-                PLONG plY,
+                PTABLEDEF pOwningTable,     // in: current table from ProcessTable
+                PROCESSMODE ProcessMode,    // in: processing mode (see ProcessAll)
+                PLONG plY,                  // in/out: current y position (decremented)
                 PDLGPRIVATE pDlgData)
 {
@@ -727,8 +731,14 @@
 /*
  *@@ ProcessTable:
- *
- *      This routine is a bit sick because it can be
- *      called recursively if a nested table is found
- *      in a COLUMNDEF.
+ *      level-2 procedure (called from ProcessAll),
+ *      which in turn calls ProcessRow for each row
+ *      in the table (which in turn calls ProcessColumn
+ *      for each column in the row).
+ *
+ *      See ProcessAll for the meaning of ProcessMode.
+ *
+ *      This routine is a bit sick because it can even be
+ *      called recursively from ProcessColumn (!) if a
+ *      nested table is found in a COLUMNDEF.
  *
  *      With PROCESS_CALC_POSITIONS, pptl must specify
@@ -742,5 +752,5 @@
 VOID ProcessTable(PTABLEDEF pTableDef,
                   const CONTROLPOS *pcpTable,       // in: table position with PROCESS_CALC_POSITIONS
-                  PROCESSMODE ProcessMode,
+                  PROCESSMODE ProcessMode,          // in: processing mode (see ProcessAll)
                   PDLGPRIVATE pDlgData)
 {
@@ -783,8 +793,21 @@
 /*
  *@@ ProcessAll:
+ *      level-1 procedure, which in turn calls ProcessTable
+ *      for each root-level table found (which in turn
+ *      calls ProcessRow for each row in the table, which
+ *      in turn calls ProcessColumn for each column in
+ *      the row).
+ *
+ *      The first trick to formatting is that ProcessAll will
+ *      get three times, thus going down the entire tree three
+ *      times, with ProcessMode being set to one of the
+ *      following for each call (in this order):
  *
  *      -- PROCESS_CALC_SIZES: calculates the sizes
  *         of all tables, rows, columns, and controls.
  *
+ *         After this first call, we know all the sizes
+ *         only and then then calculate the positions.
+ *
  *      -- PROCESS_CALC_POSITIONS: calculates the positions
  *         based on the sizes calculated before.
@@ -793,4 +816,10 @@
  *         positions and sizes calculated before.
  *
+ *      The second trick is the precondition that tables may
+ *      nest by allowing a table definition instead of a
+ *      control definition in a column. This way we can
+ *      recurse from columns back into tables and thus
+ *      know the size and position of a nested table column
+ *      just as if it were a regular control.
  */
 
Index: trunk/src/helpers/xml.c
===================================================================
--- trunk/src/helpers/xml.c	(revision 64)
+++ trunk/src/helpers/xml.c	(revision 65)
@@ -8,6 +8,7 @@
  *
  *      --  The bottom layer is implemented by @expat, which I have
- *          ported and hacked to the xwphelpers. See xmlparse.c for
- *          an introduction.
+ *          ported and hacked to the xwphelpers.
+ *
+ *          See xmlparse.c for an introduction.
  *
  *      --  Because expat requires so many callbacks and is non-validating,
Index: trunk/src/helpers/xprf.c
===================================================================
--- trunk/src/helpers/xprf.c	(revision 64)
+++ trunk/src/helpers/xprf.c	(revision 65)
@@ -39,5 +39,5 @@
  *         If you open the profile on one thread and write and read
  *         concurrently on two threads, there's no protection, and everything
- *         will blow up. The functions are reeantrant though, so for different
+ *         will blow up. The functions are reentrant though, so for different
  *         profiles there will be no problems.
  *
Index: trunk/src/helpers/xstring.c
===================================================================
--- trunk/src/helpers/xstring.c	(revision 64)
+++ trunk/src/helpers/xstring.c	(revision 65)
@@ -523,13 +523,6 @@
  *      If pxstr is empty, this behaves just like xstrcpy.
  *
- *      With ulSourceLength, specify the length of pcszSource.
- *      If you specify 0, this function will run strlen(pcszSource)
- *      itself.
- *
- *      If you already know the length of pcszSource, you can
- *      speed this function up a bit this way.
- *
- *      You are required to specify ulSourceLength if you only want
- *      to copy a substring, or pcszSource is not zero-terminated.
+ *      With ulSourceLength, specify the length of pcszSource
+ *      or 0 (see xstrcpy for details).
  *
  *      Returns the length of the new string (excluding the null
@@ -590,5 +583,5 @@
                 memcpy(pxstr->psz + pxstr->ulLength,
                        pcszSource,
-                       ulSourceLength);     // null terminator
+                       ulSourceLength);
 
                 *(pxstr->psz + pxstr->ulLength + ulSourceLength) = '\0';
@@ -629,5 +622,5 @@
  +          XSTRING str;
  +          xstrInit(&str, 0);
- +          xstrcpy(&str, "blu");
+ +          xstrcpy(&str, "blu", 0);
  +          xstrcatc(&str, 'p');
  *
