perlquestion
tomsell
<p>Hi monks, happy new year.</p>
<p>I'm not quite sure how to get Win32::GUI to display the familiar sort arrows in ListView headers. As this feature is not exposed by Win32::GUI, a suitable <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775247%28v=vs.85%29.aspx">HDITEM</a> structure has to be sent as the contents of the <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb775367%28v=vs.85%29.aspx">HDM_SETITEM message</a>.</p>
<p>Which I translate into:</p>
<hr>
<code>
sub HDI_FORMAT {0x4}
sub HDF_SORTDOWN {0x400}
sub HDF_STRING {0x4000}
sub HDM_SETITEM {0x1200 + 4}
$hditem = pack("LLLLLLLLLLL",
HDI_FORMAT, # mask
0, # cxy
0, # pszText
0, # hbm
0, # cchTextMax
HDF_SORTDOWN | HDF_STRING, # fmt
0, # lParam
0, # iImage
0, # iOrder
0, # type
0 # pvFilter
);
$hditem_ptr = pack('P44', $hditem);
$hwnd = $myListView->GetHeader();
</code>
<hr>
<p>None of the following calls seem to do anything, their return value being "4":</p>
<code>
Win32::GUI::SendMessage($hwnd, HDM_SETITEM, $columnClicked, $hditem_ptr);
Win32::GUI::SendMessage($hwnd, HDM_SETITEM, $columnClicked, unpack('L!', $hditem_ptr));
$sendMessage = new Win32::API("user32","SendMessage","NNIP","I");
$sendMessage->Call($hwnd, HDM_SETITEM, $columnClicked, $hditem_ptr);
$sendMessage->Call($hwnd, HDM_SETITEM, $columnClicked, unpack('L!', $hditem_ptr));
$listview->Show(1); # refresh
</code>
<p>However, Win32::GuiTest gets the job done:</p>
<code>
use Win32::GuiTest qw( :ALL );
$hditem_ptr = AllocateVirtualBuffer($hwnd, 44);
WriteToVirtualBuffer($hditem_ptr, $hditem);
SendMessage($hwnd, HDM_SETITEM, $columnClicked, $hditem_ptr->{ptr});
FreeVirtualBuffer($hditem_ptr);
ShowWindow($hwnd, 1); # refresh
</code>
<p>What am I missing here? Is it an address space issue or am I pack'ing the SendMessage lParam incorrectly? I'd really like to use Win32::GUI's SendMessage.</p>
<h3>Update</h3>
<p>The problem are these declarations:</p>
<code>
sub HDM_FIRST {0x1200}
sub HDM_GETITEM {HDM_FIRST+11} # !WRONG: HDM_FIRST not a function call, result is 1211
</code>
<h4>Here's the fix. </h4>
<code>
sub HDM_GETITEM {&HDM_FIRST+11} # RIGHT: HDM_FIRST called, result is 4619
</code>
<p>So, for posterity, here's the complete code to display sort arrows in Win32::GUI::ListView headers (you should put this on a ColumnClick event handler on the ListView):</p>
<code>
sub HDI_WIDTH {0x1}
sub HDI_FORMAT {0x4}
sub HDF_SORTUP {0x400}
sub HDF_SORTDOWN {0x200}
sub HDM_FIRST {0x1200}
sub HDM_GETITEM {&HDM_FIRST+11}
sub HDM_SETITEM {&HDM_FIRST+12}
sub HDF_LEFT {0x0}
sub HDF_RIGHT {0x1}
sub HDF_CENTER {0x2}
sub HDF_STRING {0x4000}
# loop over columns, setting the arrow on the clicked column
# and removing it from the others
for (0 .. scalar $myListView->GetColumnOrderArray()){
my $arrowstate = ($_ == $columnclicked) # $columnclicked courtesy of ColumnClick event handler
? $state # $state can be 'asc' or 'desc' (or something alike)
: 'unset'; # and 'unset'
setSortArrow($myListView, $_, $arrowstate);
}
sub setSortArrow {
my ($listview, $column, $state) = @_;
# request information on the format and the width of the column header
my $hditem = pack("L11",
HDI_FORMAT | HDI_WIDTH, # mask
0, # cxy
0, # pszText
0, # hbm
0, # cchTextMax
0, # fmt
0, # lParam
0, # iImage
0, # iOrder
0, # type
0 # pvFilter
);
my $ptr = pack('P44', $hditem);
my $hwnd = $listview->GetHeader();
my $r = Win32::GUI::SendMessage($hwnd, HDM_GETITEM, $column, unpack('L!', $ptr));
return unless $r;
my @members = unpack('L11', $hditem); # cxy and fmt now updated by call to HDM_GETITEM
my $width = $members[1];
my $cxy = 0;
my $fmt = sprintf("%X", $members[5]);
my $justf = (($fmt | HDF_RIGHT) eq $fmt) ? HDF_RIGHT
: ((($fmt | HDF_CENTER) eq $fmt) ? HDF_CENTER
: HDF_LEFT);
my $flags = HDF_STRING | $justf; # keep text and its justification
unless($state eq 'unset'){
$flags |= ($state eq 'asc') ? HDF_SORTUP : HDF_SORTDOWN;
# text on narrow columns will be trunacted with ellipses: adjust width a little
# FIXME: "80" is a little arbitrary and repeated sorting will keep very narrow
# columns growing until they are 80 pixels wide
if((($fmt & HDF_SORTDOWN & HDF_SORTUP) > 0) && ($width < 80)){
$cxy = $width + 5;
}
}
$members[0] = HDI_FORMAT | ($cxy ? HDI_WIDTH : 0x0);
$members[1] = $cxy;
$members[5] = $flags;
$hditem = pack("L11", @members);
$ptr = pack('P44', $hditem);
$r = Win32::GUI::SendMessage($hwnd, HDM_SETITEM, $column, unpack('L!', $ptr));
}
</code>
<p>Comments and improvements are welcome.</p>