#WM_NOTIFY in callback failure

Windows specific forum
BarryG
Addict
Addict
Posts: 4160
Joined: Thu Apr 18, 2019 8:17 am

#WM_NOTIFY in callback failure

Post 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.
Last edited by BarryG on Mon May 22, 2023 8:36 am, edited 1 time in total.
fryquez
Enthusiast
Enthusiast
Posts: 391
Joined: Mon Dec 21, 2015 8:12 pm

Re: #WM_NOTIFY in callback failure

Post 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.
BarryG
Addict
Addict
Posts: 4160
Joined: Thu Apr 18, 2019 8:17 am

Re: #WM_NOTIFY in callback failure

Post 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.
Fred
Administrator
Administrator
Posts: 18167
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: #WM_NOTIFY in callback failure

Post 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.
User avatar
mk-soft
Always Here
Always Here
Posts: 6227
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: #WM_NOTIFY in callback failure

Post by mk-soft »

I had once programmed something to simplify it. Would also be a features request

SetGadgetCallback
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
BarryG
Addict
Addict
Posts: 4160
Joined: Thu Apr 18, 2019 8:17 am

Re: #WM_NOTIFY in callback failure

Post 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.
Axolotl
Addict
Addict
Posts: 819
Joined: Wed Dec 31, 2008 3:36 pm

Re: #WM_NOTIFY in callback failure

Post 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.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
Fred
Administrator
Administrator
Posts: 18167
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: #WM_NOTIFY in callback failure

Post 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.
Axolotl
Addict
Addict
Posts: 819
Joined: Wed Dec 31, 2008 3:36 pm

Re: #WM_NOTIFY in callback failure

Post by Axolotl »

Okay, because of another question about ContainerGadget() ....
I figured something out
viewtopic.php?p=601614#p601614
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
Paul
PureBasic Expert
PureBasic Expert
Posts: 1282
Joined: Fri Apr 25, 2003 4:34 pm
Location: Canada
Contact:

Re: #WM_NOTIFY in callback failure

Post 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. :twisted:
Image Image
User avatar
mk-soft
Always Here
Always Here
Posts: 6227
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: #WM_NOTIFY in callback failure

Post 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
Last edited by mk-soft on Mon May 22, 2023 6:46 pm, edited 1 time in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Paul
PureBasic Expert
PureBasic Expert
Posts: 1282
Joined: Fri Apr 25, 2003 4:34 pm
Location: Canada
Contact:

Re: #WM_NOTIFY in callback failure

Post by Paul »

Thanks mk-soft, greatly appreciated !!
Image Image
BarryG
Addict
Addict
Posts: 4160
Joined: Thu Apr 18, 2019 8:17 am

Re: #WM_NOTIFY in callback failure

Post 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.
User avatar
mk-soft
Always Here
Always Here
Posts: 6227
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: #WM_NOTIFY in callback failure

Post 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.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
BarryG
Addict
Addict
Posts: 4160
Joined: Thu Apr 18, 2019 8:17 am

Re: #WM_NOTIFY in callback failure

Post 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.
Post Reply