Search in ListViewGadget and ListIconGadget

Just starting out? Need help? Post your questions and find answers here.
User avatar
Otrebor
Enthusiast
Enthusiast
Posts: 201
Joined: Mon Mar 17, 2014 1:42 pm
Location: São Paulo, Brasil
Contact:

Search in ListViewGadget and ListIconGadget

Post 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
Last edited by Otrebor on Sun Dec 28, 2014 5:22 pm, edited 1 time in total.
User avatar
mk-soft
Always Here
Always Here
Posts: 5409
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Search in ListViewGadget

Post 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
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Otrebor
Enthusiast
Enthusiast
Posts: 201
Joined: Mon Mar 17, 2014 1:42 pm
Location: São Paulo, Brasil
Contact:

Re: Search in ListViewGadget

Post by Otrebor »

@mk-soft
Thank you very much !!
User avatar
Otrebor
Enthusiast
Enthusiast
Posts: 201
Joined: Mon Mar 17, 2014 1:42 pm
Location: São Paulo, Brasil
Contact:

Re: replace ListViewGadget by ListIconGadget

Post 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
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Search in ListViewGadget

Post 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).
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Search in ListViewGadget

Post 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
User avatar
Otrebor
Enthusiast
Enthusiast
Posts: 201
Joined: Mon Mar 17, 2014 1:42 pm
Location: São Paulo, Brasil
Contact:

Re: Search in ListViewGadget

Post 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
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Search in ListViewGadget

Post 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
Last edited by Danilo on Sun Dec 28, 2014 12:26 pm, edited 1 time in total.
User avatar
Otrebor
Enthusiast
Enthusiast
Posts: 201
Joined: Mon Mar 17, 2014 1:42 pm
Location: São Paulo, Brasil
Contact:

Re: Search in ListViewGadget

Post 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!
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Search in ListViewGadget

Post 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
User avatar
Otrebor
Enthusiast
Enthusiast
Posts: 201
Joined: Mon Mar 17, 2014 1:42 pm
Location: São Paulo, Brasil
Contact:

Re: Search in ListViewGadget and ListIconGadget

Post by Otrebor »

Now this is...PERFECT :)
Thank you!!
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4749
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Search in ListViewGadget

Post 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.
Amateur Radio, D-STAR/VK3HAF
Post Reply