Page 1 of 1

Search in ListViewGadget and ListIconGadget

Posted: Thu Dec 25, 2014 2:41 pm
by Otrebor
Hi

I need to find a number in ListViewGadget and when found it show at the top line.
The ListViewGadget is filled in ascending order (little random).
This code is just an example to give you better idea what i want:

Code: Select all

OpenWindow(0, 0, 0, 300, 300,"Listview example",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ListViewGadget(1,10,10,200,250)
StringGadget(2,230,10,60,20,"",#PB_String_Numeric)
ButtonGadget(3,230,40,60,20,"SEARCH")


SendMessage_(GadgetID(1),#WM_SETREDRAW,#False,0)
For f = 0 To 60000
  r = Random(3 , 1)
  AddGadgetItem(1,-1,RSet((Str((f+r)&$FFFF)),5,"0"))
  f+r
Next
SendMessage_(GadgetID(1),#WM_SETREDRAW,#True,0)



Repeat
  
  Event = WaitWindowEvent()
  gadgetID = EventGadget()
  
  If Event = #PB_Event_Gadget
    If gadgetID = 3
      Debug "I need to show at the top of ListViewGadgeget, the number found"
    EndIf
  EndIf
  
Until Event = #PB_Event_CloseWindow 
I found some related topic here:
http://www.purebasic.fr/english/viewtop ... GetItem+ID+
...but i don't know if is possible to change it to my needs (also don't know how
:( )

Thank's

Re: Search in ListViewGadget

Posted: Thu Dec 25, 2014 3:45 pm
by mk-soft

Code: Select all

OpenWindow(0, 0, 0, 300, 300,"Listview example",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ListViewGadget(1,10,10,200,250)
StringGadget(2,230,10,60,20,"",#PB_String_Numeric)
ButtonGadget(3,230,40,60,20,"SEARCH")


SendMessage_(GadgetID(1),#WM_SETREDRAW,#False,0)
For f = 0 To 60000
  r = Random(3 , 1)
  AddGadgetItem(1,-1,RSet((Str((f+r)&$FFFF)),5,"0"))
  f+r
Next
SendMessage_(GadgetID(1),#WM_SETREDRAW,#True,0)


Procedure FindLB(gadget, text.s)
  
  Protected pos
  
  pos = SendMessage_(GadgetID(gadget), #LB_FINDSTRING, -1, @text)
  If pos <> #LB_ERR
    SendMessage_(GadgetID(gadget), #LB_SETTOPINDEX, pos, 0)
  EndIf
  
EndProcedure


Repeat
  
  Event = WaitWindowEvent()
  gadgetID = EventGadget()
  
  If Event = #PB_Event_Gadget
    If gadgetID = 3
      find.s = RSet(GetGadgetText(2),5,"0")
      FindLB(1, find)
      ;Debug "I need to show at the top of ListViewGadgeget, the number found"
    EndIf
  EndIf
  
Until Event = #PB_Event_CloseWindow 
GT :D

Re: Search in ListViewGadget

Posted: Thu Dec 25, 2014 3:55 pm
by Otrebor
@mk-soft
Thank you very much !!

Re: replace ListViewGadget by ListIconGadget

Posted: Sat Dec 27, 2014 4:19 pm
by Otrebor
After read about Virtual ListIcon and assuming there is not Virtual Listview, i'm considering replace the ListViewGadget by ListIconGadget.
However i found the solution suggested by mk-soft:

Code: Select all

    pos = SendMessage_(GadgetID(ListViewGadget), #LB_FINDSTRING, -1,RSet(Str(number_to_find),5,"0" )
    If pos <> #LB_ERR
      SendMessage_(GadgetID(ListViewGadget), #LB_SETTOPINDEX, pos, 0)
    EndIf
does not work with ListIcon :(

I tried it:

Code: Select all

        SendMessage_(GadgetID(_LIG), #LVM_GETITEMPOSITION, number_to_find, pt.POINT) ; get item position 
        SendMessage_(GadgetID(_LIG), #LVM_SCROLL, pt\x, pt\y - 150)     ; scroll to item position 
but in my program, the 'number_to_find', increase a bit random (like in my first code example) and the item position returned by API, does not match with the number i want to find.

Is the only way to find an string in ListIconGadget, perform a loop and try one by one the items?

Thank's

Re: Search in ListViewGadget

Posted: Sat Dec 27, 2014 5:36 pm
by IdeasVacuum
There is another approach, only use the ListIcon for displaying the values. Store the values in a linked list/structured linked list, which is very easy and fast to sort/re-arrange - when the data edit/manipulation is done, update the ListIcon (you can actually erase the current ListIcon Items and re-list the edited data - it's fast enough in most cases).

Re: Search in ListViewGadget

Posted: Sat Dec 27, 2014 5:42 pm
by Danilo
mk-soft's code for ListIcon:

Code: Select all

OpenWindow(0, 0, 0, 300, 300,"Listview example",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ListIconGadget(1,10,10,200,250,"Item",100)
StringGadget(2,230,10,60,20,"",#PB_String_Numeric)
ButtonGadget(3,230,40,60,20,"SEARCH")


SendMessage_(GadgetID(1),#WM_SETREDRAW,#False,0)
For f = 0 To 60000
  r = Random(3 , 1)
  AddGadgetItem(1,-1,RSet((Str((f+r)&$FFFF)),5,"0"))
  f+r
Next
SendMessage_(GadgetID(1),#WM_SETREDRAW,#True,0)


Procedure FindLI(gadget, text.s)
  
  Protected pos, pt1.POINT, pt2.POINT
  Protected item.LVFINDINFO
  item\flags = #LVFI_STRING ;| #LVFI_PARTIAL
  item\psz = @text
  pos = SendMessage_(GadgetID(gadget), #LVM_FINDITEM, -1, @item)
  If pos <> -1
    SendMessage_(GadgetID(gadget), #LVM_GETITEMPOSITION, pos, @pt1)             ; get point for item we want at top
    SendMessage_(GadgetID(gadget), #LVM_GETITEMPOSITION,
                 SendMessage_(GadgetID(gadget), #LVM_GETTOPINDEX, 0, 0), @pt2)  ; get point for current item at top
    SendMessage_(GadgetID(gadget), #LVM_SCROLL, 0, -(pt2\y - pt1\y))            ; scroll difference
  EndIf
  
EndProcedure


Repeat
  
  Event = WaitWindowEvent()
  gadgetID = EventGadget()
  
  If Event = #PB_Event_Gadget
    If gadgetID = 3
      find.s = RSet(GetGadgetText(2),5,"0")
      FindLI(1, find)
      ;Debug "I need to show at the top of ListViewGadgeget, the number found"
    ElseIf gadgetID = 2 And EventType() = #PB_EventType_Change
      find.s = RSet(GetGadgetText(2),5,"0")
      FindLI(1, find)
    EndIf
  EndIf
  
Until Event = #PB_Event_CloseWindow

Re: Search in ListViewGadget

Posted: Sat Dec 27, 2014 7:39 pm
by Otrebor
@IdeasVacuum
Thank you for the suggestion!
i'm new at programming (despite of my age :wink: ) and i don't know how send data from Virtual Listicon for Linked list.

@Danilo
Your code works great!
But when i try to use with virtual ListIcon, i can not get it to work.
I did a mix using this code:
http://www.purebasic.fr/english/viewtopic.php?t=35538

Code: Select all

;... Use 1000000 items 
#ItemCount = 60000 

#LVSICF_NOINVALIDATEALL = 1 
#LVN_ODCACHEHINT = #LVN_FIRST - 13


;... Array to hold data 
Global Dim myItems.s(#ItemCount,1) 

Procedure WinCallback(hwnd, msg, wParam, lParam) 
  result = #PB_ProcessPureBasicEvents 
  Select msg 
    Case #WM_NOTIFY 
      *pnmh.NMHDR = lParam 
      Select *pnmh\code 
        Case #LVN_ODCACHEHINT 
          result = 0  
        Case #LVN_GETDISPINFO 
          *pnmlvdi.NMLVDISPINFO = lParam 
          If *pnmlvdi\item\mask & #LVIF_TEXT 
            ;... Item text is being requested 
            *pnmlvdi\item\pszText = @myItems(*pnmlvdi\item\iItem,*pnmlvdi\item\iSubItem) 
          EndIf 
          
      EndSelect 
  EndSelect 
  ProcedureReturn result 
EndProcedure 

Procedure FindLI(gadget, text.s)

  Protected pos, pt1.POINT, pt2.POINT
  Protected item.LVFINDINFO
  item\flags = #LVFI_STRING ;| #LVFI_PARTIAL
  item\psz = @text
  pos = SendMessage_(GadgetID(gadget), #LVM_FINDITEM, -1, @item)
  If pos <> -1
    SendMessage_(GadgetID(gadget), #LVM_GETITEMPOSITION, pos, @pt1)             ; get point for item we want at top
    SendMessage_(GadgetID(gadget), #LVM_GETITEMPOSITION,
                 SendMessage_(GadgetID(gadget), #LVM_GETTOPINDEX, 0, 0), @pt2)  ; get point for current item at top
    SendMessage_(GadgetID(gadget), #LVM_SCROLL, 0, -(pt2\y - pt1\y))            ; scroll difference
  EndIf
  
EndProcedure

  OpenWindow(0, 0, 0, 300, 300,"Listview example",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  SetWindowCallback(@WinCallback()) 
  StringGadget(2,230,10,60,20,"",#PB_String_Numeric)
  ButtonGadget(3,230,40,60,20,"SEARCH")
  ; left column 
 ListIconGadget(1,10,10,200,280,"ID",100,#LVS_OWNERDATA) 
  ;... Set desired number of ListIconGdaget items 
  SendMessage_(GadgetID(1), #LVM_SETITEMCOUNT, #ItemCount, #LVSICF_NOINVALIDATEALL) 
  AddGadgetColumn(1,2,"Name",10) 
  For i=0 To #ItemCount
    myItems(i,0) = RSet(Str((i)),5,"0")
    ;myItems(i,1) = ""
  Next i 

  ; Here we change the ListIcon display to large icons and show an image 
  ;SetWindowCallback(0)
  
  
  Repeat 
    
    Event = WaitWindowEvent()
    gadgetID = EventGadget()
  
    If Event = #PB_Event_Gadget
      If gadgetID = 3
        find.s = RSet(GetGadgetText(2),5,"0")
        FindLI(1, find)
        ;Debug "I need to show at the top of ListViewGadgeget, the number found"
      ElseIf gadgetID = 2 And EventType() = #PB_EventType_Change 
        find.s = RSet(GetGadgetText(2),5,"0")
        FindLI(1, find)
      EndIf
    EndIf
    
    
  Until Event = #PB_Event_CloseWindow
Can you point me where is my mistake?
Thank's

Re: Search in ListViewGadget

Posted: Sat Dec 27, 2014 10:05 pm
by Danilo
Otrebor wrote:@Danilo
Your code works great!
But when i try to use with virtual ListIcon, i can not get it to work.
With a virtual ListIcon, you need to handle the #LVN_ODFINDITEM notification within your callback.

The message #LVM_GETTOPINDEX does not work anymore with virtual ListIcon (always returns 0),
so we also need to change the scrolling stuff.

A bit complicated, but I hope it works for you:

Code: Select all

;... Use 1000000 items 
#ItemCount = 60000 

#LVSICF_NOINVALIDATEALL = 1 
#LVN_ODCACHEHINT = #LVN_FIRST - 13


;... Array to hold data 
Global Dim myItems.s(#ItemCount,1) 

Procedure WinCallback(hwnd, msg, wParam, lParam) 
  result = #PB_ProcessPureBasicEvents 
  Select msg 
    Case #WM_NOTIFY 
      *pnmh.NMHDR = lParam 
      Select *pnmh\code 
        Case #LVN_ODCACHEHINT, #LVN_ODSTATECHANGED
          result = 0
        Case #LVN_GETDISPINFO 
          *pnmlvdi.NMLVDISPINFO = lParam 
          If *pnmlvdi\item\mask & #LVIF_TEXT 
            ;... Item text is being requested 
            *pnmlvdi\item\pszText = @myItems(*pnmlvdi\item\iItem,0);*pnmlvdi\item\iSubItem) 
          EndIf 
        Case #LVN_ODFINDITEM
          result = -1
          Protected *nmlvfinditem.NMLVFINDITEM = lParam
          If *nmlvfinditem And *nmlvfinditem\lvfi\psz
            Protected text.s = PeekS(*nmlvfinditem\lvfi\psz)
            For i=0 To #ItemCount
              If myItems(i,0) = text
                result = i
                Break
              EndIf
            Next i 
          EndIf
      EndSelect
  EndSelect 
  ProcedureReturn result 
EndProcedure 

Procedure FindLI(gadget, text.s)

  Protected pos, rect.RECT
  Protected item.LVFINDINFO
  item\flags = #LVFI_STRING ;| #LVFI_PARTIAL
  item\psz = @text
  pos = SendMessage_(GadgetID(gadget), #LVM_FINDITEM, -1, @item)
  If pos <> -1
    ;SendMessage_(GadgetID(gadget), #LVM_ENSUREVISIBLE,pos,0)
    header = SendMessage_(GadgetID(gadget),#LVM_GETHEADER,0,0)                 ; get header control
    GetClientRect_(header,headerRect.RECT)                                     ; get size of header control
    SendMessage_(GadgetID(gadget), #LVM_GETITEMRECT,  0 , @rect)               ; get rect for item 0
    SendMessage_(GadgetID(gadget), #LVM_SCROLL, 0, rect\top-headerRect\bottom) ; scroll to item 0 (minus header height)
    SendMessage_(GadgetID(gadget), #LVM_GETITEMRECT, pos, @rect)               ; get rect for our item
    SendMessage_(GadgetID(gadget), #LVM_SCROLL, 0, rect\top-headerRect\bottom) ; scroll to our item (minus header height)
  EndIf
  
EndProcedure

  OpenWindow(0, 0, 0, 300, 300,"Listview example",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  SetWindowCallback(@WinCallback()) 
  StringGadget(2,230,10,60,20,"",#PB_String_Numeric)
  ButtonGadget(3,230,40,60,20,"SEARCH")
  ; left column 
 ListIconGadget(1,10,10,200,280,"ID",100,#LVS_OWNERDATA) 
  ;... Set desired number of ListIconGdaget items 
  SendMessage_(GadgetID(1), #LVM_SETITEMCOUNT, #ItemCount, #LVSICF_NOINVALIDATEALL) 
  AddGadgetColumn(1,2,"Name",10) 
  For i=0 To #ItemCount
    myItems(i,0) = RSet(Str((i)),5,"0")
    ;myItems(i,1) = ""
  Next i 

  ; Here we change the ListIcon display to large icons and show an image 
  ;SetWindowCallback(0)
  
  
  Repeat 
    
    Event = WaitWindowEvent()
    gadgetID = EventGadget()
  
    If Event = #PB_Event_Gadget
      If gadgetID = 3
        find.s = RSet(GetGadgetText(2),5,"0")
        FindLI(1, find)
        ;Debug "I need to show at the top of ListViewGadgeget, the number found"
      ElseIf gadgetID = 2 And EventType() = #PB_EventType_Change 
        find.s = RSet(GetGadgetText(2),5,"0")
        FindLI(1, find)
      EndIf
    EndIf
    
Until Event = #PB_Event_CloseWindow

Re: Search in ListViewGadget

Posted: Sun Dec 28, 2014 2:21 am
by Otrebor
Excellent Danilo !!
I adapted your code in my program and it works like a charm :)

The only small issue is when i enable Purifier.
The compiler stops with the message:"Procedure stack has been corrupted"
I'm using WinXP.

Please, let me ask one more question:
Would be possible to set the color of top line?
Seems that SetGadgetItemColor() has no effect...

Thank you!

Re: Search in ListViewGadget

Posted: Sun Dec 28, 2014 1:22 pm
by Danilo
Otrebor wrote:The only small issue is when i enable Purifier.
The compiler stops with the message:"Procedure stack has been corrupted"
I'm using WinXP.
Sorry. #LVM_GETITEMPOSITION used a POINT, and I did not change it to RECT when using #LVM_GETITEMRECT. Last code is updated.
Otrebor wrote:Please, let me ask one more question:
Would be possible to set the color of top line?
Seems that SetGadgetItemColor() has no effect...
I think you need to use custom draw and draw the item yourself.
Just add 'Case #NM_CUSTOMDRAW' to your existing code, and make it look beautiful... ;)

Code: Select all

        Case #LVN_ODFINDITEM
          ; ...
        Case #NM_CUSTOMDRAW
            Protected *cd.NMLVCUSTOMDRAW = lParam
            If *cd\nmcd\dwItemSpec = 0 ; item 0
                If *cd\nmcd\dwDrawStage = #CDDS_ITEMPREPAINT
                    SetBkColor_(*cd\nmcd\hdc,RGB(140,140,140))
                    SetBkMode_(*cd\nmcd\hdc, #OPAQUE)
                    DrawText_(*cd\nmcd\hdc,@myItems(0,0),-1,*cd\nmcd\rc,#DT_VCENTER|#DT_CENTER|#DT_END_ELLIPSIS|#DT_SINGLELINE)
                    ProcedureReturn #CDRF_SKIPDEFAULT
                EndIf
            EndIf

Re: Search in ListViewGadget and ListIconGadget

Posted: Sun Dec 28, 2014 5:21 pm
by Otrebor
Now this is...PERFECT :)
Thank you!!

Re: Search in ListViewGadget

Posted: Tue Apr 19, 2016 8:14 am
by Fangbeast
Danilo wrote:mk-soft's code for ListIcon:

Code: Select all

OpenWindow(0, 0, 0, 300, 300,"Listview example",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ListIconGadget(1,10,10,200,250,"Item",100)
StringGadget(2,230,10,60,20,"",#PB_String_Numeric)
ButtonGadget(3,230,40,60,20,"SEARCH")


SendMessage_(GadgetID(1),#WM_SETREDRAW,#False,0)
For f = 0 To 60000
  r = Random(3 , 1)
  AddGadgetItem(1,-1,RSet((Str((f+r)&$FFFF)),5,"0"))
  f+r
Next
SendMessage_(GadgetID(1),#WM_SETREDRAW,#True,0)


Procedure FindLI(gadget, text.s)
  
  Protected pos, pt1.POINT, pt2.POINT
  Protected item.LVFINDINFO
  item\flags = #LVFI_STRING ;| #LVFI_PARTIAL
  item\psz = @text
  pos = SendMessage_(GadgetID(gadget), #LVM_FINDITEM, -1, @item)
  If pos <> -1
    SendMessage_(GadgetID(gadget), #LVM_GETITEMPOSITION, pos, @pt1)             ; get point for item we want at top
    SendMessage_(GadgetID(gadget), #LVM_GETITEMPOSITION,
                 SendMessage_(GadgetID(gadget), #LVM_GETTOPINDEX, 0, 0), @pt2)  ; get point for current item at top
    SendMessage_(GadgetID(gadget), #LVM_SCROLL, 0, -(pt2\y - pt1\y))            ; scroll difference
  EndIf
  
EndProcedure


Repeat
  
  Event = WaitWindowEvent()
  gadgetID = EventGadget()
  
  If Event = #PB_Event_Gadget
    If gadgetID = 3
      find.s = RSet(GetGadgetText(2),5,"0")
      FindLI(1, find)
      ;Debug "I need to show at the top of ListViewGadgeget, the number found"
    ElseIf gadgetID = 2 And EventType() = #PB_EventType_Change
      find.s = RSet(GetGadgetText(2),5,"0")
      FindLI(1, find)
    EndIf
  EndIf
  
Until Event = #PB_Event_CloseWindow
I know this is an old post but I need it. If I enable the #LVFI_PARTIAL flag, I can get a proper partial string search.

What I don't know if I should do is, highlight ALL occurrences of the match in a ListIconGadget and let the user scroll up and down to find them or just one and then search for the next one from the last position found?

And whichever choice I use, how can I highlight just that match in the listicongadget? Callbacks I suppose?

I can do this in an EditorGadget but for what I am displaying, the formatting is all over the place.