News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

LVM_SORTITEMS problem

Started by jj2007, August 22, 2012, 09:21:38 AM

Previous topic - Next topic

jj2007

I have a listview with filenames, and want to sort them via an invoke SendMessage, hLV, LVM_SORTITEMS, eax, MbSbc, where MbSbc is the callback that does the comparisons:

MbSbc proc lp1, lp2, mode ; SortByColumn
LOCAL lvi:LV_ITEM, sPath1[MAX_PATH]:BYTE, sPath2[MAX_PATH]:BYTE
mov lvi.cchTextMax, MAX_PATH
mov lvi.imask, LVIF_TEXT
and lvi.iSubItem, 0
m2m lvi.iItem, lp1
lea esi, sPath1
mov lvi.pszText, esi
invoke SendMessage, hLV, LVM_GETITEM, 0, addr lvi
mov lvi.cchTextMax, MAX_PATH
mov lvi.imask, LVIF_TEXT
and lvi.iSubItem, 0
m2m lvi.iItem, lp2
lea edi, sPath2
mov lvi.pszText, edi
invoke SendMessage, hLV, LVM_GETITEM, 0, addr lvi
invoke lstrcmpi, esi, edi ; case-insensitive string comparison


This works fine, also for the size and date columns, but it chokes for the descending sort of filenames: there is partial garbage, i.e. most items are in the correct order but some are not.

Attached a log file showing the results of the comparisons, they look correct. I am stuck :(
Any idea? Apologies, it's MasmBasic, and the case-insensitive string comparison returns correct values only with MB version 22 August (the previous one returned, in case-insensitive mode, the difference between a-B=+31, which was not what you needed here... now it returns a-b=-1).

dedndave

that sounds familiar
i think i had a similar issue, once
it worked in one direction, but in the other, the data was being over-written - oops !

sinsi

One weird thing struck me when reading MSDN's LVM_GETITEM description:
QuoteIf the LVIF_TEXT flag is set in the mask member of the LVITEM structure, the pszText member must point to a valid buffer and the cchTextMax member must be set to the number of characters in that buffer. Applications should not assume that the text will necessarily be placed in the specified buffer. The control may instead change the pszText member of the structure to point to the new text, rather than place it in the buffer.
Could this have anything to do with it?

What happens if you try this:
    mov lvi.pszText, esi
    invoke SendMessage, hLV, LVM_GETITEM, 0, addr lvi
    mov esi,lvi.pszText  ;same with edi later

jj2007

Good idea, sinsi :t
      mov lviA.pszText, esi
      invoke SendMessage, hLV, LVM_GETITEM, 0, addr lviA
      .if esi!=lviA.pszText
         deb 1, "changed", $esi, $lviA.pszText
      .endif

However, this is not the culprit - no "changed" box seen so far, and it continues to sort
..example01
..unicode
..example07
:(

sinsi

I also only get a name sort every two column clicks i.e.
click->nothing, click->correct sorting, click->nothing, click->incorrect sorting

jj2007

Yes, I noticed that, too. I found this:
QuoteIf you click on an item after sorting using the LVM_SORTITEMS message to get it sorted, then the ITEM passed to the _ItemClicked event is NOT always the same as the one you think you clicked on the screen (they'll be the same if their position in the order hasnt changed otherwise you get the wrong it).
Somehow the internal order of rows gets confused when sorting the list. The very first click yields a correct result, the following ones may fail.

There is also an obscure and hilariously complicated hack by a certain Mike, "Microsoft MVP Visual Basic", here.

dedndave

sounds to me like it's another case of "MS MeSsed it up so roll your own"

qWord

#7
Hi,
AFAICS you are using the LV_ITEM.lParam of the items as a index. After the first (correct) sorting these indexes are invalid - I would store a pointer to the string (or a structure) in  LV_ITEM.lParam to solve this problem.

EDIT: see attachment
MREAL macros - when you need floating point arithmetic while assembling!

jj2007

#8
Quote from: qWord on August 22, 2012, 08:47:11 PM
AFAICS you are using the LV_ITEM.lParam of the items as a index. After the first (correct) sorting these indexes are invalid - I would store a pointer to the string (or a structure) in  LV_ITEM.lParam to solve this problem.

Yes, it seems that these indexes become invalid. Keeping the original index in lParam was actually my initial approach, and it does seem to work better (besides, it works perfectly for size & date).

Here are lines 426ff of the attachment:

      mov ecx, Files$(lp1)   ; get ptr to file, 0-based index in lp1+lp2
      if useSD   ; use StringsDiffer or lstrcmpi?
         void StringsDiffer(ecx, Files$(lp2), 1)   ; 1=case-insensitive
            if 0
               test eax, eax         ; activate to simulate lstrcmpi behaviour
               .if Sign?
                  or eax, -1
               .elseif eax
                  mov eax, 1
               .endif
            endif

Now the weird thing is again that
a) it works fine ascending, but the descending version has usually one item that is inserted in the wrong location.
b) if I use lstrcmpi (returning -1/0/1) it sorts exactly once... 8)

P.S.: Just saw your edit - thanks a lot :t
Your version does not show the wrong sorting behaviour, but the size & date columns do no longer work... will see 8)

P.P.S.: It seems I got it, see attachment. Stupid noob bug :redface: