Windows Controls and WINLIB2B events.

Discussions related to mouse, keyboard, fonts and Graphical User Interface
Posts: 78
Joined: Sat 23 Jun 2018, 15:51

Windows Controls and WINLIB2B events.

Post by Zaphod »

A recent post reminded me of some of the difficulties in using interrupts from Windows controls.
If you are using just a few buttons then there is usually no problem BUT you do need to be careful to make sure your code is capable of being reentrant, that is, that you can handle an interrupt that occurs while you are servicing a previous interrupt. Most people are either unaware of this or ignore it and hope for the best. And in most cases you will be fine. Are you feeling lucky, punk? It is all in the manual about using interrupts.

If you use some of the more modern controls like toolbars or treeview they send click information via NM_CLICK and then there is another hoop to jump through. While the original buttons and boxes used WM_COMMAND messages to issue notifications later ones use WM_NOTIFY. To get access to these you would add *SYS 1 to your program and probably use WINLIB2B if you have a dialog. This library then passes the WM_NOTIFY and some other events to the user. WINLIB2 just ignores anything that isn't a WM_COMMAND event.
Now you have to be more careful than before and it is a very good idea to start using an event queue. See the wiki for methods of implementing event queues.
I found some time ago, when using slow machines on a large dialog, that a small event queue could be made to overflow quite easily. The solution is generally to just increase the size of the queue. That is just about the end of the story unless you like to experiment. Maybe there is someone out there who still wants to know what is happening under the bonnet.

I wrote a program that was based on a Microsoft example for control use including some that were introduced with Vista and don't feature in the manual, and I set it up to write the event details to the main window so I could see what was going on. That is the program in the following post.
As it stands you will see lots of events from the controls, which will give you a good idea what information you can get that you can use to control flow of your program. But you will quickly see lots of repeats for WM_NOTIFY codes -12 and -1249. These are:
I have never found any use for these. MSDN will tell your more about them and how they might be used, which is basically if you take control of how controls are painted. That is something I have never thought of doing and is way out of my league.
And since they aren't needed in common use I decided to try to eliminate them. It immediately stopped the queue overflow and in many cases really eliminated the need for a queue at all.
How do we remove them? Well you have to delve into the WINLIB2B library assembly code and modify it!!!
But I like to tinker with libraries since BB4W is my hobby and found a place to do that. I am pretty sure that my modification will not be seen as efficient and I had not delved into assembler since the 6502 days. If there are better ways please let me know.

The program as published here has these two notification code Constants set to zero so that you can see the effect of normal WINLIB2B operation. I know it is not the latest library as it was written s few years ago.
If you set them to their correct values (Use the find function on the toolbar) you will see a dramatic reduction in the number of events and improve the chances of you collecting the one you want if you don't have an event queue.

There are other modifications to the library that have nothing to do with events and I am NOT trying to promote anything that is modified or even really want to enter into any discussion about those: it was purely an experiment. But if any user wants to experiment and feels confident then here is a hint on how to filter out these prolific NM_NOTIFY messages, that aren't very useful, in my view, before they get to your ON SYS handler.
Oh, and there is an example in here of using the keyboard accelerator on the menu rather than the INKEY and SWAP method typically employed. I am not aware of any other examples of accelerator keys elsewhere in BB4W programs so that might be of interest as well. As I say this was written several years ago so I doubt if I can remember why any particular part was coded the way it was. Code is in next post.


Posts: 78
Joined: Sat 23 Jun 2018, 15:51

Re: Windows Controls and WINLIB2B events.

Post by Zaphod »

Part 2 the code:

Code: Select all

      WM_HSCROLL = 276
      WM_LBUTTONDOWN = 513
      WM_MBUTTONDOWN = 519
      WM_RBUTTONDOWN = 516
      WM_TIMER = 275
      WM_VSCROLL = 277
      WM_MOVE = 3
      WM_SIZE = 5
      FCONTROL = 8
      FVIRTKEY = 1
      BM_SETSTATE% = &F3
      BS_AUTO3STATE = 6
      BS_MULTILINE = 8192
      BS_NOTIFY = &4000
      BS_TOP = &400
      WM_SETTEXT = &C
      CB_ADDSTRING = &143
      WS_TABSTOP = &10000
      WS_GROUP = &20000
      BST_CHECKED = &1
      BM_SETSTATE  = &F3
      BM_SETCHECK =  &F1
      BCM_SETNOTE= &1609
      BM_SETIMAGE = &00F7
      WM_NOTIFY= 78
      WM_HELP= 83
      WM_COMMAND =273
      WM_INITMENU= 278
      WM_MENUSELECT= 287
      WM_DROPFILES= 563
      WM_HOTKEY =786

      REM Set up menus
      AM$ = "AppendMenu"   :REM Just to save typing it over and over!

      REM The SYS "AppendMenu " parameters are:
      REM handle to Menu item,
      REM menu-item flags,
      REM menu-item identifier or handle of drop-down menu or submenu. This is the number returned if it's an action.
      REM menu-item content.

      REM The menu item flags can be:
      MF_ENABLED = &0
      MF_UNCHECKED = &0
      MF_GRAYED = &1         :REM Disables and grays
      MF_DISABLED = &2
      MF_CHECKED = &8
      MF_POPUP = &10
      MF_MENUBARBREAK = &20  :REM Splits items into another column with bar in between.
      MF_MENUBREAK = &40     :REM As above but no bar!
      MF_SEPARATOR = &800

      *SYS 1

      DIM Q%(99) :REM Buffer for interrupts. Could be smaller but this length is exceedingly unlikely to overflow if you follow the Help tips.

      REM INSTALL @lib$+"WINLIB2B"  Heavily modified code below.
      REM Getting away from !dlg% and using hdlg% as the dialog handle.
      REM New functions FN_showdialog() returns the dialog handle, PROC_destroydialog(hdlg%) uses the dialog handle.
      REM FN_newdialog returns the memory block containing the dialog template, and always did.
      REM Style attributes mainly stripped off and ORed. Styles can now be manipulated as in MSDN documentation.

      ON CLOSE PROCclose :QUIT
      ON SYS Q%()=Q%(0)+ (3 AND Q%(0)<DIM(Q%(),1)),@msg%,@wparam%,@lparam%,Q%(1),Q%(2),Q%(3),Q%(4),Q%(5),Q%(6),Q%(7),Q%(8),Q%(9),Q%(10),Q%(11),Q%(12),Q%(13),Q%(14),Q%(15),Q%(16),Q%(17),Q%(18),Q%(19),Q%(20),Q%(21), \
      \ Q%(22),Q%(23),Q%(24),Q%(25),Q%(26),Q%(27),Q%(28),Q%(29),Q%(30),Q%(31),Q%(32),Q%(33),Q%(34),Q%(35),Q%(36),Q%(37),Q%(38),Q%(39),Q%(40),Q%(41),Q%(42),Q%(43),Q%(44),Q%(45),Q%(46), \
      \ Q%(47),Q%(48),Q%(49),Q%(50),Q%(51),Q%(52),Q%(53),Q%(54),Q%(55),Q%(56),Q%(57),Q%(58),Q%(59),Q%(60),Q%(61),Q%(62),Q%(63),Q%(64),Q%(65),Q%(66),Q%(67),Q%(68),Q%(69),Q%(70),Q%(71), \
      \ Q%(72),Q%(73),Q%(74),Q%(75),Q%(76),Q%(77),Q%(78),Q%(79),Q%(80),Q%(81),Q%(82),Q%(83),Q%(84),Q%(85),Q%(86),Q%(87),Q%(88),Q%(89),Q%(90),Q%(91),Q%(92),Q%(93),Q%(94),Q%(95),Q%(96): RETURN
      REM We could redirect all other interrupts to this handler thus:
      REM !388=!400 :REM would direct ON TIME into the ON SYS
      !396=!400 :REM would redirect ON MOVE
      !404=!400 :REM would redirect ON MOUSE

      REM Accelerator keys. Structure elements j&, l& and n& not used: needed for data spacing ACCEL structure. 
      REM j& is padding otherwise the Words for 'key' and 'cmd' don't sit on word boundaries. The low bytes are the bytes used. The high bytes l& and n& are zero.
      DIM Acc{(NumAcckeys%-1) fVirt&, j&, key&, l&, cmd&, n&}
      Acc{(0)}.key&=14  : Acc{(0)}.cmd&=11
      Acc{(1)}.key&=12  : Acc{(1)}.cmd&=12
      Acc{(2)}.key&=19  : Acc{(2)}.cmd&=13
      Acc{(3)}.key&=16  : Acc{(3)}.cmd&=17
      Acc{(4)}.fVirt&=FCONTROL OR FVIRTKEY : Acc{(4)}.key&=&56 : Acc{(4)}.cmd&=24
      Acc{(5)}.fVirt&=FVIRTKEY : Acc{(5)}.key&=&70 : Acc{(5)}.cmd&=51
      Acc{(6)}.key&=6   : Acc{(6)}.cmd&=27
      Acc{(7)}.key&=18  : Acc{(7)}.cmd&=28
      Acc{(8)}.fVirt&=FVIRTKEY : Acc{(8)}.key&=&72 : Acc{(8)}.cmd&=29

      SYS"CreateAcceleratorTable", Acc{(0)}, NumAcckeys% TO R%

      REM The @wparam% value of the WM_NOTIFY message contains the control's ID in the LS 16-bits (LOWORD)
      REM and the notification code in the MS 16-bits (HIWORD).
      REM This makes it possible to determine the notification code even when the parameter block pointed
      REM to by @lparam% is no longer valid.

      dlgt%=FN_newdialogaccel("Dialogue box 1",96,32,282,258,8,1200,@haccel%)
      PROC_pushbutton(dlgt%,"Default push button",1,168,200,82,24,BS_DEFPUSHBUTTON OR WS_TABSTOP)
      PROC_pushbutton(dlgt%,"Split Button",2,168,160,84,24, BS_SPLITBUTTON OR WS_TABSTOP)
      PROC_pushbutton(dlgt%,"Push button",100,168,120,84,24,WS_TABSTOP)
      PROC_checkbox(dlgt%,"Simple check box",101,24,16,88,20,WS_TABSTOP)
      PROC_pushbutton(dlgt%,"Three-State check box",102,24,40,68,18,BS_AUTO3STATE OR BS_TOP OR BS_MULTILINE OR WS_TABSTOP)
      PROC_groupbox(dlgt%,"Group Box",103,24,72,230,38,0)
      PROC_radiobutton(dlgt%,"Radio button 1",104,56,88,64,16,BS_NOTIFY OR WS_TABSTOP)
      PROC_radiobutton(dlgt%,"Radio button 2",105,144,88,64,16,0) :REM no TABSTOP.
      PROC_pushbutton(dlgt%,"Push button  Pushed",106,40,128,68,44, BS_MULTILINE OR WS_TABSTOP OR WS_GROUP)
      PROC_pushbutton(dlgt%,"Command Link",107,24,192,100,36,&E OR WS_TABSTOP)
      PROC_combobox(dlgt%,"Combo", 108, 150, 20, 60, 50, CBS_DROPDOWNLIST OR WS_TABSTOP)

      dlgt2%=FN_newdialog("Dialogue box 2",208,48,420,342,8,1000)

      REM Alternative way of defining controls that is not so easy to read.
      PROC_dlgitem(dlgt2%,"Static text",100,56,31,64,16,&50000000,&82)
      PROC_dlgitem(dlgt2%,"Pushbutton 1",101,63,56,64,16,&50030000 OR BS_NOTIFY,&80)  :REM Start of group :Tabstop
      PROC_dlgitem(dlgt2%,"Pushbutton 2",102,64,80,64,16,&50000000,&80)
      PROC_dlgitem(dlgt2%,"Pushbutton 3",103,64,104,64,16,&50000000,&80)
      PROC_dlgitem(dlgt2%,"Pushbutton 4",104,64,128,64,16,&50000000,&80)
      PROC_dlgitem(dlgt2%,"Group Box",110,61,182,112,118,&50020007,&80)    :REM End of second
      PROC_dlgitem(dlgt2%,"Checkbox 1",111,88,200,64,16,&50030003,&80)     :REM Start of third Group
      PROC_dlgitem(dlgt2%,"Checkbox 2",112,88,224,64,16,&50000003,&80)
      PROC_dlgitem(dlgt2%,"Checkbox 3",113,88,248,64,16,&50000003,&80)
      PROC_dlgitem(dlgt2%,"Checkbox 4",114,88,272,64,16,&50000003,&80)
      PROC_dlgitem(dlgt2%,"Group Box",105,228,41,114,106,&50020007,&80)   :REM end of first.
      PROC_dlgitem(dlgt2%,"Radiobutton 1",106,256,56,64,16,&50030009,&80)  :REM Start of second group
      PROC_dlgitem(dlgt2%,"Radiobutton 2",107,256,72,64,16,&50000009,&80)
      PROC_dlgitem(dlgt2%,"Radiobutton 3",108,256,88,64,16,&50000009,&80)
      PROC_dlgitem(dlgt2%,"Radiobutton 4",109,256,104,64,16,&50000009,&80)
      PROC_dlgitem(dlgt2%,"Edit Box",115,224,176,124,74,&50A10000,&81)
      PROC_dlgitem(dlgt2%,"OK",1,216,280,56,14,&50030001,&80)             :REM 5th group
      PROC_dlgitem(dlgt2%,"Cancel",2,296,280,56,14,&50010000,&80)          :REM Tabstop

      @hwacc%=hdlg%  :REM Window handling accelerator keys


      REM Some initialization of controls
      SYS "SendDlgItemMessage", hdlg%, 106, BM_SETSTATE%, 1,0 :REM Set pushed button state.
      SYS "SendDlgItemMessage", hdlg%, 101, BM_SETCHECK, BST_CHECKED,0
      SYS "SendDlgItemMessage", hdlg%, 102, BM_SETCHECK, BST_INDETERMINATE,0
      SYS "SendDlgItemMessage", hdlg%, 104, BM_SETCHECK, BST_CHECKED,0
      SYS "SendDlgItemMessage", hdlg%, 108, CB_ADDSTRING, 0, "String1"
      SYS "SendDlgItemMessage", hdlg%, 108, CB_ADDSTRING, 0, "String2"
      SYS "SendDlgItemMessage", hdlg%, 108, CB_ADDSTRING, 0, "String3"

      REM SYS "SendDlgItemMessage", hdlg2%, 111, BM_SETCHECK, BST_CHECKED,0
      REM SYS "SendDlgItemMessage", hdlg2%, 106, BM_SETCHECK, BST_CHECKED,0

      REM Command link takes Unicode, so we have to convert.
      mytext$="My text"
      DIM Text% 513   :REM Define buffer for unicode string
      SYS "MultiByteToWideChar", 0, 0, mytext$, LEN(mytext$), Text%, 256 TO Len%
      SYS "SendDlgItemMessage", hdlg%, 107, BCM_SETNOTE, 0, Text%

      REM The following groups of flags cannot be used together:

      REM Menu cut and pasted from Sudoku prog on web site!
      REM This is a sub-menu to a dropdown menu item.
      SYS "CreatePopupMenu" TO hsub1%
      SYS AM$, hsub1%, 0, 145, "1"+CHR$9+"F1"  :REM Default is Unchecked and Enabled.
      SYS AM$, hsub1%, 0, 146, "2"+CHR$9+"F2"
      SYS AM$, hsub1%, 0, 147, "3"+CHR$9+"F3"
      SYS AM$, hsub1%, 0, 148, "4"+CHR$9+"F4"
      SYS AM$, hsub1%, 0, 149, "5"+CHR$9+"F5"
      SYS AM$, hsub1%, 0, 150, "6"+CHR$9+"F6"
      SYS AM$, hsub1%, 0, 151, "7"+CHR$9+"F7"
      SYS AM$, hsub1%, 0, 152, "8"+CHR$9+"F8"
      SYS AM$, hsub1%, 0, 153, "9"+CHR$9+"F9"
      SYS AM$, hsub1%, 0, 154, "Clear Filters"

      REM This is a sub-menu to a dropdown menu item
      SYS "CreatePopupMenu" TO hsub2%
      SYS AM$, hsub2%, 0, 161, "1"+CHR$32+"Shift+F1"  :REM CHR$32 is just a space!
      SYS AM$, hsub2%, 0, 162, "2"+CHR$32+"Shift+F2"
      SYS AM$, hsub2%, 0, 163, "3"+CHR$32+"Shift+F3"
      SYS AM$, hsub2%, 0, 164, "4"+CHR$32+"Shift+F4"
      SYS AM$, hsub2%, 0, 165, "5"+CHR$32+"Shift+F5"
      SYS AM$, hsub2%, MF_MENUBARBREAK, 166, "6"+CHR$32+"Shift+F6"   :REM Multi column.
      SYS AM$, hsub2%, 0, 167, "7"+CHR$32+"Shift+F7"
      SYS AM$, hsub2%, 0, 168, "8"+CHR$32+"Shift+F8"
      SYS AM$, hsub2%, 0, 169, "9"+CHR$32+"Shift+F9"
      SYS AM$, hsub2%, 0, 154, "Clear Filters"

      REM This is a dropdown. The first. (Microsoft speak for dropdown is Popup!)
      SYS "CreatePopupMenu" TO hpop1%
      SYS AM$, hpop1%, 0, 14, "&New"+CHR$9+"Ctrl+N"
      SYS AM$, hpop1%, 0, 15, "&Open"+CHR$9+"Ctrl+O"
      SYS AM$, hpop1%, 0, 19, "&Save"+CHR$9+"Ctrl+S"
      SYS AM$, hpop1%, 0, 6, "Save &As"
      SYS AM$, hpop1%, MF_SEPARATOR, 0, 0                :REM This is a divider line
      SYS AM$, hpop1%, 0, 16, "&Print"+CHR$9+"Ctrl+P "
      SYS AM$, hpop1%, MF_SEPARATOR, 0, 0                :REM This is a divider line
      SYS AM$, hpop1%, 0, 12, "E&xit"

      REM This is a dropdown. The second.
      SYS "CreatePopupMenu" TO hpop2%
      SYS AM$, hpop2%, 0, 26, "&Undo"+CHR$9+"Ctrl+Z"
      SYS AM$, hpop2%, 0, 25, "&Redo"+CHR$9+"Ctrl+Y"
      SYS AM$, hpop2%, MF_SEPARATOR, 0, 0
      SYS AM$, hpop2%, 0, 24, "Cu&t All"+CHR$9+"Ctrl+X"
      SYS AM$, hpop2%, 0, 3, "&Copy All"+CHR$9+"Ctrl+C"
      SYS AM$, hpop2%, 0, 22, "&Paste Cells"+CHR$9+"Ctrl+V"
      SYS AM$, hpop2%, 0, 14, "Clear &all"

      REM This is a dropdown.
      SYS "CreatePopupMenu" TO hpop3%
      SYS AM$, hpop3%, 0, 18, "&Restore"+CHR$9+"Ctrl+R"
      SYS AM$, hpop3%, 0, 29, "Restore previous"
      SYS AM$, hpop3%, 0, 30, "Reset Timer"
      SYS AM$, hpop3%, MF_SEPARATOR, 0,0
      SYS AM$, hpop3%, MF_GRAYED, 8, "&Tidy Grid"+CHR$9+"Bkspc"
      SYS AM$, hpop3%, MF_POPUP, hsub2%, "&Highlight"      :REM Sub-menu 2 attached here.
      SYS AM$, hpop3%, MF_POPUP, hsub1%, "&Filter"         :REM Sub-menu 1 attached here.
      SYS AM$, hpop3%, 0, 4, "Sna&pshot"
      SYS AM$, hpop3%, MF_DISABLED, 33, "Generate New Symmetrical"  :REM Looks OK but is non-functional.
      SYS AM$, hpop3%, MF_SEPARATOR, 0,0
      SYS AM$, hpop3%, 0, 34, "Generate New External"

      REM This is a dropdown.
      SYS "CreatePopupMenu" TO hpop4%
      SYS AM$, hpop4%, 0, 7, "&Grid"+CHR$9+"Ctrl+G"
      SYS AM$, hpop4%, MF_CHECKED, 31, "&Overlay Grid"
      SYS AM$, hpop4%, MF_GRAYED, 2, "&Reveal"           :REM Grayed out.
      SYS AM$, hpop4%, 0, 9, "&Auto-Check"+CHR$9+"Tab"
      SYS AM$, hpop4%, MF_SEPARATOR, 0,0
      SYS AM$, hpop4%, 0, 28, "Preferences"

      REM This is a dropdown.
      SYS "CreatePopupMenu" TO hpop5%
      SYS AM$, hpop5%, 0, 17, "&Help"
      SYS AM$, hpop5%, MF_SEPARATOR, 0, 0
      SYS AM$, hpop5%, 0, 1, "&Solve"
      SYS AM$, hpop5%, 0, 21, "Show &Cell"
      SYS AM$, hpop5%, MF_SEPARATOR, 0, 0
      SYS AM$, hpop5%, 0, 5, "&About"

      REM The top level Menu items. Here we attach the dropdowns to a top level
      REM And build the top level. This is all in memory so far.
      SYS "CreateMenu" TO H%
      SYS AM$, H%, MF_POPUP, hpop1%, "&File      "
      SYS AM$, H%, MF_POPUP, hpop2%, "&Edit      "
      SYS AM$, H%, MF_POPUP, hpop4%, "&Options   "
      SYS AM$, H%, MF_POPUP, hpop3%, "&Tools     "
      SYS AM$, H%, MF_POPUP, hpop5%, "&Help     "
      REM This is the main level. It has no dropdown.
      SYS AM$, H%, 0, 20, "&Website"

      SYS "SetMenu",hdlg%,H%   :REM Assign the Menu we just built to our window.
      SYS "DrawMenuBar",hdlg%  :REM Draw it in our window.

      REM To change the menu state use the following.
      SYS "EnableMenuItem",hpop3%,33, MF_ENABLED :REM  MF_GRAYED  or MF_DISABLED as appropriate.
      SYS "CheckMenuItem", hpop4%, 9, MF_CHECKED :REM or MF_UNCHECKED

      REM =====================================PROGRAM LOOP========================================================

        REM Update any status indications here perhaps
        WAIT 0
        IF K%> 0 IF K% < 27 PROCmenu(K%+1000) : REM CTRL+Key. Add 1000 so we tag that it came from the main window.
        REM CTRL+key combinations don't get passed to Dialog boxes so we have to use accelerators to use Ctrl+key with them.
        REM within polling loop: all action stems from the event processing.

      REM =====================================DEFINITIONS==========================================================
      DEF PROC_eventpoll
      LOCAL G%, E%()
      DIM E%(2)
      WHILE Q%(0)
        REM Note we don't have to check that the subscript goes out of range as it can't with this version of ON SYS.
        Q%(0) -= 3

      DEF PROC_event(a%())
      LOCAL a$
      CASE a%(0) OF
        WHEN WM_NOTIFY: PROCnotify(a%(1)>>16, a%(1) AND &FFFF)   :REM Notification code in HIWORD of @wparam%. V5.92a onwards.
        WHEN WM_COMMAND: PROCcommand(a%(1)>>16, a%(1) AND &FFFF, a%(2))
          REM Size or move
        WHEN WM_SIZE: a$="WM_SIZE"
        WHEN WM_MOVE: a$="WM_MOVE"
          REM These interupts not very commonly used:
        WHEN WM_HELP: a$="WM_HELP"
        WHEN WM_INITMENU: a$="WM_INITMENU" :REM The WM_INITMENU message is sent when a menu is about to become active. It occurs when the user clicks an item on the menu bar or presses a menu key. Allows the application to modify the menu before it is displayed.
        WHEN WM_INITMENUPOPUP: a$="WM_INITMENUPOPUP" :REM The WM_INITMENUPOPUP message is sent when a drop-down menu or submenu is about to become active. This allows an application to modify the menu before it is displayed, without changing the entire menu.
        WHEN WM_MENUSELECT: a$="WM_MENUSELECT" :REM The WM_MENUSELECT message is sent to a menu's owner window when the user selects a menu item.
          REM If you link in ON MOUSE then you would put those interrupts into this case statement.
          REM Finally
        WHEN WM_TIMER: a$="WM_TIMER"
      PRINT a$

      DEF PROCcommand(wparamhi%, wparamlo%, lparam%)
      REM Splits the WM_COMMAND messages into three groups.
        WHEN lparam% <> 0: PROCcontrol(wparamhi%, wparamlo%, lparam%): REM It's a control
        WHEN wparamhi% =1: PROCacc(wparamlo%) :                        REM It's an accelerator
          PROCmenu(wparamlo%):                                         REM it's a menu

      :REM The custom code goes into these PROCs as the program is now largely interrupt driven
      DEF PROCcontrol(code%,id%,hcontrol%)
      LOCAL H%
      REM code% is Notification code. Many older controls use this rather than WM_NOTIFY
      REM It is control specific, but may include getting focus, selecting items from a list, list dropdown etc.
      REM hcontrol% is control handle. Can be used to separate controls with same id% (bad idea!) or in different windows as below.
      REM Normal control clicks and many notifications all come here.
      REM BB4W dialogs are non-modal so several can be active. The sensible thing would be to separate the control
      REM ID's into groups but you can have common ID's and then check against which parent it is from.
      SYS"GetParent", hcontrol% TO H%
      IF H%=hdlg% THEN
        REM We have two dialogs open. It doesn't matter if they have the same ID's for controls as we can use the parent
        REM handle to distinguish between them.
        PRINT "I am dialog 1 :";
        CASE id% OF
          WHEN 1: PRINT "It's OK really"
          WHEN 2: PRINT "Closing down":PROC_destroydialog(H%)
            PRINT "Control "+STR$id% + " Code "+STR$code%
        PRINT "I am dialog 2 :";
        CASE id% OF
          WHEN 1: PRINT "It's OK really"
          WHEN 2: PRINT "Closing down":PROC_destroydialog(H%)
            PRINT "Control "+STR$id%+ " Code "+STR$code%

      DEF PROCacc(idm%)
      LOCAL H%, where$
      REM Accelerator keys. Note this requires FN_newdialogaccel and setting up of accelerator keys
      REM Needs next line to resolve.
      SYS "GetForegroundWindow" TO H%  :REM Find which window is being used. (GetFocus does not work)
      IF H%=@hwnd% where$="Main Window " ELSE where$= "Dialog"
      PRINT "Accelerator code "+STR$idm%+" to "+where$
      CASE idm% OF
          REM Acceleration actions go here

      DEF PROCmenu(idm%)
      IF idm% > 1000 THEN
        PRINT "CTRL-"+A$+" Key to Main Window"
        CASE idm% OF
            REM Main window control-key action here.
        PRINT "Menu "+STR$idm%
        CASE idm% OF
            REM Menu actions.

      DEF PROCnotify(wparamhi%,wparamlo%)
      PRINT "Notify", "code:"+STR$wparamhi%, "Control "+STR$wparamlo%

      DEF PROCclose
      hdlg%+=0: IF hdlg% PROC_destroydialog(hdlg%)
      hdlg2%+=0: IF hdlg2% PROC_destroydialog(hdlg2%)
      SYS "DestroyAcceleratorTable", @haccel%

      REM ======================================================================================================
      REM Modified from WINLIB2B.BBC Version 1.6
      REM Dialogue box functions
      REM Remove -12 and -1249 WM_NOTIFY codes
      REM Controls now cleared of TABSTOPS and additional stypes by default. User adds as needed.
      REM Styles are ORed to allow C type manipulations. AND NOT removes unwanted styles.
      REM Additional functions to return and use dialog handle not the position of the dialog template.
      REM which aligns this library to WINLIB5 style of access.
      REM Dialogue box items:
      DEF PROC_pushbutton(dlgt%,text$,id%,x%,y%,cx%,cy%,style%): style% OR= &50000000:LOCAL class%:class%=&80
      DEF PROC_checkbox(dlgt%,text$,id%,x%,y%,cx%,cy%,style%):   style% OR= &50000003:LOCAL class%:class%=&80
      DEF PROC_radiobutton(dlgt%,text$,id%,x%,y%,cx%,cy%,style%):style% OR= &50000009:LOCAL class%:class%=&80
      DEF PROC_groupbox(dlgt%,text$,id%,x%,y%,cx%,cy%,style%):   style% OR= &50000007:LOCAL class%:class%=&80
      DEF PROC_editbox(dlgt%,text$,id%,x%,y%,cx%,cy%,style%):    style% OR= &50800000:LOCAL class%:class%=&81
      DEF PROC_static(dlgt%,text$,id%,x%,y%,cx%,cy%,style%):     style% OR= &50000000:LOCAL class%:class%=&82
      DEF PROC_listbox(dlgt%,text$,id%,x%,y%,cx%,cy%,style%):    style% OR= &50A00003:LOCAL class%:class%=&83
      DEF PROC_combobox(dlgt%,text$,id%,x%,y%,cx%,cy%,style%):   style% OR= &50200000:LOCAL class%:class%=&85
      DEF PROC_dlgitem(dlgt%,text$,id%,x%,y%,cx%,cy%,style%,class%)
      LOCAL P%, L%
      P% = dlgt%!12
      WHILE (P% AND 3) P% += 1 : ENDWHILE
      IF (P%+2*LENtext$+26) > dlgt%!8 ERROR 0, "No room for dialogue template"

      P%!0 = style%
      P%!4 = 0
      P%!8 = (y% << 16) OR x%
      P%!12 = (cy% << 16) OR cx%
      P%!16 = &FFFF0000 OR id%
      P%!20 = class%

      SYS "MultiByteToWideChar", 0, 0, text$, -1, P%+22, 65536 TO L%
      P%!(2*L%+20) = 0
      dlgt%!12 = P%+2*L%+24
      dlgt%!24 += 1
      DEF FN_showdialog(dlgt%)  :REM NEW Returns the window handle
      REM Display dialogue box:
      DEF PROC_showdialog(dlgt%) : LOCAL H%, I%
      IF !dlgt% <> 0 ENDPROC
      SYS "CreateThread", 0, 1024, dlgt%!4, 0, 0, ^I% TO H%
      REPEAT SYS "Sleep", 10 : UNTIL !dlgt% <> 0
      SYS "CloseHandle", H%
      REM Remove dialogue box:  DEPRECATED
      DEF PROC_closedialog(dlgt%)
      IF !dlgt% = 0 ENDPROC
      SYS "PostMessage", !dlgt%, 18, 0, 0
      REPEAT SYS "Sleep", 10 : UNTIL !dlgt% = 0
      DEF PROC_destroydialog(RETURN hdlg%)  :REM NEW closes and removes window given its handle.
      LOCAL R%
      IF hdlg% = 0 ENDPROC
      SYS "PostMessage", hdlg%, 18, 0, 0
        SYS "Sleep", 10
        SYS "IsWindow", hdlg% TO R%
      UNTIL R% = 0
      REM Create a new dialogue box:
      DEF FN_newdialog(title$,x%,y%,cx%,cy%,font%,size%) : LOCAL accel%
      DEF FN_newdialogaccel(title$,x%,y%,cx%,cy%,font%,size%,accel%)
      REM This function accepts an additional parameter (accel%) which is the handle of a keyboard 'accelerator table'.
      REM You need to have first created the accelerator table using the SYS "CreateAcceleratorTable" API.
      LOCAL dlgtemplate%, msg%, L%, F%,G%, P%, I%, C%, E%, N%, D%, Q%, T%, M%, R%, S%
      DIM P% 240:
      DIM msg% 27, dlgtemplate% size%-1
      WHILE (dlgtemplate% AND 3) dlgtemplate% += 1 : size% -= 1 : ENDWHILE
      IF (2*LENtitle$+70) > size% ERROR 0, "No room for dialogue template"

      NM_CUSTOMDRAW =0 :REM -12      <<<================================================ Set these to the correct values to filter them.
      BCN_HOTITEMCHANGE = 0 :REM -1249
      SYS "GetWindowLong", @hwnd%, -6 TO I%
      OPT 10
      push dword [esp+16]  ;lParam
      push dword [esp+16]  ;wParam
      push eax             ;Msg
      push @hwnd%          ;hWnd
      call "SendMessage"
      mov eax,1 : ret 16
      ;This next part is modified from the standard WINLIB2B. Don't forget to increase space in DIM P% above.
      push eax                         ; save Msg (again)
      mov eax,[esp+20]                 ; Indirection from lParam to Message header.
      cmp dword [eax+8],BCN_HOTITEMCHANGE
      pop eax
      jnz C%                           ; Send the other notifications to the user.
      xor eax,eax                      ; set exit code of 0
      ret 16
      .E%                              ; Modification to standard to filter NM_CUSTOMDRAW
      push eax                         ; save message on stack
      mov eax,[esp+20]                 ; Indirection from lParam to NMHDR.
      cmp dword [eax+8], NM_CUSTOMDRAW ; eax+8 is code in NMHDR structure
      pop eax                          ; retrieve message
      jnz S%                           ; Continue for other notification BCN_HOTITEMCHANGE
      xor eax,eax                      ; set exit code of 0
      ret 16
      ;END of modified code.
      .D%                  ;Dialog message loop starts here.
      mov eax,[esp+8]
      cmp eax,272
      je N%
      cmp eax,273
      je C%
      cmp eax,277
      je C%
      cmp eax,83
      je C%
      cmp eax,276
      je C%
      cmp eax,78          ;WM_NOTIFY
      je E%
      xor eax,eax         ;Sets eax to 0
      ret 16
      xchg eax,[dlgtemplate%]
      push eax
      call "DestroyWindow"
      push 0  ; lParamInit
      push D% ; lpDialogFunc
      push @hwnd%  ; hWndParemt
      push dlgtemplate%+16 ; lpTemplate
      push I% ; hInstance
      call "CreateDialogIndirectParam"
      mov [dlgtemplate%],eax
      push 0
      push 0
      push 0
      push msg%
      call "GetMessage"
      or eax,eax
      jz Q%
      ]                                     <<<================== Cross Reverence Utility flags this as an error. Don't see why. 

      IF accel% THEN
        [ OPT 10
        push msg%
        push accel%
        push dword [dlgtemplate%]
        call "TranslateAccelerator"
        or eax,eax
        jnz M%

      [ OPT 10
      push msg%
      push dword [dlgtemplate%]
      call "IsDialogMessage"
      or eax,eax
      jnz M%
      push msg%
      call "DispatchMessage"
      jmps M%

      dlgtemplate%!4  = T%
      dlgtemplate%!8  = dlgtemplate%+size%
      dlgtemplate%!16 = &90C800C4
      dlgtemplate%!20 = 0
      dlgtemplate%!24 = 0
      dlgtemplate%!26 = x%
      dlgtemplate%!28 = y%
      dlgtemplate%!30 = cx%
      dlgtemplate%!32 = cy%
      dlgtemplate%!34 = 0
      REM MSDN recommended protection against buffer overrun
      SYS "MultiByteToWideChar", 0, 0, title$, -1, G%, 0 TO R%  :REM Determines the buffer size needed
      SYS "MultiByteToWideChar", 0, 0, title$, -1, dlgtemplate%+38, R% TO G%
      dlgtemplate%!(2*G%+38) = font%
      SYS "MultiByteToWideChar", 0, 0, "MS Shell Dlg", -1, F%, 0 TO R%
      SYS "MultiByteToWideChar", 0, 0, "MS Shell Dlg", -1, dlgtemplate%+40+(2*G%), R% TO F%
      dlgtemplate%!12 = dlgtemplate%+40+(2*G%+2*F%)
      = dlgtemplate%
      REM Dialogue box control:
      DEF PROC_dlgctrl(dlgt%,text$,id%,x%,y%,cx%,cy%,style%,class$)
      LOCAL L%,M%,T%
      T% = dlgt%!12-8
      IF (T%+2*LENclass$+2*LENtext$+6) > dlgt%!8 ERROR 0, "No room for dialogue template"
      SYS "MultiByteToWideChar", 0, 0, class$, -1, T%, 256 TO L%
      SYS "MultiByteToWideChar", 0, 0, text$, -1, T%+L%*2, 65536 TO M%
      dlgt%!12 = T%+2*L%+2*M%+2
Last edited by Zaphod on Thu 12 Jul 2018, 01:07, edited 1 time in total.

Posts: 143
Joined: Wed 04 Apr 2018, 06:36

Re: Windows Controls and WINLIB2B events.

Post by KenDown »

Is there any reason why we can't adopt your revised version and call it WINLIB2C? Then if we want the facilities you describe we can install 2C and stick with 2B if we don't.

Posts: 17
Joined: Thu 05 Apr 2018, 21:41

Re: Windows Controls and WINLIB2B events.

Post by RNBW »

You can produce your own libraries for use with BB4W, so I can't see any reason why you can't recognise it and call it WINLIB2C. However, in doing so, if you pass code including the library to someone else you must make sure that they have the revised library as well. Perhaps Richard might be prepared to include it within the official libraries.

Posts: 143
Joined: Wed 04 Apr 2018, 06:36

Re: Windows Controls and WINLIB2B events.

Post by KenDown »

Unless I am greatly mistaken, when you compile a program, installed libraries are included in the .exe file. Thus the WINLIB2C would automatically be passed on to the user.