Have you ever asked yourself why things are that complicated? If so, you might want to read further...
Being forced to dive deeper into the complicated matter of Windoze' controlled Ups and Downs, I started to insert some experimental code into my raw DatTools skeleton. After writing a tool to retrieve all received messages and put them into a dump file, I finally got a clue how these controls really work. As it turned out, there is absolutely nothing complicated about it - except the HLL hocus pocus which hides really simple stuff behind important sounding goobledygook to keep normal people away from using these controls. Actually, Windoze' UpDown-Controls are less complicated than OS/2's Spinbuttons!
Umm ... this introduction is getting too large, let us start to learn something new. There are several ways to create an UpDown-Control. You can do it the really complicated way and use WinCreateUpDownControl() to create the control, but it is a mess to retrieve the proper x, y, w and h values for this function. The least complicated way to retrieve course parameters is to query the rectangle of the associated edit control, add a few pixel to (x + w) as new x position, use the same y and h and take h plus a few pixel as w parameter. The easiest way (for really lazy sods like me...) is to add one (or several) line(s) like this
The most interesting detail is the iDelta parameter. As a matter of fact, this is the only useful thing an UpDown-Control emits. It keeps us informed about which of the buttons is held down currently. It is FFFFFFFF for DOWN and 00000001 for UP, when the UpDown starts spinning, and might be increased if one of the arrow keys is held down for a while. You can control this behaviour by setting the ACCEL structures associated with the UpDown to values suiting your needs. There are at least three ACCEL structure for each UpDown-Control. With these few parameters, it is easy to create very complex structures controlling multiple 'slave' windows with a single UpDown-Control.
Back to business. The old function awaited four parameters
Windoze UpDown-Controls
Well, microsoft better had called them DownDown-Controls, because they are a cheap imitation of OS/2's sophisticated Spinbutton Controls. When I saw microsoft's documentation the very first time, I just skipped the implementation of my advanced spinbuttons after reading two or three pages. Meanwhile, I started to recreate DatTools, because I urgently need a tool to manage my datafields. I surely can do it with a hex editor, but - it is quite time consuming to enter strings this way...Being forced to dive deeper into the complicated matter of Windoze' controlled Ups and Downs, I started to insert some experimental code into my raw DatTools skeleton. After writing a tool to retrieve all received messages and put them into a dump file, I finally got a clue how these controls really work. As it turned out, there is absolutely nothing complicated about it - except the HLL hocus pocus which hides really simple stuff behind important sounding goobledygook to keep normal people away from using these controls. Actually, Windoze' UpDown-Controls are less complicated than OS/2's Spinbuttons!
Umm ... this introduction is getting too large, let us start to learn something new. There are several ways to create an UpDown-Control. You can do it the really complicated way and use WinCreateUpDownControl() to create the control, but it is a mess to retrieve the proper x, y, w and h values for this function. The least complicated way to retrieve course parameters is to query the rectangle of the associated edit control, add a few pixel to (x + w) as new x position, use the same y and h and take h plus a few pixel as w parameter. The easiest way (for really lazy sods like me...) is to add one (or several) line(s) like this
CONTROL "", 0x138B, "msctls_updown32", 0x50010044, 85, 18, 15, 12to your whatever.dlg file. The hexadecimal 0x50010044 is a short version of
UDS_ALIGNRIGHT | UDS_HORZ | WS_CHILD | WS_VISIBLE | WS_TABSTOPThese styles are defined for UpDown-Controls:
UDS_WRAP 0x0001 UDS_SETBUDDYINT 0x0002 UDS_ALIGNRIGHT 0x0004 UDS_ALIGNLEFT 0x0008 UDS_AUTOBUDDY 0x0010 UDS_ARROWKEYS 0x0020 UDS_HORZ 0x0040 UDS_NOTHOUSANDS 0x0080 UDS_HOTTRACK 0x0100Having done that, we can leave HeLL and start with some real work. Everything begins with processing the WM_INITDIALOG message. In general, you might want to set the upper and lower limits via something like
xorl %eax, %eax xorl %r9d, %r9d movq %rdi, %rcx movl $0x138B,%edx incl %eax movl $0x046F,%r8d decl %r9d movq %rax, 0x20(%rsp) call _SnDIMSnDIM() is a wrapper for SendDlgItemMessageA(), preserving and restoring the eight registers destroyed by Win's API. RDI is my fixed storage for the dialog's HWND - I used RCX previously to pass parameters to other functions not shown here. After setting the lower and upper limits, you might want to tell the UpDown-Control where to start:
incl %r9d addl $0x02,%edx movq %r9d, 0x20(%rsp) call _SnDIMAs you can see, wrappers save reloading all destroyed registers after each API call. Keep care to pass a zero in R09 (WPARAM), as well! The hexadecimal in RDX is the resource ID of the UpDown, the hexadecimal in R08 the message sent to the UpDown-Control. These are all messages you can send to an UpDown:
UDM_SETRANGE 0x0465 UDM_GETRANGE 0x0466 UDM_SETPOS 0x0467 UDM_GETPOS 0x0468 UDM_SETBUDDY 0x0469 UDM_GETBUDDY 0x046A UDM_SETACCEL 0x046B UDM_GETACCEL 0x046C UDM_SETBASE 0x046D UDM_GETBASE 0x046E UDM_SETRANGE32 0x046F UDM_GETRANGE32 0x0470 UDM_SETPOS32 0x0471 UDM_GETPOS32 0x0472 UDM_SETUNICODEFORMAT 0x2005 UDM_GETUNICODEFORMAT 0x2006This is almost all of the complicated stuff to do. Oh, yes, not to forget - the only thing we have to evaluate after initialising the dialog is the WM_NOTIFY message. Whenever RDX holds 0x4E, we should check if the low word of R08 (WPARAM) is the ID of one of our UpDown-Controls. If so, R09 (LPARAM) holds the address of a 32 byte wide stack location where the following parameters are parked:
00 DQ hwndFrom control HWND 08 DQ idFrom ID 10 DQ code 0xFFFFFD2E (UDN_DELTAPOS) 18 SD iPos position current 1C SD iDelta newYou either can use iPos directly or add iDelta to the current value of your own parameter. Converting the new value to a hexadecimal or decimal string, selecting an entry from a string table or whatever else you want to do with the returned result is trivial and might be discussed in another post. There are a few messages UpDown-Controls may send in the code quadword:
UDN_FIRST 0xFFFFFD2F UDN_LAST 0xFFFFFD27 UDN_DELTAPOS 0xFFFFFD2ENow that you learned all important facts about UpDowns, It is time for some remarks.
Tips And Tricks
You might associate your UpDowns with the previous window (buddy window) in the dialog's window hierarchy, which probably is an edit control. If you want to implement some kind of true control over your controls, I strongly recommend not to associate your UpDowns with a buddy window. Let them work as simple controls with the ability to send WM_NOTIFY messages repeatedly as long as one of the arrow keys is pressed.The most interesting detail is the iDelta parameter. As a matter of fact, this is the only useful thing an UpDown-Control emits. It keeps us informed about which of the buttons is held down currently. It is FFFFFFFF for DOWN and 00000001 for UP, when the UpDown starts spinning, and might be increased if one of the arrow keys is held down for a while. You can control this behaviour by setting the ACCEL structures associated with the UpDown to values suiting your needs. There are at least three ACCEL structure for each UpDown-Control. With these few parameters, it is easy to create very complex structures controlling multiple 'slave' windows with a single UpDown-Control.
The Future Is Less Than A Picosecond Away
It will take more than a few picoseconds to port my advanced spinbuttons to Windoze, but, nevertheless - let me introduce ST-Open's Spinbutton Library. Like all ST-Open Libraries, spinbuttons are controlled via datafields and provide an easy to use interface for assembler programmers as well as for C-style coders. All libraries follow standard C calling conventions. The entire programming interface consists of a single function, one common structure and a few line-filling definitions to keep C(+-*#?) programmers happy. What did we do if we were not forced to scroll the text on our 250 * 15,000 pixel screen sidewards? Quite unbelievable that any line of simple code was not sufficient to fill even 30,000 pixel wide screens, isn't it? But: Such code definitely exists, see above!Back to business. The old function awaited four parameters
spin number SPN_* command numeric input (optional) address in/out (optional)where command was defined as one of these:
SPN_SET 0x08 SPN_GETCUR 0x07 SPN_GETID 0x06 SPN_GETSTRUC 0x05 SPN_QUERY 0x04 SPN_END 0x03 SPN_DN 0x02 SPN_UP 0x01 SPN_INIT 0x00As mentioned above, the UpDown delivers all necessary parameters without lengthy requests to API functions. You can pass R08 and R09 through as you got them for SPN_NOTIFY and SPN_EDITED, while you have to provide one parameter for the other commands which are reduced to
SPN_SETSTRUC 0x06 SPN_GETSTRUC 0x05 SPN_QUERY 0x04 SPN_SET 0x03 SPN_EDITED 0x02 SPN_NOTIFY 0x01 SPN_INIT 0x00where SPN_GETSTRUC and SPN_SETSTRUC only are required for HLL freaks (real programmers know how to read memory blocks without contorted manoeuvres). The positive side effects of datafield driven spinbuttons:
- Due to the advanced conceptual design, code is greatly reduced and most tasks are perfomed by the highly optimised library function.
- The spinbutton datafield is loaded automatically with the first SPN_INIT command if it is not present, yet.
- All spinbuttons automatically start with the values of the last session without any line of extra code.
- You have to set minimum and maximum values for each spinbutton only once.
- Changing parameters, including the spinbutton type, is comfortably done with DatTools' spinbutton editor.
SPN_STR 0x08 SPN_DATE 0x07 SPN_TIME 0x06 SPN_HEX64 0x05 SPN_HEX32 0x04 SPN_HEX16 0x03 SPN_HEX08 0x02 SPN_DEC64 0x01 SPN_DEC32 0x00Are there any wishes left? Oh, well, there are no floating point spinbuttons, yet. As long as there are no FP conversions in my libraries, there are no FP spinbuttons. I never will code one more wrapper to call functions of a C library. Keep dirty programming where it belongs to (e.g. microsoft...). Fullstop.
No comments:
Post a Comment