I still couldn't fix that one. When you right click, the "old" item is still marked as selected, and the "new" one is not... perhaps I'm missing something about this CDIS_* flags?


Works with dual wheel mice. Also, right click and HOLD the right mouse button down on an item, you'll see a highlight happen (the old way).

If I'm not mistaken that is handled by the TVHT_ONITEM. As your right click menu only happens if you're not on an item (directly).
Posted on 2003-07-29 16:34:47 by FunkyMeister
Outstanding! Now it works with the mouse wheel! This will really help many people!
Posted on 2003-07-29 16:39:28 by Gunner
I wanted to make a lot of changes in the source... this is my last working copy.
- Added a new menu.
- Added an option to change the algorithm withour having to rebuild.
- Added an option to switch off the background and the filtering algorithm.
- Cosmetic change on the main window's icon ;)
- Now the pointer to the default treeview window proc is no longer in a global variable, but in GWL_USERDATA. (I took the idea for that from a sniplet recently posted by donkey).
Posted on 2003-07-30 14:08:40 by QvasiModo
And this is where all hell breaks loose, my latest (NOT WORKING) copy... :grin:

What I tried to do is to move all the global variables now used by the treeview procedures into a memory block allocated in the heap, and then storing the pointer to it in GWL_USERDATA. That way, we could have more than one treeview with a background in out application.

The problem is the program simply crashes when CallWindowProc is called for the first time. At first I thought that maybe the value at GWL_USERDATA, or the memory allocated in the heap, was being overwritten... but OllyDbg proved me wrong. When CallWindowProc is called, all the values passed to it are correct, and the data does not seem to be corrupted. However, it doesn't return from the call, instead it forces a termination with error code -1. Outside the debugger, it shows a GPF error message.

I don't really like to post buggy code here, but this is driving me nuts. I'm totally lost on what's going on here. :(

EDIT: Fixed. There must be a couple of bugs still lurking around, but I don't think it's going to crash again. :rolleyes:

There were a couple of reasons (I know of) of why it was crashing:
- The BITMAPINFO structure was not defined in windows.inc the way I thought it was, so some data was not written properly.
- There was an attempt to access the memory block after it was freed (after processing WM_DESTROY message).

BTW, I've also fixed the right click problem. FunkyMeister, you were right... however not passing right click messages to default procedure when hit test returns TVHT_ONITEM did not seem to work. Neither TVHT_ONITEMLABEL. So I simply never let the treeview default procedure handle right-click messages. :grin: I hope that does not cause problems...
Posted on 2003-07-30 14:15:59 by QvasiModo
New update:

- Added checkboxes and hot tracking support.

- Added an alternative painting routine in case CreateDIBSection fails. It's taff's original painting algo. However, I could not get it to work very well...

- Added XP styles support. Not tested (I don't have XP)

- Now the custom item colors don't need any extra code in TreeProc. Instead it is stored in TV_ITEM.lParam. It also supports the use of COLORREF values and COLOR_* equates (for the system colors).

- Added transparent items text support (only when COLOR_WINDOW is specified as the text item color).

- Uses VirtualAlloc instead of GlobalAlloc (faster)

- The checkboxes now work automatically (when all child items have the same check state, it is passed to the parent item)

- Now the background and algorithm selection is changed through window messages instead of procedure calls. This is part of my attempt of making the treeview independent (so we can have several of them, not conflicting with each other).

- There may be other changes, I don't remember :)
Posted on 2003-08-01 12:50:15 by QvasiModo
Another update, not many changes (I made some nifty switch/case macros, and used them in this project to increase code readability). Also slightly optimized it for speed (I think) by testing for more frequent window messages first (less cmp/jmp executions).

I was originally going to get rid of hll-like code when I saw all those ret/.elseif generating extra jumps that never were executed. Then I thought of making this macros, it still looks hll (this kind of sample apps should be readable), but does not generate any useless extra code.

I was also thinking of using bitRAKE's macros for that, but for a matter of principles I never use a macro I don't understand... :rolleyes:

The .between macro may to work very well when you pass it more than 2 values (I haven't tried). All the others should work fine. If someone finds this macros useful, or has any suggestions, please let me know. :)
Posted on 2003-08-04 11:07:19 by QvasiModo
Just tried it on XP here at college, I didn't even try to change the system colors and I got this. I'm going to have some trouble trying to debug this here... but I'll see what I can do. Of course, if anybody can help...

EDIT: I've managed to debug it and after a quick examination nothing seems to go wrong with my code. What does seem to happen is that the background is not a unique color, there seem to be small differences from one pixel to the next. I did not check this throughly, it was just my first impression. I've saved a screencap in BMP format, I'll see it back home (I just hate working with Visual C debugger :mad: ).

BTW, the computer is running Windows XP SP1 here. P2M had already reported this... but I thought this was happening only when the system colors were changed.
Posted on 2003-08-04 12:26:43 by QvasiModo

Just tried it on XP here at college, I didn't even try to change the system colors and I got this. I'm going to have some trouble trying to debug this here... but I'll see what I can do. Of course, if anybody can help...

EDIT: I've managed to debug it and after a quick examination nothing seems to go wrong with my code. What does seem to happen is that the background is not a unique color, there seem to be small differences from one pixel to the next. I did not check this throughly, it was just my first impression. I've saved a screencap in BMP format, I'll see it back home (I just hate working with Visual C debugger :mad: ).

BTW, the computer is running Windows XP SP1 here. P2M had already reported this... but I thought this was happening only when the system colors were changed.


Seen that mess on 98 too, with certain treeview classes. Moreover, the bottom of the treelines ignore the background color, and don't seem to redraw it at all. Just a suggestion, try getting the class name of the exact treeview you want to use, as XP may be substituting the one you asked for generally with another that's not 100% compatible with what you're doing.
Posted on 2003-08-05 07:03:04 by FunkyMeister
updates:
- Full 3-state checkboxes support.
- Automatic filtering of image lists to prevent unexpected transparency.
- Custom message to configure the automatic handling of check boxes.
- Text file that briefly explains how to add this to your code (for the impatient ;) ).
- Bug fixes. There are still a few version incompatibilities.
- Brand new bugs :grin:
Posted on 2003-08-06 13:48:39 by QvasiModo

updates:
- Full 3-state checkboxes support.
- Automatic filtering of image lists to prevent unexpected transparency.
- Custom message to configure the automatic handling of check boxes.
- Text file that briefly explains how to add this to your code (for the impatient ;) ).
- Bug fixes. There are still a few version incompatibilities.
- Brand new bugs :grin:


Brand new bug (well, it's actually and physically there), but it IS catchable (done it). The Checkbox, when you doubleclick on it, you'll notice something. :) The tree expands after the check happens. Now, the check setting by mousedown isn't such an idea, hitting that instead of the + beside it, can cause grief (usally the change happens on the mouse up, BUT, you have to make sure you're still OVER the same item's checkbox).

The easiest way to manually (and evilly) get rid of the doubleclick issue, is to toss an invalid button value into the message and let it go on it's merry way. (It'll invalidate it as being part of a doubleclick event. Cheap, cheating, but worked for me. Now if I can only remember where I put that code. :/ I do know that I had to change the message to WM_NONE, but the mouse state also had to be messed with. You could also invalidate the doubleclick time.)




DOH, toss that, when the treeview receives a doubleclick ignore it if it's over the checkbox. That'll make sure that doubleclicking only works elsewhere. :)
Posted on 2003-08-06 21:38:29 by FunkyMeister

Brand new bug (well, it's actually and physically there), but it IS catchable (done it). The Checkbox, when you doubleclick on it, you'll notice something. :) The tree expands after the check happens.

I noticed... but I thought it was a normal treeview behavior :grin: . After all, our treeview proc sends all messages to the default procedure, and then checks if the item state was changed or not. I didn't know if there was some kind of notification message for that...
DOH, toss that, when the treeview receives a doubleclick ignore it if it's over the checkbox. That'll make sure that doubleclicking only works elsewhere. :)

That was my problem, I didn't know when the click was on the checkbox. I don't think there's a hit text value for that... how do we find out?
:confused:
Posted on 2003-08-07 13:07:14 by QvasiModo
Another problem: the checkboxes look like crap. :grin: That's because I'm using the OEM bitmaps for it... but treeview uses different images. I couldn't retreive the defualt state image list (TVM_GETIMAGELIST returns NULL).

I was thinking of drawing the check bitmaps myself using the OEM bitmap for the checkmark only, and drawing a frame around it, but if you know a better way, please let me know...
Posted on 2003-08-07 13:12:47 by QvasiModo
Great. Now it does this. I'm reconsidering the use of 3 state checkboxes...

Ok, maybe I shouldn't have used OEM bitmaps. Or maybe the checkbox size is fixed for those (I was using the system metrics... big mistake). Or maybe I should make custom check box images and use those instead, compiling them as resources.

By the way, could anybody test this on an XP box that uses styles? I can't test that here (I don't have permission to change the config).
Posted on 2003-08-07 13:33:03 by QvasiModo


I noticed... but I thought it was a normal treeview behavior :grin: . After all, our treeview proc sends all messages to the default procedure, and then checks if the item state was changed or not. I didn't know if there was some kind of notification message for that...


It is, annoyingly enough, I had to get rid of it on another program that uses a treeview, because doubleclicking on the item took you to a website, doing so on the checkbox did the undesireable, did the check, expanded the tree AND opened the website. Mass chaos with just 2 clicks, fun, wow.



That was my problem, I didn't know when the click was on the checkbox. I don't think there's a hit text value for that... how do we find out?
:confused:


What I did was this:

A local variable to hold the label Node you're over.

On Hit test messages, record the node you're OVER in that local variable, ONLY if it's on the label, otherwise, 0 it.

When a doubleclick comes in, test the node (in the local variable), if it's at 0, ignore it (you're not on a label).


TVHT_ONITEMICON ; The icon beside the label.
TVHT_ONITEMSTATEICON ; The Checkbox!
TVHT_ONITEMLABEL ; I'll just say DUH here. :grin:

I get the top two mixed up most of the time. Another thing with the hit test messages that come in, you can do tree node text expansion, by putting up a tooltip class window with the full entry if the window isn't wide enough to show all the text. :)

Gee, now if only my issues with 100,000+ listitem adds were as easy as your treeview woes. :mad:
Posted on 2003-08-07 16:04:51 by FunkyMeister

It is, annoyingly enough, I had to get rid of it on another program that uses a treeview, because doubleclicking on the item took you to a website, doing so on the checkbox did the undesireable, did the check, expanded the tree AND opened the website. Mass chaos with just 2 clicks, fun, wow.

LOL :grin: :grin: :grin:
What I did was this:

A local variable to hold the label Node you're over.

On Hit test messages, record the node you're OVER in that local variable, ONLY if it's on the label, otherwise, 0 it.

When a doubleclick comes in, test the node (in the local variable), if it's at 0, ignore it (you're not on a label).


TVHT_ONITEMICON ; The icon beside the label.
TVHT_ONITEMSTATEICON ; The Checkbox!
TVHT_ONITEMLABEL ; I'll just say DUH here. :grin:

I get the top two mixed up most of the time. Another thing with the hit test messages that come in, you can do tree node text expansion, by putting up a tooltip class window with the full entry if the window isn't wide enough to show all the text. :)


When I saw the hit test values, I totally forgot that check boxes were implemented as state images... :o Silly of me. This is going to improve the code... :)

Gee, now if only my issues with 100,000+ listitem adds were as easy as your treeview woes. :mad:


:grin: I had a similar problem quite some time ago. The solution I found was callback items and custom draw messages. That saves memory because you don't even have to add subitems (can be handled from custom draw messages). Of course, my case might have been simpler, all the labels were stored in .data section... I had a list of possible values, and the code to process callback messages would simply return a pointer to the hardcoded string. :)
Posted on 2003-08-07 16:23:21 by QvasiModo


When I saw the hit test values, I totally forgot that check boxes were implemented as state images... :o Silly of me. This is going to improve the code... :)


Sometimes others point out the obvious. (Gee, maybe I need that done for my listview, odd on how WM_NOTIFY comes as 204E and not 4E.)



:grin: I had a similar problem quite some time ago. The solution I found was callback items and custom draw messages. That saves memory because you don't even have to add subitems (can be handled from custom draw messages). Of course, my case might have been simpler, all the labels were stored in .data section... I had a list of possible values, and the code to process callback messages would simply return a pointer to the hardcoded string. :)


Well, the data is as follows: Column 0 is the "prettied display", 1 is the raw data, 2 is the sort data. The listviews are "banks", each one contains 32767 entries and the total listviews is also 32767 (doubt anyone would ever get that far), now the problem is, it gets slower the more I add to each listview (chances are there are messages I could be intercepting to make that easier, but I can't find reference to them: 1007, 1038 , 204E (WM_NOTIFY)

That and all the mangled mouse messages are annoying. :/ Guess I need to figure out if it's trying to do a drag start, there's not a single proper mouse event on that listview, the mouse down happens on the mouse up, the mouseup happens on a doubleclick, it's just bizarre.
Posted on 2003-08-08 07:43:12 by FunkyMeister
If you can, store all your listview data with VirtualAlloc, trying to put all of it in one block (to minimize memory usage). When the LVN_GETDISPINFO message arrives, instead of copying the data to the buffer provided you can place a pointer to your already allocated data in pszText, and set the LVIS_DI_SETITEM flag. That way the listview does not store the data, and does not ask for it again. It should be working fast enough, the catch is that you must keep all of your data in commited memory all the time...
A slower option is to allocate using LocalAlloc, could be VERY slow...
Another option is the first method, but reducing memory usage by storing only the minimum required data, and calculating the rest when LVN_GETDISPINFO arrives. The catch is you can't set LVIS_DI_SETITEM...
I think that's pretty much everything one can do with a listview. If it's still causing trouble, you could use a different control. Who knows :)

EDIT: One more thing, you can also process custom draw messages to set the subitems data, instead of calling LVM_SETITEM for that. That might help, but I guess it depends a lot on the data you are using and how it is stored. It could be slower, though, since you have to process a message for each subitem in each item... but only for the visible items. LVN_GETDISPINFO is probably the one slowing things down...
Posted on 2003-08-08 10:40:58 by QvasiModo

I get the top two mixed up most of the time. Another thing with the hit test messages that come in, you can do tree node text expansion, by putting up a tooltip class window with the full entry if the window isn't wide enough to show all the text. :)

Will do... :)

I tried blocking doubleclick messages on the state image, but it didn't work... Also tried sending dummy WM_LBUTTONDOWN messages, but to no avail. I'll keep trying.
Posted on 2003-08-08 11:02:40 by QvasiModo

If you can, store all your listview data with VirtualAlloc, trying to put all of it in one block (to minimize memory usage). When the LVN_GETDISPINFO message arrives, instead of copying the data to the buffer provided you can place a pointer to your already allocated data in pszText, and set the LVIS_DI_SETITEM flag. That way the listview does not store the data, and does not ask for it again. It should be working fast enough, the catch is that you must keep all of your data in commited memory all the time...
A slower option is to allocate using LocalAlloc, could be VERY slow...
Another option is the first method, but reducing memory usage by storing only the minimum required data, and calculating the rest when LVN_GETDISPINFO arrives. The catch is you can't set LVIS_DI_SETITEM...
I think that's pretty much everything one can do with a listview. If it's still causing trouble, you could use a different control. Who knows :)


I thought about the bulk load, but the problem is, the program needs to modify it, the data is nowhere near static, it sorts, inserts, deletes, manipulates it (sometimes insanely). As for the LVN_GETDISPINFO, I'm already catching that and telling it to take a flying leap if the listview isn't visible and if it is, only respond to the first column. I thought it was the GETDISPINFO myself, but it doesn't seem to phase it at all, though the data I'm working with is about 8MB right now, split across 3 listviews (put the max to 16383 entries, as getting to 32767 seemed to cause even the single column to get sluggish).

The different control idea had me wondering too, I originally was syncing the data to the listview's first column from a RichText control, but it's sad limitation of 16 bit line number accessing, made grabbing the text based data a waste. :/ I was thinking of MS's FlexGrid control, but alas, I'm not wanting to go to the efforts of actually doing the code to do that, only to find it's also going to be just as slow or have even worse limits. :( I just don't seem to be able to find something sane to work with. I was thinking of a database (ODBC) and repopulating it with the data, but all that work may not pay of either. :( Sometimes designing data structures can be a pest.

Originally posted by QvasiModo
EDIT: One more thing, you can also process custom draw messages to set the subitems data, instead of calling LVM_SETITEM for that. That might help, but I guess it depends a lot on the data you are using and how it is stored. It could be slower, though, since you have to process a message for each subitem in each item... but only for the visible items. LVN_GETDISPINFO is probably the one slowing things down...


I had thought of that, but the problem is, all the code necessary to do that, wouldn't really speed it up. I was also considering making the column 0 out of the data manually, for speed of addition (which that would speed it up a good deal), but then the overhead when rolling the list up and down fast would be worse. I seem to need to trade off speed for functionality somewhere. :/
Posted on 2003-08-08 18:30:35 by FunkyMeister

I thought about the bulk load, but the problem is, the program needs to modify it, the data is nowhere near static, it sorts, inserts, deletes, manipulates it (sometimes insanely). As for the LVN_GETDISPINFO, I'm already catching that and telling it to take a flying leap if the listview isn't visible and if it is, only respond to the first column. I thought it was the GETDISPINFO myself, but it doesn't seem to phase it at all, though the data I'm working with is about 8MB right now, split across 3 listviews (put the max to 16383 entries, as getting to 32767 seemed to cause even the single column to get sluggish).


How about this: allocate some relatively small memory space for each item (say 4kb), so you will have room for small and fast modifications without changing the pointer. That way you can use the GETDISPINFO trick, giving it a pointer and telling listview not to retrieve it again. If you need more room, you can realloc the memory and tell listview to invalidate data for that item (causing the LVN_GETDISPINFO message to arrive again). That way memory usage will be higher, but you have a fast and easy way of changing your items data.

BTW, the dispinfo message should be replied always if you do this... disregarding the message when the item is invisible will only cause listview to resend the message several times. Anyway, I think (but I have not confirmed this) that message is only sent when the items data is updated, for newly added items, and when an item becomes visible. So in most cases you could rely on listview not asking for the data too often.
I had thought of that, but the problem is, all the code necessary to do that, wouldn't really speed it up. I was also considering making the column 0 out of the data manually, for speed of addition (which that would speed it up a good deal), but then the overhead when rolling the list up and down fast would be worse. I seem to need to trade off speed for functionality somewhere. :/

Yes, it didn't seem much of an idea... it could be useful, though, if you become severely limited my memory usage, and need to sacrifice some speed to make the program work with very large lists. After all, a slow program is better than a crashed one :grin:
Posted on 2003-08-09 11:35:49 by QvasiModo