Page 1 of 2
#WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 8:03 am
by BarryG
Just upgraded from 6.01 to 6.02 in the Windows version, and this part of my window callback no longer generates a #WM_NOTIFY message:
Code: Select all
Procedure AppCallback(hWnd,Message,wParam,lParam)
result=#PB_ProcessPureBasicEvents
If Message=#WM_NOTIFY
Debug "This is not seen in 6.02, but is for 6.01"
ElseIf Message=#WM_HOTKEY
[...]
Obviously I can't paste the whole code, but the message above doesn't show in 6.02 but does in 6.01. How can I even start to debug this? Or is it a bug? After all, if it notifies in 6.01, then it also should in 6.02, right? No code changes.
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 8:35 am
by fryquez
There are some breaking changes with 6.02 and PanelGadgets, maybe also ContainerGadget.
WM_NOTIFY should be passed to the parent hWnd, but earlier version passed it to main window.
So it was very easy getting the messages with your SetWindowCallback.
With 6.02 you will need to subclass the actual parent of the hWnd that generates the message.
For debugging WM_Messages
SpyEx is very useful.
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 9:04 am
by BarryG
This change has affected all my CalendarGadgets, ListIconGadgets, and PanelTabs. Basically #WM_NOTIFY isn't firing for any of those now, and thus my app is totally broken. Looks like I'll just stick with 6.01 forever, as it's too much work to investigate and fix it all. Dang.
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 9:19 am
by Fred
You can check these topics:
viewtopic.php?t=81573 and
viewtopic.php?t=81568 . It's shouldn't be that difficult, you should basically change SetWindowCallback() by SetWindowLongPtr_() and it should work like before.
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 10:39 am
by mk-soft
I had once programmed something to simplify it. Would also be a features request
SetGadgetCallback
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 11:19 am
by BarryG
Basically, here's the part of my window callback that doesn't work with 6.02 at all. (Obviously I've left out some stuff for privacy). How can these be made to work with 6.02 then? All the gadgets are children of a PanelGadget. Any help appreciated, as it's way out of my league. Even just getting #WM_NOTIFY to be triggered would probably solve the problem.
Code: Select all
result=#PB_ProcessPureBasicEvents
If Message=#WM_NOTIFY ; Never triggers on 6.02, but does on 6.01. Seems to be the whole problem?
*pnmh.NMHDR=lParam
gadid=*pnmh\hwndFrom
Select *pnmh\code
Case #LVN_COLUMNCLICK ; ListIconGadget header click.
Select gadid
Case blah1 : ; Do something now that this header was clicked.
Case blah2 : *NMLV.NMLISTVIEW=lParam : If *NMLV\iSubItem=1 : globalvar=1 : EndIf
EndSelect
Case #HDN_BEGINTRACK ; Stop user resizing 0-width (hidden) ListIcon columns.
*lpnm.NMHEADER=lParam
type=*lpnm\hdr\code
If type=#HDN_BEGINTRACK Or type=#HDN_DIVIDERDBLCLICK Or type=#HDN_ITEMCHANGING
If *lpnm\hdr\hwndFrom=GetWindow_(GadgetID(#gad1),#GW_CHILD) And *lpnm\iItem=#gad1 : result=1
ElseIf *lpnm\hdr\hwndFrom=GetWindow_(GadgetID(#gad2),#GW_CHILD) And *lpnm\iItem=>#gad2 : result=1
EndIf
EndIf
Case #TCN_SELCHANGING ; Panel tab changing.
; Do something.
Case #TCN_SELCHANGE ; Panel tab finished changing.
; Finished doing something.
Case #MCN_FIRST+1 ; = #MCN_SELCHANGE (needed to get events from calendar/date gadgets after 19 Jan 2038).
; CalendarGadget do something.
Case #LVN_DELETEALLITEMS ; Fast clear ListIconGadget.
result=1
EndSelect
EndIf
ProcedureReturn result
Note: I tried putting this code into its own procedure and calling it with this:
Code: Select all
Global MyPanelCallback
Procedure PanelCallback(hWnd,Message,wParam,lParam)
[...]
ProcedureReturn CallWindowProc_(MyPanelCallback,hWnd,Message,wParam,lParam)
; But I really need "ProcedureReturn result" here, to cancel ListIcon column width changing, etc.
EndProcedure
MyPanelCallback=SetWindowLongPtr_(GadgetID(#PanelGadget),#GWLP_WNDPROC,@PanelCallback())
But it still didn't trigger the #WM_NOTIFY message. I'm lost.
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 12:13 pm
by Axolotl
Hi BarryG,
the PanelGadget() needs its own callback with 6.02.
The old behavior was more or less a lucky coincidence (as fred called it).
Look at this example...
viewtopic.php?p=600962#p600962
BTW: And yes, ContainerGadget() does not work as well, but I haven't used ContainerGadget() so I was not motivated to find a working solution...
Happy coding and stay healthy.
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 12:16 pm
by Fred
@BarryG, you need to bind it to each PanelItem, that's why it doesn't trigger. I agree than a SetGadgetCallback() could be useful, if it also bind the parent gadget to get the NOTIFY message.
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 4:32 pm
by Axolotl
Okay, because of another question about ContainerGadget() ....
I figured something out
viewtopic.php?p=601614#p601614
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 5:05 pm
by Paul
Procedure PanelCallback(hWnd,Message,wParam,lParam)
[...]
ProcedureReturn CallWindowProc_(MyPanelCallback,hWnd,Message,wParam,lParam)
; But I really need "ProcedureReturn result" here, to cancel ListIcon column width changing, etc.
EndProcedure
I have the same question, how do you deal with
ProcedureReturn result when subclassing?
How would I convert something like this?
Compile under 6.01 to see what it is suppose to look like.
Code: Select all
#Window_Main=1
#Gadget_Main_Panel=1
#Gadget_Main_Tab1=2
#Gadget_Main_List=3
Procedure WindowCallback(WindowID,Message,wParam,lParam)
Shared hRowSelected,hRow
Protected ReturnValue=#PB_ProcessPureBasicEvents
Select Message
Case #WM_NOTIFY
*LVCDHeader.NMLVCUSTOMDRAW=lParam
If *LVCDHeader\nmcd\hdr\code=#NM_CUSTOMDRAW
If *LVCDHeader\nmcd\hdr\hwndFrom=GadgetID(#Gadget_Main_List)
Select *LVCDHeader\nmcd\dwDrawStage
Case #CDDS_PREPAINT
ReturnValue=#CDRF_NOTIFYITEMDRAW
Case #CDDS_ITEMPREPAINT
hRowSelected=#False
hRow=*LVCDHeader\nmcd\dwItemSpec
If GetGadgetItemState(#Gadget_Main_List,hRow) & #PB_ListIcon_Selected
hRowSelected=#True
*LVCDHeader\clrText=$FFFFFF
Else
*LVCDHeader\clrText=$000000
EndIf
If hRowSelected
SetGadgetItemState(#Gadget_Main_List,hRow,0)
*LVCDHeader\clrTextBk=$000000
ReturnValue=#CDRF_NEWFONT|#CDRF_NOTIFYPOSTPAINT
Else
If (hRow/2)*2=hRow
*LVCDHeader\clrTextBk=$DFFFE7
Else
*LVCDHeader\clrTextBk=$F3FFF6
EndIf
ReturnValue=#CDRF_NEWFONT
EndIf
Case #CDDS_ITEMPOSTPAINT
If hRowSelected
SetGadgetItemState(#Gadget_Main_List,hRow,#PB_ListIcon_Selected)
EndIf
ReturnValue=#CDRF_DODEFAULT
EndSelect
EndIf
EndIf
EndSelect
ProcedureReturn ReturnValue
EndProcedure
If OpenWindow(#Window_Main,0,0,759,458,"Panel Test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
PanelGadget(#Gadget_Main_Panel,10,10,735,435)
AddGadgetItem(#Gadget_Main_Panel,-1,"Tab 1")
ListIconGadget(#Gadget_Main_List,10,10,710,395,"My Data",600,#PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection)
CloseGadgetList()
For tmp=1 To 50
AddGadgetItem(#Gadget_Main_List,-1,"This is a test "+Str(tmp))
Next
SetWindowCallback(@WindowCallback())
Repeat:Until WaitWindowEvent()=#PB_Event_CloseWindow
EndIf
Sure would be nice to have the option to override the fix in 6.02 and put the "bug" back to have thing function as before.

Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 6:25 pm
by mk-soft
It must be the parent of the ListIconGadget. In this case, not the PanelGadget but the Item from the PanelGadget.
Solution:
Code: Select all
;-TOP
; Comment : Module SetGadgetCallback (Windows Only)
; Author : mk-soft
; Version : v0.04
; Created : 10.06.2018
; Updated : 22.05.2023
; Link : https://www.purebasic.fr/english/viewtopic.php?f=12&t=70842
;
; Syntax Callback:
; Procedure GadgetCB(hWnd,uMsg,wParam,lParam)
; Select uMsg
; ;TODO
; EndSelect
; ; Call previous gadget procedure
; ProcedureReturn CallGadgetProc(hWnd,uMsg,wParam,lParam)
; EndProcedure
;
; *****************************************************************************
DeclareModule GadgetCallback
Declare SetGadgetCallback(Gadget, *lpNewFunc, Parent = #False)
Declare CallGadgetProc(hWnd, uMsg, wParam, lParam)
EndDeclareModule
Module GadgetCallback
EnableExplicit
; ---------------------------------------------------------------------------
Procedure SetGadgetCallback(Gadget, *lpNewFunc, Parent = #False)
Protected hWnd, *lpPrevFunc
hWnd = GadgetID(Gadget)
If Parent
hwnd = GetParent_(hwnd)
EndIf
*lpPrevFunc = GetProp_(hWnd, "PB_PrevFunc")
; Remove exists Callback
If *lpPrevFunc
SetWindowLongPtr_(hWnd, #GWL_WNDPROC, *lpPrevFunc)
RemoveProp_(hWnd, "PB_PrevFunc")
EndIf
; Set new Callback
If *lpNewFunc
*lpPrevFunc = SetWindowLongPtr_(hWnd, #GWL_WNDPROC, *lpNewFunc)
SetProp_(hWnd, "PB_PrevFunc", *lpPrevFunc)
ProcedureReturn *lpPrevFunc
EndIf
ProcedureReturn 0
EndProcedure
; ---------------------------------------------------------------------------
Procedure CallGadgetProc(hWnd, uMsg, wParam, lParam)
Protected result, *lpPrevFunc
*lpPrevFunc = GetProp_(hWnd, "PB_PrevFunc")
If *lpPrevFunc
result = CallWindowProc_(*lpPrevFunc, hWnd, uMsg, wParam, lParam)
EndIf
ProcedureReturn result
EndProcedure
EndModule
; *****************************************************************************
EnableExplicit
UseModule GadgetCallback
#Window_Main=1
#Gadget_Main_Panel=1
#Gadget_Main_Tab1=2
#Gadget_Main_List=3
Define hRowSelected,hRow
Procedure GadgetCallback(WindowID,Message,wParam,lParam)
Shared hRowSelected,hRow
Protected *LVCDHeader.NMLVCUSTOMDRAW
Select Message
Case #WM_NOTIFY
*LVCDHeader.NMLVCUSTOMDRAW=lParam
If *LVCDHeader\nmcd\hdr\code=#NM_CUSTOMDRAW
If *LVCDHeader\nmcd\hdr\hwndFrom=GadgetID(#Gadget_Main_List)
Select *LVCDHeader\nmcd\dwDrawStage
Case #CDDS_PREPAINT
ProcedureReturn #CDRF_NOTIFYITEMDRAW
Case #CDDS_ITEMPREPAINT
hRowSelected=#False
hRow=*LVCDHeader\nmcd\dwItemSpec
If GetGadgetItemState(#Gadget_Main_List,hRow) & #PB_ListIcon_Selected
hRowSelected=#True
*LVCDHeader\clrText=$FFFFFF
Else
*LVCDHeader\clrText=$000000
EndIf
If hRowSelected
SetGadgetItemState(#Gadget_Main_List,hRow,0)
*LVCDHeader\clrTextBk=$000000
ProcedureReturn #CDRF_NEWFONT|#CDRF_NOTIFYPOSTPAINT
Else
If (hRow/2)*2=hRow
*LVCDHeader\clrTextBk=$DFFFE7
Else
*LVCDHeader\clrTextBk=$F3FFF6
EndIf
ProcedureReturn #CDRF_NEWFONT
EndIf
Case #CDDS_ITEMPOSTPAINT
If hRowSelected
SetGadgetItemState(#Gadget_Main_List,hRow,#PB_ListIcon_Selected)
EndIf
ProcedureReturn #CDRF_DODEFAULT
EndSelect
EndIf
EndIf
EndSelect
ProcedureReturn CallGadgetProc(WindowID,Message,wParam,lParam)
EndProcedure
Define tmp
If OpenWindow(#Window_Main,0,0,759,458,"Panel Test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
PanelGadget(#Gadget_Main_Panel,10,10,735,435)
AddGadgetItem(#Gadget_Main_Panel,-1,"Tab 1")
ListIconGadget(#Gadget_Main_List,10,10,710,395,"My Data",600,#PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection)
CloseGadgetList()
For tmp=1 To 50
AddGadgetItem(#Gadget_Main_List,-1,"This is a test "+Str(tmp))
Next
SetGadgetCallback(#Gadget_Main_List, @GadgetCallback(), #True)
Repeat:Until WaitWindowEvent()=#PB_Event_CloseWindow
EndIf
Re: #WM_NOTIFY in callback failure
Posted: Mon May 22, 2023 6:33 pm
by Paul
Thanks mk-soft, greatly appreciated !!
Re: #WM_NOTIFY in callback failure
Posted: Tue May 23, 2023 12:04 am
by BarryG
Okay, after sleeping on it, I've come to the conclusion that the time-tested and good-old method of using one single neat window callback to catch all non-PureBasic messages is now gone with 6.02. Sadly, I don't want to have to code multiple callbacks for multiple gadgets (I have hundreds) to work around this massive code-altering change, so I've decided 6.01 is the final PureBasic version for me. Thanks for trying to help anyway, guys, but it's way too much extra work and hassle to deal with for a 60000+ line app.
Afterthought: It also means I can't contribute code here anymore in a lot of cases, as I'll be using the old code method which won't run for users who are on 6.02 and higher.
In fact, a lot of old code and historic tips on this forum are now broken because of this change. 
I ask that it goes back to the old way.
Re: #WM_NOTIFY in callback failure
Posted: Tue May 23, 2023 12:40 am
by mk-soft
It's not that extreme after all. You can also rewrite the WindowCallback on my example with SetGadgetCallback once and redirect all gadgets, even those that have the window as parent, to the same Routime.
Re: #WM_NOTIFY in callback failure
Posted: Tue May 23, 2023 1:09 am
by BarryG
It's not that simple. Your GadgetCallback() example is specific to ListIcons. I need to handle ANY gadget that needs #WM_NOTIFY triggered, but specifically as I mentioned: ListIcons, CalendarGadgets, and PanelGadgets. It's a LOT of re-work when I can just use 6.01 and forget about the hassle. Time is money.
Also, your SetGadgetCallback() is ProcedureReturning either 0 or CallWindowProc_(*lpPrevFunc, hWnd, uMsg, wParam, lParam), but my app needs to return 1 at times, so I can't use your code anyway. Thanks for posting it, though.
If anyone can show me a drop-in way to make ANY gadget get notified of #WM_NOTIFY, without having to code something for every gadget (I have hundreds), then I'm all ears. Even more so if it can just transfer that message to SetWindowCallback() instead so I can handle it there, like 6.01 does. But that still leaves the problem of lots of broken historic code on this forum now.