Subclassing support

György 'Nog' Jeney nog at sdf.lonestar.org
Wed Oct 2 12:29:38 CDT 2002


ChangeLog:
 * include/commctrl.h
 * dlls/comctl32/commctrl.c
 * dlls/comctl32/comctl32.h
 * dlls/comctl32/comctl32.spec
   Implement {G|S}etWindowSubClass, DefSubclassProc, RemoveWindowSubclass

nog.

-------------- next part --------------
Index: include/commctrl.h
===================================================================
RCS file: /home/wine/wine/include/commctrl.h,v
retrieving revision 1.96
diff -u -r1.96 commctrl.h
--- include/commctrl.h	17 Sep 2002 01:35:09 -0000	1.96
+++ include/commctrl.h	2 Oct 2002 17:16:57 -0000
@@ -677,6 +677,12 @@
 BOOL  WINAPI InitializeFlatSB(HWND);
 HRESULT WINAPI UninitializeFlatSB(HWND);
 
+/* Subclassing stuff */
+typedef LRESULT (CALLBACK *SUBCLASSPROC)(HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR);
+BOOL WINAPI SetWindowSubclass(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
+BOOL WINAPI GetWindowSubclass(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR*);
+BOOL WINAPI RemoveWindowSubclass(HWND, SUBCLASSPROC, UINT_PTR);
+LRESULT WINAPI DefSubclassProc(HWND, UINT, WPARAM, LPARAM);
 
 /* Header control */
 
Index: dlls/comctl32/commctrl.c
===================================================================
RCS file: /home/wine/wine/dlls/comctl32/commctrl.c,v
retrieving revision 1.55
diff -u -r1.55 commctrl.c
--- dlls/comctl32/commctrl.c	17 Sep 2002 01:35:09 -0000	1.55
+++ dlls/comctl32/commctrl.c	2 Oct 2002 17:17:05 -0000
@@ -998,6 +998,259 @@
 
 
 /***********************************************************************
+ * SetWindowSubclass [COMCTL32.@]
+ *
+ * Starts a window subclass
+ *
+ * PARAMS
+ *     hWnd [in] handle to window subclass.
+ *     pfnSubclass [in] Pointer to new window procedure.
+ *     uIDSubclass [in] Unique identifier of sublass together with pfnSubclass.
+ *     dwRef [in] Reference data to pass to window procedure.
+ *
+ * RETURNS
+ *     Success: non-zero
+ *     Failure: zero
+ *
+ * BUGS
+ *     If an application manually subclasses a window after subclassing it with
+ *      this API and then with this API again, then none of the previous 
+ *      subclasses get called or the origional window procedure.
+ */
+BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
+                        UINT_PTR uIDSubclass, DWORD_PTR dwRef)
+{
+   LPSUBCLASS_INFO stack;
+   int newnum, n;
+
+   TRACE ("(%x, %p, %x, %x)\n", hWnd, pfnSubclass, uIDSubclass, dwRef);
+
+   /* Since the window procedure that we set here has two additional arguments,
+    * we can't simply set it as the new window procedure of the window. So we
+    * set our own window procedure and then calculate the other two arguments
+    * from there. */
+
+   /* See if we have been called for this window */
+   stack = (LPSUBCLASS_INFO)GetPropA (hWnd, COMCTL32_aSubclass);
+   if (!stack) {
+      /* allocate stack */
+      stack = (LPSUBCLASS_INFO)HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                         sizeof(SUBCLASS_INFO));
+      if (!stack) {
+         ERR ("Failed to allocate our Subclassing stack");
+         return FALSE;
+      }
+      SetPropA (hWnd, COMCTL32_aSubclass, (HANDLE)stack);
+
+      /* set window procedure to our own and save the current one */
+      if (IsWindowUnicode (hWnd))
+         stack->origproc = (WNDPROC)SetWindowLongW (hWnd, GWL_WNDPROC,
+                                                   (LONG)DefSubclassProc);
+      else
+         stack->origproc = (WNDPROC)SetWindowLongA (hWnd, GWL_WNDPROC,
+                                                   (LONG)DefSubclassProc);
+   }
+
+   /* Check to see if we have called this function with the same uIDSubClass
+    * and pfnSubclass */
+   for (n = 0; n <= stack->stacknum + stack->stacknew - 1; n++)
+      if ((stack->SubclassProcs[n].id == uIDSubclass) && 
+         (stack->SubclassProcs[n].subproc == pfnSubclass)) {
+         stack->SubclassProcs[n].ref = dwRef;
+         return TRUE;
+      }
+
+   if ((stack->stacknum + stack->stacknew) >= 32) {
+      ERR ("We have a Subclass stack overflow, please increment size");
+      return FALSE;
+   }
+
+   /* we can't simply increment both stackpos and stacknum because there might
+    * be a window procedure running lower in the stack, we can only get them
+    * up to date once the last window procedure has run */
+   if (stack->stacknum == stack->stackpos) {
+      stack->stacknum++;
+      stack->stackpos++;
+   } else
+      stack->stacknew++;
+
+   newnum = stack->stacknew + stack->stacknum - 1;
+
+   stack->SubclassProcs[newnum].subproc = pfnSubclass;
+   stack->SubclassProcs[newnum].ref = dwRef;
+   stack->SubclassProcs[newnum].id = uIDSubclass;
+   
+   return TRUE;
+}
+
+
+/***********************************************************************
+ * GetWindowSubclass [COMCTL32.@]
+ *
+ * Gets the Reference data from a subclass.
+ *
+ * PARAMS
+ *     hWnd [in] Handle to window which were subclassing
+ *     pfnSubclass [in] Pointer to the subclass procedure
+ *     iID [in] Unique indentifier of the subclassing procedure
+ *     pdwRef [out] Pointer to the reference data
+ *
+ * RETURNS
+ *     Success: non-sero
+ *     Failure: zero
+ */
+BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
+                              UINT_PTR uID, DWORD_PTR *pdwRef)
+{
+   LPSUBCLASS_INFO stack;
+   int n;
+
+   TRACE ("(%x, %p, %x, %p)\n", hWnd, pfnSubclass, uID, pdwRef);
+
+   /* See if we have been called for this window */
+   stack = (LPSUBCLASS_INFO)GetPropA (hWnd, COMCTL32_aSubclass);
+   if (!stack)
+      return FALSE;
+
+   for (n = 0; n <= stack->stacknum + stack->stacknew - 1; n++)
+      if ((stack->SubclassProcs[n].id == uID) &&
+         (stack->SubclassProcs[n].subproc == pfnSubclass)) {
+         *pdwRef = stack->SubclassProcs[n].ref;
+         return TRUE;
+      }
+
+   return FALSE;
+}
+
+
+/***********************************************************************
+ * RemoveWindowSubclass [COMCTL32.@]
+ *
+ * Removes a window subclass.
+ *
+ * PARAMS
+ *     hWnd [in] Handle to the window were subclassing
+ *     pfnSubclass [in] Pointer to the subclass procedure
+ *     uID [in] Unique identifier of this subclass
+ *
+ * RETURNS
+ *     Success: non-zero
+ *     Failure: zero
+ */
+BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uID)
+{
+   LPSUBCLASS_INFO stack;
+   int n;
+
+   TRACE ("(%x, %p, %x)\n", hWnd, pfnSubclass, uID);
+
+   /* Find the Subclass to remove */
+   stack = (LPSUBCLASS_INFO)GetPropA (hWnd, COMCTL32_aSubclass);
+   if (!stack)
+      return FALSE;
+
+   if ((stack->stacknum == stack->stackpos == 1) && !stack->stacknew) {
+      TRACE("Last Subclass removed, cleaning up\n");
+      /* clean up our heap and reset the origional window procedure */
+      if (IsWindowUnicode (hWnd))
+         SetWindowLongW (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
+      else
+         SetWindowLongA (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
+      HeapFree (GetProcessHeap (), 0, stack);
+      return TRUE;
+   }
+ 
+   for (n = stack->stacknum + stack->stacknew - 1; n >= 0; n--)
+      if ((stack->SubclassProcs[n].id == uID) &&
+         (stack->SubclassProcs[n].subproc == pfnSubclass)) {
+         if (n != (stack->stacknum + stack->stacknew))
+            /* Fill the hole in the stack */
+            memmove (&stack->SubclassProcs[n], &stack->SubclassProcs[n + 1],
+                    sizeof(stack->SubclassProcs[0]) * (stack->stacknew + stack->stacknum - n));
+         stack->SubclassProcs[n].subproc = NULL;
+         stack->SubclassProcs[n].ref = 0;
+         stack->SubclassProcs[n].id = 0;
+
+         /* If we are currently running a window procedure we have to manipulate
+          * the stack position pointers so that we don't corrupt the stack */
+         if ((n < stack->stackpos) || (stack->stackpos == stack->stacknum)) {
+            stack->stacknum--;
+            stack->stackpos--;
+         } else if (n >= stack->stackpos)
+            stack->stacknew--;
+         return TRUE;
+      }
+
+   return FALSE;
+}
+
+
+/***********************************************************************
+ * DefSubclassProc [COMCTL32.@]
+ *
+ * Calls the next window procedure (ie. the one before this subclass)
+ *
+ * PARAMS
+ *     hWnd [in] The window that we're subclassing
+ *     uMsg [in] Message
+ *     wParam [in] WPARAM
+ *     lParam [in] LPARAM
+ *
+ * RETURNS
+ *     Success: non-zero
+ *     Failure: zero
+ */
+LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+   LPSUBCLASS_INFO stack;
+   int stackpos;
+   LRESULT ret;
+
+   /* retrieve our little stack from the Properties */
+   stack = (LPSUBCLASS_INFO)GetPropA (hWnd, COMCTL32_aSubclass);
+   if (!stack) {
+      ERR ("Our sub classing stack got erased for %x!! Nothing we can do\n", hWnd);
+      return 0;
+   }
+
+   /* If we are at pos 0 then we have to call the origional window procedure */
+   if (stack->stackpos == 0) {
+      if (IsWindowUnicode (hWnd))
+         return CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam);
+      else
+         return CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam);
+   }
+
+   stackpos = --stack->stackpos;
+   /* call the Subclass procedure from the stack */
+   ret = stack->SubclassProcs[stackpos].subproc (hWnd, uMsg, wParam, lParam,
+         stack->SubclassProcs[stackpos].id, stack->SubclassProcs[stackpos].ref);
+   stack->stackpos++;
+
+   if ((stack->stackpos == stack->stacknum) && stack->stacknew) {
+      stack->stacknum += stack->stacknew;
+      stack->stackpos += stack->stacknew;
+      stack->stacknew = 0;
+   }
+
+   /* If we removed the last entry in our stack while a window procedure was
+    * running then we have to clean up */
+   if (stack->stackpos == stack->stacknum == 0) {
+      TRACE("Last Subclass removed, cleaning up\n");
+      /* clean up our heap and reset the origional window procedure */
+      if (IsWindowUnicode (hWnd))
+         SetWindowLongW (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
+      else
+         SetWindowLongA (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
+      HeapFree (GetProcessHeap (), 0, stack);
+      return TRUE;
+   }
+
+   return ret;
+}
+
+
+/***********************************************************************
  * COMCTL32_CreateToolTip [NOT AN API]
  *
  * Creates a tooltip for the control specified in hwnd and does all
Index: dlls/comctl32/comctl32.h
===================================================================
RCS file: /home/wine/wine/dlls/comctl32/comctl32.h,v
retrieving revision 1.18
diff -u -r1.18 comctl32.h
--- dlls/comctl32/comctl32.h	17 Sep 2002 18:30:07 -0000	1.18
+++ dlls/comctl32/comctl32.h	2 Oct 2002 17:17:06 -0000
@@ -260,4 +260,18 @@
     DEFINE_TOOLTIPSCREATED_NOTIFICATION(CTRLINFO, hwndSelf) \
     struct __forward_dummy_struc_dec_to_catch_missing_semicolon
 
+/* Our internal stack structure of the window procedures to subclass */
+typedef struct
+{
+   struct {
+      SUBCLASSPROC subproc;
+      UINT_PTR id;
+      DWORD_PTR ref;
+   } SubclassProcs[31];
+   int stackpos;
+   int stacknum;
+   int stacknew;
+   WNDPROC origproc;
+} SUBCLASS_INFO, *LPSUBCLASS_INFO;
+
 #endif  /* __WINE_COMCTL32_H */
Index: dlls/comctl32/comctl32.spec
===================================================================
RCS file: /home/wine/wine/dlls/comctl32/comctl32.spec,v
retrieving revision 1.34
diff -u -r1.34 comctl32.spec
--- dlls/comctl32/comctl32.spec	16 Aug 2002 01:43:11 -0000	1.34
+++ dlls/comctl32/comctl32.spec	2 Oct 2002 17:17:08 -0000
@@ -188,3 +188,9 @@
 @ stdcall PropertySheetW(ptr) PropertySheetW
 @ stdcall UninitializeFlatSB(long) UninitializeFlatSB
 @ stdcall _TrackMouseEvent(ptr) _TrackMouseEvent
+
+# These are only availible in comctrl 6
+@ stdcall SetWindowSubclass(long ptr ptr ptr) SetWindowSubclass
+@ stdcall GetWindowSubclass(long ptr ptr ptr) GetWindowSubclass
+@ stdcall RemoveWindowSubclass(long ptr ptr) RemoveWindowSubclass
+@ stdcall DefSubclassProc(long long ptr ptr) DefSubclassProc



More information about the wine-patches mailing list