User Tools

Site Tools


colour_20text_20in_20a_20list_20view

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

colour_20text_20in_20a_20list_20view [2018/03/31 13:19]
127.0.0.1 external edit
colour_20text_20in_20a_20list_20view [2018/04/13 11:47] (current)
richardrussell
Line 1: Line 1:
 =====Colour text in a List View===== =====Colour text in a List View=====
  
-//by Michael Hutton 22/​01/​2009//​\\ \\  Getting windows to print colour text in a list view involes intercepting windows messages about the state of its current Paint cycle. When a common control (in this case the list view) is about to paint (draw) something it sends a message to its parent window informing it of what it is about to do. We have to intercept this NM_CUSTOMDRAW message and process it ourselves. Unfortunately,​ by the time the message has got through to BB4W to be handled by a ON SYS statement some of the data asscociated with the message may or may not still be present. Because of this volatility of the data asscociated with the message we have to intercept it before it gets to the ON SYS statement. At present, it is not possible to SUBCLASS the window using the SUBCLASS.BBC library and so we must process the message in our own message loop. This require some assembly language. This article describes intercepting and processing the messages.\\ \\  Here is an example program you may wish to look at:\\ http://​tech.groups.yahoo.com/​group/​bb4w/​files/​%22Temp%20Folder%22/​MDCH/​LVCOLOURTEXT.bbc\\ \\  and you may want to cross reference this article with the SDK.\\  ms-help://MS.LHSMSSDK.1033/MS.LHSWinSDK.1033/Controls/controls/custdraw/custdraw.htm\\ \\  ​First, before we assemble the code we will need to define some variables/​constants.\\ +//by Michael Hutton 22/​01/​2009//​\\ \\  Getting windows to print colour text in a list view involes intercepting windows messages about the state of its current Paint cycle. When a common control (in this case the list view) is about to paint (draw) something it sends a message to its parent window informing it of what it is about to do. We have to intercept this NM_CUSTOMDRAW message and process it ourselves. Unfortunately,​ by the time the message has got through to BB4W to be handled by a ON SYS statement some of the data asscociated with the message may or may not still be present. Because of this volatility of the data asscociated with the message we have to intercept it before it gets to the ON SYS statement. At present, it is not possible to SUBCLASS the window using the SUBCLASS.BBC library and so we must process the message in our own message loop. This require some assembly language. This article describes intercepting and processing the messages.\\ \\  Here is an example program you may wish to look at:\\ http://​tech.groups.yahoo.com/​group/​bb4w/​files/​%22Temp%20Folder%22/​MDCH/​LVCOLOURTEXT.bbc\\ \\  and you may want to cross reference this article with the SDK
 + 
 +[[https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx]] 
 + 
 +First, before we assemble the code we will need to define some variables/​constants. 
 + 
 +<code bb4w>
    ​WM_NOTIFY = 78    ​WM_NOTIFY = 78
    ​NM_CUSTOMDRAW=-12    ​NM_CUSTOMDRAW=-12
Line 39: Line 45:
    \ iGroup% \    \ iGroup% \
    \ }    \ }
-Although we don't need all of these I have included them for future use. The MDCH_SUBITEM is my application defined variable we will need later. I have included the lvi{} here but if you have already set up and filled a list view you will probably already have a definition of an LVITEM structure. You may have to change the name of the lvi{} to suit your program. Also, the assembly process will need to know the handle to your list view. This is the value returned from the FN_createwindow( "​SysListView32"​...) function you use to set up the list view. In this article I will use hList% but you may need to change this name.\\ ​ Now for the PROCassemble\\ +    
 +</​code>​ 
 + 
 +Although we don't need all of these I have included them for future use. The MDCH_SUBITEM is my application defined variable we will need later. I have included the lvi{} here but if you have already set up and filled a list view you will probably already have a definition of an LVITEM structure. You may have to change the name of the lvi{} to suit your program. Also, the assembly process will need to know the handle to your list view. This is the value returned from the FN_createwindow( "​SysListView32"​...) function you use to set up the list view. In this article I will use hList% but you may need to change this name.\\ ​ Now for the PROCassemble 
 + 
 +<code bb4w>
    DEF PROCassemble    DEF PROCassemble
    LOCAL lvit{}, Textbuffer%,​ gap%, code, L%    LOCAL lvit{}, Textbuffer%,​ gap%, code, L%
Line 116: Line 127:
    SYS "​SetWindowLong",​ @hwnd%, -4, newwndproc    SYS "​SetWindowLong",​ @hwnd%, -4, newwndproc
    ​ENDPROC    ​ENDPROC
-I will now go through this code line by line explaining what each line does. This code is from the example program and you will probably want to modify it to colour text according to your application. The example program should show you a list view with three columns with a coloured background, letters in the first column, numbers in the second with negative numbers coloured red, and some numbers in the third column all coloured green.\\ +</​code>​ 
 + 
 +I will now go through this code line by line explaining what each line does. This code is from the example program and you will probably want to modify it to colour text according to your application. The example program should show you a list view with three columns with a coloured background, letters in the first column, numbers in the second with negative numbers coloured red, and some numbers in the third column all coloured green. 
 + 
 +<code bb4w>
    LOCAL lvit{}, Textbuffer%,​ gap%, code, L%    LOCAL lvit{}, Textbuffer%,​ gap%, code, L%
    DIM lvit{}=lvi{},​ Textbuffer% 256, gap% 2048, code 500, L% -1    DIM lvit{}=lvi{},​ Textbuffer% 256, gap% 2048, code 500, L% -1
Line 122: Line 137:
    P% = code    P% = code
    [OPT pass%    [OPT pass%
 +</​code>​
 +
 First we define a space for a LVITEM structure which we will use, an address for a Text Buffer, a gap between the variables and the code, the code, and a variable (L%) as the LIMIT variable when assembling the code.\\ ​ The DIM statement now defines the structure and reserves the space needed for the code.\\ ​ FOR...[OPT pass% ... is a standard way of performing two pass assembly. We need two pass assembly because we use forward reference labels. (See the manual or your favourite ASM reference for more details.)\\ ​ First we define a space for a LVITEM structure which we will use, an address for a Text Buffer, a gap between the variables and the code, the code, and a variable (L%) as the LIMIT variable when assembling the code.\\ ​ The DIM statement now defines the structure and reserves the space needed for the code.\\ ​ FOR...[OPT pass% ... is a standard way of performing two pass assembly. We need two pass assembly because we use forward reference labels. (See the manual or your favourite ASM reference for more details.)\\ ​
    ​.oldwndproc    ​.oldwndproc
Line 183: Line 200:
    #​endif    #​endif
    } NMLVCUSTOMDRAW,​ *LPNMLVCUSTOMDRAW;​    } NMLVCUSTOMDRAW,​ *LPNMLVCUSTOMDRAW;​
-Yuk! Translating this to BB4W gives:\\ +Yuk! Translating this to BB4W gives: 
 + 
 +<code bb4w>
    DIM NMLVCUSTOMDRAW{ \    DIM NMLVCUSTOMDRAW{ \
    \ nmcd{}=NMCUSTOMDRAW{},​ \    \ nmcd{}=NMCUSTOMDRAW{},​ \
Line 197: Line 216:
    \ rcText{}=RECT{},​ \    \ rcText{}=RECT{},​ \
    \ uAlign% }    \ uAlign% }
-where:\\ +</​code>​ 
 + 
 +where: 
 + 
 +<code bb4w>
    DIM RECT{l%,​t%,​r%,​b%}    DIM RECT{l%,​t%,​r%,​b%}
    DIM NMHDR{hwndFrom%,​ idFrom%, code%}    DIM NMHDR{hwndFrom%,​ idFrom%, code%}
Line 208: Line 231:
    \ ItemState%, \    \ ItemState%, \
    \ ItemlParam% }    \ ItemlParam% }
 +</​code>​
 +
 We don't need to include these definitions,​ they are given for reference only but we do need to know the relative address of the different members to be able to change them.\\ ​ To change the background colour of a SUBITEM we need to change the NMLVCUSTOMDRAW.clrTextBk% member. If we count up the bytes each member occupies of the NMLVCUSTOMDRAW structure we find that clrTextBk% is at byte 52. Eh? Surely it's at 8 where nmcd{} is at byte 0? Well, we need to include all the bytes of the substructures aswell, so nmcd{} is made of 48 bytes (including their hdr{} and rc{} substructures...) and clrTextBk% is after clrTextBk%... we now need to change the value to a COLORREF value which is in the form &​00BBGGRR where RR,GG and BB are the byte values of the Red, Green and Blue components. So for example to change it to a vomit-like greeny brown we change eax+52 to &​9999.\\ ​ Next, we'd like to change the colour of the text in the second of third column so we have to find out which column (SUBITEM) we are currently drawing. We query the value of NMLVCUSTOMDRAW.iSubItem%. It is a zero based index of columns ie the first column is 0. If it is column 1 jump to subitem1 routine otherwise if it is column 2 change it to green.\\ ​ We don't need to include these definitions,​ they are given for reference only but we do need to know the relative address of the different members to be able to change them.\\ ​ To change the background colour of a SUBITEM we need to change the NMLVCUSTOMDRAW.clrTextBk% member. If we count up the bytes each member occupies of the NMLVCUSTOMDRAW structure we find that clrTextBk% is at byte 52. Eh? Surely it's at 8 where nmcd{} is at byte 0? Well, we need to include all the bytes of the substructures aswell, so nmcd{} is made of 48 bytes (including their hdr{} and rc{} substructures...) and clrTextBk% is after clrTextBk%... we now need to change the value to a COLORREF value which is in the form &​00BBGGRR where RR,GG and BB are the byte values of the Red, Green and Blue components. So for example to change it to a vomit-like greeny brown we change eax+52 to &​9999.\\ ​ Next, we'd like to change the colour of the text in the second of third column so we have to find out which column (SUBITEM) we are currently drawing. We query the value of NMLVCUSTOMDRAW.iSubItem%. It is a zero based index of columns ie the first column is 0. If it is column 1 jump to subitem1 routine otherwise if it is column 2 change it to green.\\ ​
    .red    .red
Line 248: Line 273:
    push hList%    push hList%
    call "​SendMessage"​    call "​SendMessage"​
-First, notice that we first save eax (the address of the NMLVCUSTOMDRAW{}) on the stack by push eax. This is because the "​SendMessage"​ function will return a value in eax and overwrite it if we don't. We then check to see if the first character in the textbuffer (where the string was sent to) is "​-"​ (&2D). If it is we know that it represents a negative value. As before, the zero flag will be set if it is. We then restore eax by "pop eax" and test the zero flag. If it is zero (ie value is negative) we jump to the red label which will write the COLORREF red to the clrText% member of the NMLVCUSTOMDRAW structure.\\ ​ The final thing we must do is to finish the assembly process and then tell our machine code where the oldwndproc address is. This is done with the following lines.\\ +First, notice that we first save eax (the address of the NMLVCUSTOMDRAW{}) on the stack by push eax. This is because the "​SendMessage"​ function will return a value in eax and overwrite it if we don't. We then check to see if the first character in the textbuffer (where the string was sent to) is "​-"​ (&2D). If it is we know that it represents a negative value. As before, the zero flag will be set if it is. We then restore eax by "pop eax" and test the zero flag. If it is zero (ie value is negative) we jump to the red label which will write the COLORREF red to the clrText% member of the NMLVCUSTOMDRAW structure.\\ ​ The final thing we must do is to finish the assembly process and then tell our machine code where the oldwndproc address is. This is done with the following lines. 
 + 
 +<code bb4w>
    ]    ]
    NEXT    NEXT
Line 254: Line 281:
    SYS "​SetWindowLong",​ @hwnd%, -4, newwndproc    SYS "​SetWindowLong",​ @hwnd%, -4, newwndproc
    ​ENDPROC    ​ENDPROC
 +</​code>​
 +
 And that should be it! I hope this is not too long winded. Obviously all the mistakes are my own. I hope people may find this useful. I would love to hear about any comments or experiences people have trying to use the code.\\ \\  Michael. And that should be it! I hope this is not too long winded. Obviously all the mistakes are my own. I hope people may find this useful. I would love to hear about any comments or experiences people have trying to use the code.\\ \\  Michael.
colour_20text_20in_20a_20list_20view.txt · Last modified: 2018/04/13 11:47 by richardrussell