 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
evl
Joined: 24 Aug 2005 Posts: 1235
|
Posted: Fri Apr 14, 2006 9:32 pm Post subject: Listview colors for individual lines (e.g. highlighting) |
|
|
This code allows you to apply colors to a line (or as many as you like) in a listview - both background and text colors can be changed. This allows for effects such as:
- Highlighting alternating lines to make them easier to read.
- Highlighting certain lines to attract a user's attention to them.
- Flashing a line by toggling between colors.
Credit for this code goes to shimanov, I'm just providing a couple of extra little examples. Updated with ideas from toralf.
I think it might be possible to highlight specific "cells" within a listview by using CDRF_NOTIFYSUBITEMDRAW but I haven't been able to figure it out.
| Code: |
/* struct NMHDR {
HWND hwndFrom; uint4 0
UINT idFrom; uint4 4
UINT code; uint4 8
} 12
*/
/* struct NMCUSTOMDRAW {
NMHDR hdr; 12 0
DWORD dwDrawStage; uint4 12
HDC hdc; uint4 16
RECT rc; 16 20
DWORD_PTR dwItemSpec; uint4 36
UINT uItemState; uint4 40
LPARAM lItemlParam; int4 44
} 48
*/
/* struct NMLVCUSTOMDRAW {
NMCUSTOMDRAW nmcd; 48 0
COLORREF clrText; uint4 48
COLORREF clrTextBk; uint4 52
#if (_WIN32_IE >= 0x0400)
int iSubItem; int4 56
#endif
#if (_WIN32_IE >= 0x560)
DWORD dwItemType; uint4 60
COLORREF clrFace; uint4 64
int iIconEffect; int4 68
int iIconPhase; int4 72
int iPartId; int4 76
int iStateId: int4 80
RECT rcText; 16 84
UINT uAlign; uint4 100
#endif
} 104
*/
Gui, +LastFound
Gui, Add, ListView, x5 y5 w200 h200 vLV_Sample, index|day
LV_Add( "", 1, "Monday" )
LV_Add( "", 2, "Tuesday" )
LV_Add( "", 3, "Wednesday" )
LV_Add( "", 4, "Thursday" )
LV_Add( "", 5, "Friday" )
LV_Add( "", 6, "Saturday" )
LV_Add( "", 7, "Sunday" )
LV_ModifyCol( 1, "AutoHdr" )
LV_ModifyCol( 2, "AutoHdr" )
Gui, Show, x50 y50 w210 h210
;nothing special up to this point
LV_ColorInitiate() ; (Gui_Number, Control) - defaults to: (1, SysListView321)
Msgbox, Example 1: Highlighting alternating lines in the listview.
Loop, % LV_GetCount()
{
If ( Mod( A_Index, 2 ) )
LV_ColorChange(A_Index, "0xFFFFFF", "0xFF0000")
}
Msgbox, Example 2: Highlighting specific lines in the listview.
LV_ColorChange() ; clear all highlighting
LV_ColorChange(6, "0xFFFFFF", "0xFF0000")
LV_ColorChange(7, "0xFFFFFF", "0xFF0000")
Msgbox, Example 3: Flashing specific lines in the listview.
LV_ColorChange() ; clear all highlighting
Loop, 3 {
LV_ColorChange(6) ; default color (highlight off)
LV_ColorChange(7)
Sleep, 500
LV_ColorChange(6, "0xFFFFFF", "0xFF0000")
LV_ColorChange(7, "0xFFFFFF", "0xFF0000")
Sleep, 500
}
Return
GuiClose:
GuiEscape:
Exitapp
LV_ColorInitiate(Gui_Number=1, Control="") ; initiate listview color change procedure
{
global hw_LV_ColorChange
If Control =
Control =SysListView321
Gui, %Gui_Number%:+Lastfound
Gui_ID := WinExist()
ControlGet, hw_LV_ColorChange, HWND,, %Control%, ahk_id %Gui_ID%
OnMessage( 0x4E, "WM_NOTIFY" )
}
LV_ColorChange(Index="", TextColor="", BackColor="") ; change specific line's color or reset all lines
{
global
If Index =
Loop, % LV_GetCount()
LV_ColorChange(A_Index)
Else
{
Line_Color_%Index%_Text := TextColor
Line_Color_%Index%_Back := BackColor
WinSet, Redraw,, ahk_id %hw_LV_ColorChange%
}
}
WM_NOTIFY( p_w, p_l, p_m )
{
local draw_stage, Current_Line, Index
if ( DecodeInteger( "uint4", p_l, 0 ) = hw_LV_ColorChange ) {
if ( DecodeInteger( "int4", p_l, 8 ) = -12 ) { ; NM_CUSTOMDRAW
draw_stage := DecodeInteger( "uint4", p_l, 12 )
if ( draw_stage = 1 ) ; CDDS_PREPAINT
return, 0x20 ; CDRF_NOTIFYITEMDRAW
else if ( draw_stage = 0x10000|1 ){ ; CDDS_ITEM
Current_Line := DecodeInteger( "uint4", p_l, 36 )+1
LV_GetText(Index, Current_Line)
If (Line_Color_%Index%_Text != ""){
EncodeInteger( Line_Color_%Index%_Text, 4, p_l, 48 ) ; foreground
EncodeInteger( Line_Color_%Index%_Back, 4, p_l, 52 ) ; background
}
}
}
}
}
DecodeInteger( p_type, p_address, p_offset, p_hex=true )
{
old_FormatInteger := A_FormatInteger
ifEqual, p_hex, 1, SetFormat, Integer, hex
else, SetFormat, Integer, dec
StringRight, size, p_type, 1
loop, %size%
value += *( ( p_address+p_offset )+( A_Index-1 ) ) << ( 8*( A_Index-1 ) )
if ( size <= 4 and InStr( p_type, "u" ) != 1 and *( p_address+p_offset+( size-1 ) ) & 0x80 )
value := -( ( ~value+1 ) & ( ( 2**( 8*size ) )-1 ) )
SetFormat, Integer, %old_FormatInteger%
return, value
}
EncodeInteger( p_value, p_size, p_address, p_offset )
{
loop, %p_size%
DllCall( "RtlFillMemory", "uint", p_address+p_offset+A_Index-1, "uint", 1, "uchar", p_value >> ( 8*( A_Index-1 ) ) )
}
|
Last edited by evl on Sat Apr 15, 2006 5:47 pm; edited 3 times in total |
|
| Back to top |
|
 |
toralf
Joined: 31 Jan 2005 Posts: 3911 Location: Bremen, Germany
|
Posted: Fri Apr 14, 2006 10:50 pm Post subject: |
|
|
Very cool stuff. Thanks for sharing it.
I guess it can be even more simplified (put into function calls) like it was done with the statusbar functions. But maybe Chris wants to implement it directly into AHK, since I seem to remember that he wanted to take a look into it (although I do not find it in the planned features list) since some users asked for it. Either way you have reduced R&D and helped a lot of people. I'll use it. _________________ Ciao
toralf  |
|
| Back to top |
|
 |
toralf
Joined: 31 Jan 2005 Posts: 3911 Location: Bremen, Germany
|
Posted: Sat Apr 15, 2006 12:12 am Post subject: |
|
|
This is a mod of the third example. It introduces the following functions that (I hope) will simpily the use of this feature.
LV_ActivateColor()
LV_ChangeColor(Index, TextColor, BackColor)
The original code had a problem when the user sorted the lines in a different order, the color didn't follow the lines. If you had given row 6 a color, row 6 would keep the color regardless how the lines in the listview change. This code will make sure the color stays with the line. It uses the index of each line to assign the color.
| Code: | Gui, +LastFound
Gui, Add, ListView, x5 y5 w200 h200 vLV_Sample, index|day
Gui, Show, x50 y50 w210 h210
LV_Add( "", 1, "Monday" )
LV_Add( "", 2, "Tuesday" )
LV_Add( "", 3, "Wednesday" )
LV_Add( "", 4, "Thursday" )
LV_Add( "", 5, "Friday" )
LV_Add( "", 6, "Saturday" )
LV_Add( "", 7, "Sunday" )
LV_ModifyCol( 1, "AutoHdr" )
LV_ModifyCol( 2, "AutoHdr" )
;nothing special up to this point
LV_ActivateColor()
Loop, 5 {
LV_ChangeColor(6, "", "")
LV_ChangeColor(7, "", "")
Sleep, 500
LV_ChangeColor(6, "0xFFFFFF", "0xFF0000")
LV_ChangeColor(7, "0xFFFFFF", "0xFF0000")
Sleep, 500
}
return
LV_ActivateColor(){
global hw_LV_Sample
OnMessage( 0x4E, "WM_NOTIFY" )
ControlGet, hw_LV_Sample, HWND,, SysListView321
}
LV_ChangeColor(Index, TextColor, BackColor){
global
Line_Color_%Index%_Text := TextColor
Line_Color_%Index%_Back := BackColor
WinSet, Redraw,, ahk_id %hw_LV_Sample%
}
WM_NOTIFY( p_w, p_l, p_m ) {
global hw_LV_Sample
if ( DecodeInteger( "uint4", p_l, 0 ) = hw_LV_Sample ) {
if ( DecodeInteger( "int4", p_l, 8 ) = -12 ) { ; NM_CUSTOMDRAW
draw_stage := DecodeInteger( "uint4", p_l, 12 )
if ( draw_stage = 1 ) ; CDDS_PREPAINT
return, 0x20 ; CDRF_NOTIFYITEMDRAW
else if ( draw_stage = 0x10000|1 ){ ; CDDS_ITEM
Current_Line := DecodeInteger( "uint4", p_l, 36 )+1
LV_GetText(Index, Current_Line)
If (Line_Color_%Index%_Text != ""){
EncodeInteger( Line_Color_%Index%_Text, 4, p_l, 48 ) ; foreground
EncodeInteger( Line_Color_%Index%_Back, 4, p_l, 52 ) ; background
}
}
}
}
}
DecodeInteger( p_type, p_address, p_offset, p_hex=true ){
old_FormatInteger := A_FormatInteger
ifEqual, p_hex, 1, SetFormat, Integer, hex
else, SetFormat, Integer, dec
StringRight, size, p_type, 1
loop, %size%
value += *( ( p_address+p_offset )+( A_Index-1 ) ) << ( 8*( A_Index-1 ) )
if ( size <= 4 and InStr( p_type, "u" ) != 1 and *( p_address+p_offset+( size-1 ) ) & 0x80 )
value := -( ( ~value+1 ) & ( ( 2**( 8*size ) )-1 ) )
SetFormat, Integer, %old_FormatInteger%
return, value
}
EncodeInteger( p_value, p_size, p_address, p_offset ){
loop, %p_size%
DllCall( "RtlFillMemory", "uint", p_address+p_offset+A_Index-1, "uint", 1, "uchar", p_value >> ( 8*( A_Index-1 ) ) )
} |
_________________ Ciao
toralf  |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1235
|
Posted: Sat Apr 15, 2006 11:32 am Post subject: |
|
|
Nice idea with the syntax and an elegant fix for the sorting/highlighting. It can be simplified a bit more by making the default color parameters blank if not passed, e.g.:
Reset color: LV_ChangeColor(6)
(instead of: LV_ChangeColor(6, "", "") )
And changing the function to have the default values:
LV_ChangeColor(Index, TextColor="", BackColor=""){
I'll update the 2nd 2 examples in my 1st post with these changes to make it easier for users to find the best code to use. |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1235
|
Posted: Sat Apr 15, 2006 1:09 pm Post subject: |
|
|
| I updated the whole of the first post's code to make it a single example, including the new syntax and a few small additions and a bugfix for global/local variables. |
|
| Back to top |
|
 |
toralf
Joined: 31 Jan 2005 Posts: 3911 Location: Bremen, Germany
|
Posted: Sat Apr 15, 2006 2:40 pm Post subject: |
|
|
Other possible changes:
- LV_ColorActivate can be called for different GUIs and Controls
- LV_ColorChange also allows the reset if "reset" is specified instead of a index number
| Code: | LV_ColorActivate(GuiID=1, ControlNN="") ; initiate listview color change procedure
{
global hw_LV_Sample
Gui, %GuiID%:+Lastfound
GuiHWND := WinExist()
If ClassNN is space
ClassNN = SysListView321
ControlGet, hw_LV_Sample, HWND,, %ControlNN%, ahk_id %GuiHWND%
OnMessage( 0x4E, "WM_NOTIFY" )
}
LV_ColorChange(Index, TextColor="", BackColor="") ; change specific line's color
{
global
If Index = Reset
Loop, % LV_GetCount()
LV_ColorChange(A_Index)
Else {
Line_Color_%Index%_Text := TextColor
Line_Color_%Index%_Back := BackColor
WinSet, Redraw,, ahk_id %hw_LV_Sample%
}
} | *not tested* _________________ Ciao
toralf  |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1235
|
Posted: Sat Apr 15, 2006 5:52 pm Post subject: |
|
|
Updated the first post to reflect those ideas and altered the syntax a little to make it clearer. (Thank goodness for being able to assign default values to parameters in functions, it keeps things so simple ) |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10666
|
Posted: Sat Apr 15, 2006 6:55 pm Post subject: |
|
|
Great demonstration (and clearly presented). I know this will be a popular topic since it has been frequently asked how to make some rows look different than others. Also, as was mentioned above, your work will make R&D easier when the time comes to integrate such features into AHK.
Thanks to Shimanov, Evl, and Toralf for this script. |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1235
|
Posted: Wed Apr 19, 2006 1:47 pm Post subject: |
|
|
I've noticed a problem with the highlighted lines not being re-drawn after LV_Modify is used to select a highlighted line and then un-selected - this also happens sometimes when using the mouse or keyboard to select a line, but less frequently (1 in 20 times maybe).
Any ideas why this might be?
Here's an example script to show what I mean:
http://autohotkey.net/evl/-Problems/listview%20color%20highlighting%20problem.ahk |
|
| Back to top |
|
 |
ambi Guest
|
Posted: Mon Apr 23, 2007 9:14 am Post subject: Changing the colors of a selected row in a listview |
|
|
I have merged the information here with evl's sample code.
The resulting example below not only highlights alternating rows in the listview using custom colors, but it also uses (different) custom colors for the selected row.
| Code: |
#NoEnv
SendMode Input
/*
struct NMHDR {
HWND hwndFrom; uint4 0
UINT idFrom; uint4 4
UINT code; uint4 8
} 12
struct NMCUSTOMDRAW {
NMHDR hdr; 12 0
DWORD dwDrawStage; uint4 12
HDC hdc; uint4 16
RECT rc; 16 20
DWORD_PTR dwItemSpec; uint4 36
UINT uItemState; uint4 40
LPARAM lItemlParam; int4 44
} 48
struct NMLVCUSTOMDRAW {
NMCUSTOMDRAW nmcd; 48 0
COLORREF clrText; uint4 48
COLORREF clrTextBk; uint4 52
#if (_WIN32_IE >= 0x0400)
int iSubItem; int4 56
#endif
#if (_WIN32_IE >= 0x560)
DWORD dwItemType; uint4 60
COLORREF clrFace; uint4 64
int iIconEffect; int4 68
int iIconPhase; int4 72
int iPartId; int4 76
int iStateId: int4 80
RECT rcText; 16 84
UINT uAlign; uint4 100
#endif
} 104
struct LVITEM {
UINT mask; uint4 0
int iItem; 4
int iSubItem; 8
UINT state; uint4 12
UINT stateMask; uint4 16
LPTSTR pszText;
int cchTextMax;
int iImage;
LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
int iIndent;
#endif
#if (_WIN32_WINNT >= 0x560)
int iGroupId;
UINT cColumns; // tile view columns
PUINT puColumns;
#endif
#if (_WIN32_WINNT >= 0x0600)
int* piColFmt
int iGroup
#endif
}
*/
Gui, +LastFound
Gui, Add, ListView, x5 y5 w200 h200 gLV_Sample vLV_Sample, index|day
LV_Add( "", 1, "Monday" )
LV_Add( "", 2, "Tuesday" )
LV_Add( "", 3, "Wednesday" )
LV_Add( "", 4, "Thursday" )
LV_Add( "", 5, "Friday" )
LV_Add( "", 6, "Saturday" )
LV_Add( "", 7, "Sunday" )
LV_ModifyCol( 1, "AutoHdr" )
LV_ModifyCol( 2, "AutoHdr" )
Gui, Show, x50 y50 w210 h210
LV_ColorInitiate() ; (Gui_Number, Control) - defaults to: (1, SysListView321)
; Highlighting alternating lines in the listview.
Loop, % LV_GetCount()
{
If ( Mod( A_Index, 2 ) )
LV_ColorChange(A_Index, "0xFFFFFF", "0xFF0000")
}
Return
LV_Sample:
if A_GuiEvent = DoubleClick
{
LV_GetText(myday, A_EventInfo, 2)
MsgBox, %myday%!
}
Return
GuiClose:
GuiEscape:
Exitapp
LV_ColorInitiate(Gui_Number=1, Control="") ; initiate listview color change procedure
{
global hw_LV_ColorChange, LvItem
If Control =
Control =SysListView321
Gui, %Gui_Number%:+Lastfound
Gui_ID := WinExist()
ControlGet, hw_LV_ColorChange, HWND,, %Control%, ahk_id %Gui_ID%
VarSetCapacity(LvItem, 36, 0)
OnMessage( 0x4E, "WM_NOTIFY" )
}
LV_ColorChange(Index="", TextColor="", BackColor="") ; change specific line's color or reset all lines
{
global
If Index =
Loop, % LV_GetCount()
LV_ColorChange(A_Index)
Else
{
Line_Color_%Index%_Text := TextColor
Line_Color_%Index%_Back := BackColor
WinSet, Redraw,, ahk_id %hw_LV_ColorChange%
}
}
WM_NOTIFY( p_w, p_l, p_m )
{
local draw_stage, Current_Line, Index, IsSelected=0
Critical
if ( DecodeInteger( "uint4", p_l, 0 ) = hw_LV_ColorChange ) { ; NMHDR->hwndFrom
if ( DecodeInteger( "int4", p_l, 8 ) = -12 ) { ; NMHDR->code ; NM_CUSTOMDRAW
draw_stage := DecodeInteger( "uint4", p_l, 12 ) ; NMCUSTOMDRAW->dwDrawStage
Current_Line := DecodeInteger( "uint4", p_l, 36 )+1 ; NMCUSTOMDRAW->dwItemSpec
if ( draw_stage = 1 ) ; CDDS_PREPAINT
return, 0x20 ; CDRF_NOTIFYITEMDRAW
else if ( draw_stage = 0x10000|1 ) { ; CDDS_ITEMPREPAINT
If ( DllCall("GetFocus") = hw_LV_ColorChange ) { ; Control has Keyboard Focus?
SendMessage, 4140, Current_Line-1, 2, , ahk_id %hw_LV_ColorChange% ; LVM_GETITEMSTATE
IsSelected := ErrorLevel
If ( IsSelected = 2 ) { ; LVIS_SELECTED
EncodeInteger( "0xFFFFFF", 4, p_l, 48 ) ; NMCUSTOMDRAW->clrText ; foreground
EncodeInteger( "0x0000FF", 4, p_l, 52 ) ; NMCUSTOMDRAW->clrTextBk ; background
EncodeInteger(0x0, 4, &LvItem, 12) ; LVITEM->state
EncodeInteger(0x2, 4, &LvItem, 16) ; LVITEM->stateMask ; LVIS_SELECTED
SendMessage, 4139, Current_Line-1, &LvItem, , ahk_id %hw_LV_ColorChange% ; Disable Highlighting
; We want item post-paint notifications
Return, 0x00000010 ; CDRF_NOTIFYPOSTPAINT
}
LV_GetText(Index, Current_Line)
If (Line_Color_%Index%_Text != "") {
EncodeInteger( Line_Color_%Index%_Text, 4, p_l, 48 ) ; NMLVCUSTOMDRAW->clrText ; foreground
EncodeInteger( Line_Color_%Index%_Back, 4, p_l, 52 ) ; NMLVCUSTOMDRAW->clrTextBk ; background
}
}
}
else if ( draw_stage = 0x10000|2 ) ; CDDS_ITEMPOSTPAINT
If ( IsSelected ) {
EncodeInteger(0x02, 4, &LvItem, 12) ; LVITEM->state
EncodeInteger(0x02, 4, &LvItem, 16) ; LVITEM->stateMask ; LVIS_SELECTED
SendMessage, 4139, Current_Line-1, &LvItem, , ahk_id %hw_LV_ColorChange% ; LVM_SETITEMSTATE
}
}
}
}
DecodeInteger( p_type, p_address, p_offset, p_hex=true )
{
old_FormatInteger := A_FormatInteger
ifEqual, p_hex, 1, SetFormat, Integer, hex
else, SetFormat, Integer, dec
StringRight, size, p_type, 1
loop, %size%
value += *( ( p_address+p_offset )+( A_Index-1 ) ) << ( 8*( A_Index-1 ) )
if ( size <= 4 and InStr( p_type, "u" ) != 1 and *( p_address+p_offset+( size-1 ) ) & 0x80 )
value := -( ( ~value+1 ) & ( ( 2**( 8*size ) )-1 ) )
SetFormat, Integer, %old_FormatInteger%
return, value
}
EncodeInteger( p_value, p_size, p_address, p_offset )
{
loop, %p_size%
DllCall( "RtlFillMemory", "uint", p_address+p_offset+A_Index-1, "uint", 1, "uchar", p_value >> ( 8*( A_Index-1 ) ) )
}
|
|
|
| Back to top |
|
 |
Roland
Joined: 08 Jun 2006 Posts: 284
|
Posted: Tue Apr 24, 2007 6:54 pm Post subject: |
|
|
| Very nice, thanks! |
|
| Back to top |
|
 |
Tekl
Joined: 24 Sep 2004 Posts: 814 Location: Germany
|
Posted: Mon Nov 12, 2007 9:00 am Post subject: |
|
|
Is it also possible to change colors from external controls like the detail view of the explorer? _________________ Tekl |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 4250 Location: Belgrade
|
Posted: Mon Nov 12, 2007 9:54 am Post subject: |
|
|
Not with AHK  _________________
 |
|
| Back to top |
|
 |
almex
Joined: 16 Oct 2007 Posts: 10 Location: Las Vegas, NV, USA
|
Posted: Mon Feb 04, 2008 4:59 pm Post subject: |
|
|
Sorry for bringing up an old thread, but I've been using this code (thanks again, evl!) and noticed that the color hex is reversed. For instance:
0x008000 shows correctly as green. BUT...
0x0000FF should be blue, but shows as red.
0xFF0000 should be red, but shows as blue.
I'll admit I don't fully understand the code behind DecodeInteger & EncodeInteger, but I believe the problem lies in one of those functions. |
|
| Back to top |
|
 |
SKAN
Joined: 26 Dec 2005 Posts: 7609
|
Posted: Mon Feb 04, 2008 5:18 pm Post subject: |
|
|
| almex wrote: | 0x008000 shows correctly as green. BUT...
0x0000FF should be blue, but shows as red.
0xFF0000 should be red, but shows as blue. |
Try swapping the values from RGB to be BGR.
For example:
0xFF0000 will be Blue
 |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|