Procédure: Recherche dans une ListIconGadget

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
Avatar de l’utilisateur
Jacobus
Messages : 1559
Inscription : mar. 06/avr./2004 10:35
Contact :

Procédure: Recherche dans une ListIconGadget

Message par Jacobus »

:)
Salut à tous, voici une procedure que j'ai trouvé sur CodeArchives et qui
sert à faire une recherche dans une Listicongadget, ce qui est très utile.
On obtient la sélection de l'item recherché dans la liste en entrant le
nom de l'élément (ici de la colonne 1) dans un stringgadget et un bouton
qui lance la dite procédure.
Je l'utilise et ça fonctionne très bien. je suis donc satisfait de ce côté là.

Là où je ne suis pas satisfait c'est dans la compréhension de chaque ligne
de cette procédure et c'est pourquoi je me suis dit que quelqu'un pourrait
m'en expliquer, en détails, à quoi correspondent chaque terme.
Cela servirait, j'en suis sûr, à plusieurs d'entre nous. cela ferait une
sorte de Tuto sur ce bout de code qui utilise plusieurs fonctions intéressantes.


Voici donc la procédure dans sa forme d'usage :

Code : Tout sélectionner

Procedure FindStringLIG(searchString1.s) 
  SendMessage_(GadgetID(#ListIcon_P1), #LVM_FIRST + $54, $8, 1) 
  fItem.LV_FINDINFO 
  fItem\flags   = #LVFI_STRING 
  fItem\psz     = @searchString1 
  itemNumber = SendMessage_(GadgetID(#ListIcon_P1), #LVM_FINDITEM, -1, fItem)   
  If itemNumber > -1 
    Goto SCROLL_AND_SELECT 
  Else 
    For i = 0 To CountGadgetItems(#ListIcon_P1) - 1 
      If searchString1 = GetGadgetItemText(#ListIcon_P1, i, 1)
        itemNumber = i       
        Goto SCROLL_AND_SELECT         
      EndIf 
    Next 
  EndIf   
  Goto PROC_ERROR 
  SCROLL_AND_SELECT: 
    pItem.POINT 
    SendMessage_(GadgetID(#ListIcon_P1), #LVM_GETITEMPOSITION, itemNumber , pItem) 
    SendMessage_(GadgetID(#ListIcon_P1), #LVM_SCROLL, pItem\x, pItem\y - 150)
    sItem.LV_ITEM 
    sItem\mask      = #LVIF_STATE 
    sItem\state     = #LVIS_SELECTED 
    sItem\stateMask = #LVIS_SELECTED 
    SendMessage_(GadgetID(#ListIcon_P1), #LVM_SETITEMSTATE, itemNumber , sItem)     
    Goto PROC_END   
  PROC_ERROR: 
    MessageRequester("ERREUR", "L'élément recherché : " + searchString1 + "  n'a pas été trouvé", #MB_ICONEXCLAMATION | #MB_OK)  
  PROC_END: 
EndProcedure 
Et maintenant décomposée avec des lignes commentées de ce que je comprend ou que je crois comprendre, et des commentaires sur ce que je ne pige pas.

Code : Tout sélectionner

Procedure FindStringLIG(searchString1.s) 
 ;L'API suivante envoie un message à la ListIcon_P1, mais que représentent
 ;la constante: #LVM_FIRST  et les valeurs : + $54, $8, 1
  SendMessage_(GadgetID(#ListIcon_P1), #LVM_FIRST + $54, $8, 1) 
  
  fItem.LV_FINDINFO ; find item par ses infos 
  fItem\flags   = #LVFI_STRING; find item en fonction du contenu du stringgadget  
  fItem\psz     = @searchString1; find item en fonction de sa position 
  itemNumber = SendMessage_(GadgetID(#ListIcon_P1), #LVM_FINDITEM, -1, fItem) ; Renvoie le n° de l'item trouvé  
  If itemNumber > -1 ;si l'item est supérieur à -1,
    Goto SCROLL_AND_SELECT ;on passe directement à cette procédure (voir plus bas) 
  Else ;sinon
  ;on compte le nombre d'items de la ListIcon
  ;on recherche l'item demandé dans la colonne n°1...
    For i = 0 To CountGadgetItems(#ListIcon_P1) - 1 
      If searchString1 = GetGadgetItemText(#ListIcon_P1, i, 1);s'il a été trouvé 
        itemNumber = i ; le n° de l'item devient celui recherché      
        Goto SCROLL_AND_SELECT; on se rend à l'endroit où se situe l'item         
      EndIf 
    Next 
  EndIf   
  Goto PROC_ERROR ;Si l'item n'a pas été trouvé, activation de la procédure d'erreur
  
  SCROLL_AND_SELECT: ;Label (Peut on donner un autre nom, genre PositionAndSelection ?)
    pItem.POINT ;PositionItem.Point ?
    SendMessage_(GadgetID(#ListIcon_P1), #LVM_GETITEMPOSITION, itemNumber , pItem) 
    ;Ci-dessus:Renvoi la position de l'item en fonction de son numéro 
    SendMessage_(GadgetID(#ListIcon_P1), #LVM_SCROLL, pItem\x, pItem\y - 150)
    ;Ci-dessus:déplacement jusqu'à la position de l'item 
    sItem.LV_ITEM                   ;besoin de lumière...ma chandelle est morte
    sItem\mask      = #LVIF_STATE   ;besoin de lumière...
    sItem\state     = #LVIS_SELECTED ;besoin de lumière...
    sItem\stateMask = #LVIS_SELECTED ;besoin de lumière...
    SendMessage_(GadgetID(#ListIcon_P1), #LVM_SETITEMSTATE, itemNumber , sItem);Sélection de l'entête de l'item ?     
    Goto PROC_END ;Fin de la procédure  
  PROC_ERROR: ;procédure d'erreur si item pas trouvé (voir plus haut)
    MessageRequester("ERREUR", "L'élément recherché : " + searchString1 + "  n'a pas été trouvé", #MB_ICONEXCLAMATION | #MB_OK)  
  PROC_END: 
EndProcedure 
Voilà où j'en suis avec cette procédure.
Dans l'aide, il est dit qu'un code bien fait ne devrait pas utiliser
de GOTO. j'en conclu que celui-ci n'est pas bien fait... mais il fonctionne.

J'imagine que pour certains c'est de la rigolade...Ben j'aimerai bien rigoler
avec eux, c'est pourquoi vos lumières seront les bienvenues.

@+ Jacobus
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Pourquoi tu n'essaye pas d'écrire TA procedure de recherche, les commandes PB sans API permettent de le faire.
nico
Messages : 3702
Inscription : ven. 13/févr./2004 0:57

Message par nico »

Non Denis, ce n'est pas possible sans API car le code permet non seulement de trouver l'item recherché mais de scroller la listicon pour que l'item trouvé apparaisse toujours dans la zone affichable.

On a l'impression qu'il y a un doublon dans la procédure de recherche, mais en fait l'api ne permettrais pas de faire les recherches sur toutes les colonnes de la listicon.

J'ai écrit un code qui fait la même chose mais qui est légèrement différent:
http://purebasic.hmt-forum.com/viewtopic.php?t=298

ça peut t'intéressé.


Sinon, on peut facilement supprimer les labels du code, je verrais plus tard pour ta demande d'explication.

:)
Avatar de l’utilisateur
Jacobus
Messages : 1559
Inscription : mar. 06/avr./2004 10:35
Contact :

Message par Jacobus »

:D Merci Nico,
C'est tout à fait ça, l'intérêt réside dans le positionnement et la sélection de l'item recherché rapidement.

Il me semble qu'en virant les Goto et Labels on obtient la même chose en appliquant les procedures qu'ils amènent à l'endroit voulu.

Le saut fait par le Goto évite la répétition des procédures internes SCROLL_AND_SELECT et PROC_ERROR , c'est comme ça que je le perçois.

@Denis: Si c'est possible, je vais essayer de faire Ma procédure... si c'est pas possible, ben je suis pas à une connerie près. :roll:
Un petit mot concernant tes librairies de colorisation de gadgets. Elles fonctionnent au poil même sur mon système à manivelle... depuis PB3.92
Merci pour ce beau boulot.
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

T'as raison Nico, je ne pensais pas que l'intérêt était dans le positionnement.

Moi j'utilise le message suivant pour forcer l'élément à être affiché, ça marche bien.

SendMessage_(handle, #LVM_ENSUREVISIBLE, index, 0)
Avatar de l’utilisateur
Chris
Messages : 3731
Inscription : sam. 24/janv./2004 14:54
Contact :

Message par Chris »

Voilà, moi, comment je fais.
Par contre, est-ce que c'est rapide???

Code : Tout sélectionner

#MyWindow = 0
#ListIcon_P1 = 0

Procedure FindStringLIG(searchString1.s,NbColumns)
  For Colonne = 0 To NbColumns -1
    For Ligne = 0 To CountGadgetItems(#ListIcon_P1)
      If GetGadgetItemText(#ListIcon_P1,Ligne,Colonne) = searchString1
        SetGadgetItemState(#ListIcon_P1,Ligne,#PB_ListIcon_Selected)
        SendMessage_(GadgetID(#ListIcon_P1),#LVM_ENSUREVISIBLE,Ligne,#True)
        Break 2
      EndIf
    Next
  Next
EndProcedure 

If OpenWindow(#MyWindow,100,100,300,100,#PB_Window_SystemMenu|#PB_Window_ScreenCentered,"ListIcon") 
  If CreateGadgetList(WindowID()) 
    ListIconGadget(#ListIcon_P1,5,5,290,90,"Colonne 1",100,#PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection) 
    AddGadgetColumn(#ListIcon_P1,1,"Colonne 2",250) 
    
    For i = 0 To 10
      AddGadgetItem(#ListIcon_P1,-1,"Ligne "+Str(i)+Chr(10)+"Ligne "+Str((i+10)*10))
    Next
    
    FindStringLIG("Ligne 160",2)
    
    Repeat 
      EventID = WaitWindowEvent() 
    Until EventID = #PB_Event_CloseWindow And EventWindowID() = #MyWindow 
  EndIf 
EndIf
nico
Messages : 3702
Inscription : ven. 13/févr./2004 0:57

Message par nico »

Code : Tout sélectionner

  ; La Structure LV_ITEM permet de consulter ou de modifier toutes 
  ; les informations utiles sur un item choisi.
  sItem.LV_ITEM
  
  ; Dans cette structure, toutes les variables sont optionnelles
  ; et c'est la variable mask qui permet de valider les variables 
  ; sur lesquelles on souhaite travailler
  
  ; on affecte la constante #LVIF_STATE à mask indiquant ici que
  ; l'on souhaite travailler avec la variables state.
  sItem\mask      = #LVIF_STATE   
   
  ; mais la variable state elle même contient plusieurs informations
  ; et c'est la variable stateMask qui permet de choisir les informations
  ; sur lesquelles on désire travailler.
  
  ; ici on choisit sur l'état sélectionné de l'item
  sItem\stateMask = #LVIS_SELECTED 
  
  ; ici on affecte la constante #LVIS_SELECTED à la constante state
  ; car on ne veut pas obtenir une information mais imposer cet état
  ; à l'item passé au Sendmessage.
  sItem\state     = #LVIS_SELECTED
  
  ;Sélection de l'item grâce à la structure LV_ITEM passé en argument.
  SendMessage_(GadgetID(#ListIcon_P1), #LVM_SETITEMSTATE, itemNumber , sItem)
  
  ; ça parait compliqué, mais une visite sur MSDN permet
  ; d'avoir une vue d'ensemble complète.
  
  ; Il y a plus simple:
  SetGadgetItemState(#ListIcon_P1, itemNumber, 1) 
  

  
  ; De même pour scroller j'utilisais ce code
  
  ;Message pour récupérer la hauteur d'un item 
  SendMessage_(handle, #LVM_GETITEMRECT, 0, @prc.RECT) 
  ;Message pour scroller tout au début (item*hauteur) 
  SendMessage_(handle, #LVM_SCROLL, 0, -NbItem*(prc\bottom-prc\top)) 
  ;Message pour scroller sur l'élément recherché 
  SendMessage_(handle, #LVM_SCROLL, 0, index*(prc\bottom-prc\top)) 
  
  ; et Denis utilise un code plus simple
  SendMessage_(handle, #LVM_ENSUREVISIBLE, index, 0)
  
  
  ; Tous ces codes sont bons, certains permettant un control plus grand.
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Nico,

un certain nombre de structures ont évolué avec les versions de Windows.

LV_Item est fausse si tu tourne avec XP et tu risque d'avoir des erreurs (plantage, hein Régis, tu te souviens ?)

voici ce que dit la doc MS

LVITEM Structure

--------------------------------------------------------------------------------

Specifies or receives the attributes of a list-view item. This structure has been updated to support a new mask value (LVIF_INDENT) that enables item indenting. This structure supersedes the LV_ITEM structure.
Donc la structure LVITEM remplace LV_ITEM et est définie comme ceci

Code : Tout sélectionner

typedef struct _LVITEM { 
    UINT mask; 
    int iItem; 
    int iSubItem; 
    UINT state; 
    UINT stateMask; 
    LPTSTR pszText; 
    int cchTextMax; 
    int iImage; 
    LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
    int iIndent;
#endif
#if (_WIN32_IE >= 0x560)
    int iGroupId;
    UINT cColumns; // tile view columns
    PUINT puColumns;
#endif
} LVITEM, *LPLVITEM; 

que l'on traduit en PB comme ceci

Code : Tout sélectionner

Structure LVITEM 
   Mask.l
   iItem.l
   iSubItem.l
   State.l
   stateMask.l
   pszText.l
   cchTextMax.l
   iImage.l
   lParam.l
   iIndent.l
   iGroupId.l
   cColumns.l
   puColumns.l
EndStructure

il faut utiliser ces nouvelles structures car sinon il y a un risque d'écrassement de l'adresse de retour sur la pile par les valeurs de la structure donc la taille est de 52 au lieu de 36 pour LV_ITEM.

D'autres structures ont changé, il vaut mieux utiliser les dernières avec tous les OS, ca évitera les plantages.
Dernière modification par Anonyme2 le mar. 23/nov./2004 20:09, modifié 1 fois.
nico
Messages : 3702
Inscription : ven. 13/févr./2004 0:57

Message par nico »

Oui c'est vrai, tu fais bien de le spécifier!

:)
Le Soldat Inconnu
Messages : 4312
Inscription : mer. 28/janv./2004 20:58
Localisation : Clermont ferrand OU Olsztyn
Contact :

Message par Le Soldat Inconnu »

Moi pour les recherche, je fais tous en PB et j'utilise simplement les fonction SetListPos et GetListPos de Denis pour placer la liste au bon endroit (voir sur 2 dev pour ces 2 procedures)

Code : Tout sélectionner

; Auteur : Le Soldat Inconnu
; Version de PB : 3.92
;
; Explication du programme :
; Faire une recherche dans un ListIconGadget

Enumeration
  ; Menu
  #Menu_Quitter
  #Menu_Rechercher
  #Menu_Suivant

  ; Gadgets
  #Liste
EndEnumeration

#WinX = 200
#WinY = 300

Global Recherche.s
PositionListe = -1

Procedure.l GetListPos(Gadget.l)
  ProcedureReturn SendMessage_(GadgetID(Gadget), #LVM_GETTOPINDEX, 0, 0)
EndProcedure

Procedure.l SetListPos(Gadget.l, Position.l)
  Protected Pos.POINT
  SendMessage_(GadgetID(Gadget), #LVM_GETITEMPOSITION, Position - 1, Pos)
  SendMessage_(GadgetID(Gadget), #LVM_SCROLL, 0, Pos\y)
EndProcedure

Procedure.l RechercheSuivant()
  Protected Txt.s, Txt_Longueur.l, Valeur.s, Pos
  
  Pos = GetGadgetState(#Liste) ; On récupère la position sélectionné dans la liste
  
  Repeat
    For n = Pos + 1 To CountGadgetItems(#Liste) ; De la position actuelle + 1 (car on ne fait pas la recherche sur l'élément sélectionné mais sur les suivants) jusqu'à la fin de la liste
    
      Txt.s = LCase(GetGadgetItemText(#Liste, n, 0)) ; On récypère le texte de la ListIconGadget
      
      If Recherche = Txt ; Si le texte correspond à la recherche
        SetGadgetState(#Liste, n) ; On sélectionne l'élément
        SetListPos(#Liste, n) ; On déplace la liste pour voir l'élément
        ProcedureReturn 1
      EndIf
      
    Next
    
    Pos = -1 ; On se place au début de la liste
  Until MessageRequester("Recherche", "Fin de fichier atteinte." + Chr(10) + "Reprendre la recherche à partir du début du fichier ?", 4) = 7
  ; On continue tant que l'utilisateur dit de continuer la recherche du début
  ProcedureReturn 0
  
EndProcedure

Procedure Recherche()
  Txt.s = InputRequester("Rechercher", "Entrer le texte recherché  :", Recherche) ; On demande le texte recherché
  If Txt
    Recherche = LCase(Txt)
    RechercheSuivant()
  EndIf
EndProcedure




;/ Début du programme

;{ Création de la fenêtre et de la GadgetList
If OpenWindow(0, 0, 0, #WinX, #WinY, #PB_Window_SystemMenu | #PB_Window_ScreenCentered, "Recherche") = 0 Or CreateGadgetList(WindowID()) = 0
  End
EndIf
;}

;{- Menu
If CreateMenu(0, WindowID())
  MenuTitle("Fichier")
  MenuItem(#Menu_Quitter, "Quitter" + Chr(9) + "Alt+F4")
  
  MenuTitle("Edition")
  MenuItem(#Menu_Rechercher, "Rechercher" + Chr(9) + "Ctrl+F")
  MenuItem(#Menu_Suivant, "Suivant" + Chr(9) + "F3")
  
  AddKeyboardShortcut(0, #PB_Shortcut_F3, #Menu_Suivant)
  AddKeyboardShortcut(0, #PB_Shortcut_Control | #PB_Shortcut_F, #Menu_Rechercher)
EndIf
;}

;{- Gadgets
ListIconGadget(#Liste, 1, 1, #WinX - 2, #WinY - 2 - MenuHeight(), "Texte", 100, #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
; On rempli le listiconGadget avec du texte
For n = 97 To 122
  AddGadgetItem(#Liste, -1, Chr(n))
Next
For n = 65 To 90
  AddGadgetItem(#Liste, -1, Chr(n))
Next
;}

Repeat
  Event = WaitWindowEvent()
  
  If Event = #PB_EventMenu
    Select EventMenuID() ; Menu
      Case #Menu_Rechercher
        Recherche() ; on fait une nouvelle recherche
      Case #Menu_Suivant
        RechercheSuivant() ; On recherche le prochain texte correspondant à votre recherche
      Case #Menu_Quitter
        Event = #PB_EventCloseWindow ; On quitte
    EndSelect
  EndIf
  
Until Event = #PB_EventCloseWindow

End
Et voilà une belle recherche presque sans API et très simple :D
Je ne suis pas à moitié Polonais mais ma moitié est polonaise ... Vous avez suivi ?

[Intel quad core Q9400 2.66mhz, ATI 4870, 4Go Ram, XP (x86) / 7 (x64)]
Avatar de l’utilisateur
Jacobus
Messages : 1559
Inscription : mar. 06/avr./2004 10:35
Contact :

Message par Jacobus »

:D , Ca au moins c'est de l'info comme on en a envie...
Merci Nico d'avoir pris le temps de me donner ces explications,
C'est très clair. J'irai sans doute faire un tour sur MSDN pour essayer d'approfondir le sujet des structures complexes.

@Chris, ta méthode fonctionne parfaitement et aussi rapidement, et en plus elle a un gros avantage...elle est beaucoup moins lourde, ce qui ne gâche rien.

Bref, encore merci à vous
@+ Jacobus :wink:
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
Avatar de l’utilisateur
Jacobus
Messages : 1559
Inscription : mar. 06/avr./2004 10:35
Contact :

Message par Jacobus »

Argh ! Juste quand je postais ma réponse,
tu envoyais la tienne Régis, :D
je vais donc également la tester dès que possible.
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
Le Soldat Inconnu
Messages : 4312
Inscription : mer. 28/janv./2004 20:58
Localisation : Clermont ferrand OU Olsztyn
Contact :

Message par Le Soldat Inconnu »

Oui, j'ai mis du temps à simplifier un gros code pour en extraire juste la recherche, commenter le bazard, enfin tous ça, j'espère que ça va te plaire :D
Je ne suis pas à moitié Polonais mais ma moitié est polonaise ... Vous avez suivi ?

[Intel quad core Q9400 2.66mhz, ATI 4870, 4Go Ram, XP (x86) / 7 (x64)]
nico
Messages : 3702
Inscription : ven. 13/févr./2004 0:57

Message par nico »

A partir du moment où tu utilises, ne serait-ce qu'une API, je vois pas l'intérêt de se limiter!

Mais bon chacun son truc.

:)
Avatar de l’utilisateur
Chris
Messages : 3731
Inscription : sam. 24/janv./2004 14:54
Contact :

Message par Chris »

Jacobus a écrit :@Chris, ta méthode fonctionne parfaitement et aussi rapidement, et en plus elle a un gros avantage...elle est beaucoup moins lourde, ce qui ne gâche rien.
Pour la vitesse, je ne sais pas, il faudrait faire des tests avec beaucoup de données.

Le problème de ce code, telle qu'il est, c'est qu'il faut connaître le nombre de colonnes de la liste.
On peut facilement remédier à ça en modifiant la procédure comme ça:

Code : Tout sélectionner

Procedure FindStringLIG(searchString1.s)
  hHeader = SendMessage_(GadgetID(#ListIcon_P1), #LVM_GETHEADER,0,0)
  NbColumns = SendMessage_(hHeader,#HDM_GETITEMCOUNT,0,0)
  
  For Colonne = 0 To NbColumns -1 
    For Ligne = 0 To CountGadgetItems(#ListIcon_P1) 
      If GetGadgetItemText(#ListIcon_P1,Ligne,Colonne) = searchString1 
        SetGadgetItemState(#ListIcon_P1,Ligne,#PB_ListIcon_Selected) 
        SendMessage_(GadgetID(#ListIcon_P1),#LVM_ENSUREVISIBLE,Ligne,#True) 
        Break 2 
      EndIf 
    Next 
  Next 
EndProcedure
L'appel de la procédure ne demande plus qu'un paramètre, la chaine à rechercher:

Code : Tout sélectionner

    FindStringLIG("Ligne 160")
Répondre