News:

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

Main Menu

Treeview find by lParam?

Started by 2B||!2B, June 28, 2019, 04:29:59 PM

Previous topic - Next topic

2B||!2B

Hi,

Is there any way to find a treeview item (parent) by its lParam? just like Listview.
Or must iterate through all parents to find the item?

jj2007

Have you tried something like this, in a WM_NOTIFY event?
.if NotifyID==MyTree && NotifyCode==TVN_SELCHANGEDW
mov edx, lParam_
mov eax, [edx.NMHDR.hwndFrom]
push eax
mov tvItem.hItem, rv(SendMessageW, eax, TVM_GETNEXTITEM, TVGN_CARET, 0) ; last="handle to an item"
m2m tvItem._mask, TVIF_TEXT or TVIF_HANDLE ; use handle, receive text
lea eax, buffer
mov tvItem.pszText, eax
m2m tvItem.cchTextMax, 100
pop ecx
invoke SendMessage, ecx, TVM_GETITEM, TVGN_CARET, addr tvItem
print tvItem.pszText
; deb 4, "GetItem", eax, $Err$(), $$tvItem.pszText ; MyTree, NotifyCode, NotifyHandle
.endif 

2B||!2B

Hi Jochen,

Thanks for the reply.
But i do not want to look for a selected item. Just a simple find by lParam like Listview. I think it is not possible since there is no TVM_FINDITEM message.

jj2007

You want to find what, a string? At a certain point, you put text into the treeview, so you have an array of strings, I suppose, that you could search. What exactly is your goal here?

fearless

I created a TreeViewFindItem function to search for text in a treeview, but could be adapted to look for other stuff I guess:
https://github.com/mrfearless/libraries/blob/master/Treeview/Treeview%20x86/TreeViewFindItem.asm

I use that in the cjsontree to search for a treeview item for text that is entered in the find textbox: https://github.com/mrfearless/cjsontree/blob/master/search.asm#L340

2B||!2B

Quote from: jj2007 on June 28, 2019, 07:55:55 PM
You want to find what, a string? At a certain point, you put text into the treeview, so you have an array of strings, I suppose, that you could search. What exactly is your goal here?

Finding an item by iterating through all treeview items is not ideal. It is slow when dealing with many items. Instead, you use item's lParam to find the index that has that lParam associated with it like LVM_FINDITEM(LVFI_PARAM set on LVFINDINFO structure) on listview.

Quote from: fearless on June 28, 2019, 07:58:37 PM
I created a TreeViewFindItem function to search for text in a treeview, but could be adapted to look for other stuff I guess:
https://github.com/mrfearless/libraries/blob/master/Treeview/Treeview%20x86/TreeViewFindItem.asm

I use that in the cjsontree to search for a treeview item for text that is entered in the find textbox: https://github.com/mrfearless/cjsontree/blob/master/search.asm#L340

Thanks fearless.
Unfortunately, there is no treeview message to find an item by its lParam value.
Will just have to adapt to this fact and use the string compare method.

fearless

I think the only way is to make an array, and every time you insert a treeview item you add to the array and record the lParam in the array along with the hItem. Then for searching, you only need to search the array table comparing the dword for lParam, once found return the hItem of the treeview item.

Im guessing internally the listview does some search through an array rather than iterate every hListitem
Could also expand the array idea to handle different hTreeview, first matching the treeview handle, then searching it's item array:

TVFIND STRUCT
  hTreeview DD ?
  dwItemCount DD ?
  lParamArray DD ? ; point to array of TVFITEM's x dwItemCount
TVFIND ENDS

TVFITEM STRUCT
  lParam DD ?
  hItem DD ?
TVFITEM ENDS

jj2007

Quote from: 2B||!2B on June 28, 2019, 08:17:20 PMFinding an item by iterating through all treeview items is not ideal. It is slow when dealing with many items.

Sure, it's slow:

include \masm32\MasmBasic\MasmBasic.inc         ; download
include ArrayFind.inc   ; see attachment
  Init
  PrintCpu 0
  Recall "Bible.txt", bible$()
  NanoTimer()
  Print Str$("The string is at index %i", ArrayFind(bible$(id), "He which testifieth these things saith"))      ; one of the last lines in Bible.txt
  PrintLine CrLf$, NanoTimer$(), " for finding the string"
EndOfCode


Intel(R) Core(TM) i5-2450M CPU @ 2.50GHz
The string is at index 33975
6536 µs for finding the string


Six milliseconds for a 4 MB text file (or 1.5 ms with the FAST version of Instr_()).

Do what fearless wrote, and what I tried to convey further up: Grab the index while you are adding text to your treeview.

2B||!2B

Nice example there Jochen. It is very fast method.

Edit: Thanks guys i have it working now.

Jochen.
Does your Masmbasic have a function similar to wInStr(unicode) but from right to left instead of left to right?
I couldn't find it in Masmbasic reference.
Or maybe is it wRinstr?

jj2007

wRinstr is available but only as wRinstr(src, pattern) with a one-byte pattern (e.g. "\")

Yes indeed. No idea why I never implemented that properly, probably because I never needed it :badgrin:

There is a workaround:

include \masm32\MasmBasic\MasmBasic.inc
  Init
  wLet esi=wRec$("This is a Unicode string: текст текст текст текст etc etc")
  Print Str$("The first occurrence of текст is at position %i\n", Instr_(Utf8$(esi), "текст"))
  Inkey Str$("The last occurrence of текст is at position  %i", Rinstr(Utf8$(esi), "текст"))
EndOfCode


The first occurrence of текст is at position 27
The last occurrence of текст is at position  60


It works, somehow, but the position of текст returned is in bytes, not Utf8 chars. No problem if you just want to extract a part of the string, though. What do you need it for?

TimoVJL

#10
Maybe making a hash value from string to lParam while inserting to tree is a good idea ?
Reading LPARAM is only about 2 times faster ?
May the source be with you

2B||!2B

#11
Thanks Jochen.
I was needing it to get file extension but i later used a windows API PathFindExtensionW.
It's always difficult for me to get the function i need from Masmbasic due to the poor documentation  :sad:

jj2007

Since thousands of coders stumble over this problem sooner or later, you have a chance to google the solution if a Windows API exists. But MasmBasic does have a solution, too:

include \masm32\MasmBasic\MasmBasic.inc
  Init

  ; version A, Unicode as Utf-8 strings:
  Let esi="Имя файла Доброе утро.текст"
  Let edi=Mid$(esi, Rinstr(esi, ".")+1)         ; the +1 is not possible with wRinstr()
  PrintLine "The extension is [", edi, "]  excluding the dot"

  ; version B, Unicode as wide strings:
  wLet esi=wRec$("Имя файла Доброе утро.текст")
  wLet edi=wMid$(esi, wRinstr(esi, "."))
  wPrintLine "The extension is [", edi, "] including the dot"

  ; version C, Unicode as wide strings:
  wLet esi=wRec$("Имя файла Доброе утро.текст")
  mov ecx, wLen(esi)
  .Repeat
        dec ecx
  .Until word ptr [esi+2*ecx]=="."
  lea ecx, [2*ecx+esi+2]
  wInkey "The extension is [", ecx, "]  excluding the dot"

EndOfCode


The extension is [текст]  excluding the dot
The extension is [.текст] including the dot
The extension is [текст]  excluding the dot


Quote from: jj2007 on June 29, 2019, 07:08:44 PM
wRinstr is available but only as wRinstr(src, pattern) with a one-byte pattern (e.g. "\")

And the dot is a one-byte pattern, too :thumbsup: