Télécharger le .dir correspondant à cette étape
(bouton droit -> enregistrer la cible)


Quatrième étape :

Maintenant que notre ascenseur est fonctionnel, et que nous savons que nous allons pouvoir le réutiliser comme bon nous semblera, nous allons implémenter le moteur de recherche dans le texte.

Cette fonction de recherche sera semblable, à quelques détails près, à n'importe quelle commande "rechercher" figurant dans tous les menus "Edition" des applications commerciales courantes que nous avons l'habitude d'utiliser (traitement de texte, tableur, Director lui-même etc...).

Voici quel est son cahier des charges :
- l'utilisateur saisit une chaîne de caractères dans le champ qui lui est offert, et que nous avons paramétré à "modifiable" (propriété "editable" de l'acteur champ en lingo), au début de l'exercice. Je rappelle que le nom de cet acteur champ est "cherche".
- lorsqu'une chaîne a été saisie, le bouton permettant de lancer la recherche devient actif (on peut toujours appuyer sur le bouton quand on n'a rien saisi, mais d'une part le bouton ne se met pas en surbrillance, et d'autre part, il ne se passe rien...),
- L'appui sur le bouton déclenche la recherche : toutes les occurrences de la chaîne cherchée se mettent en évidence (en rouge et en gras),
- dans un autre champ, situé en dessous du texte à analyser, et que nous avons appelé "resultat", le moteur indique le nombre d'occurrences trouvées de la chaîne recherchée.

Puisque c'est l'appui sur le bouton qui va déclencher le processus de recherche, nous allons nécessairement trouver un gestionnaire on mouseUp posé sur le bouton.
Puisque nous avons spécifié qu'on ne pouvait lancer une recherche si on n'avait rien saisi, nous allons également trouver, dans ce mouseUp, une condition qui va vérifier que le champ de recherche n'est pas vide.

Nous pouvons donc écrire :
quand on clique sur le bouton
si le champ "cherche" n'est pas vide alors
faire la recherche
fin si
fin

Que va-t-on trouver dans la proposition exprimée ci-dessus par "faire la recherche" ?
Pour pouvoir faire cette recherche (et nous allons voir comment tout de suite après), il faut que nous sachions :
- dans quel texte chercher,
- quoi chercher.

Toujours dans le souci de pouvoir réutiliser notre fonction de recherche pour d'autres cas, nous nous abstiendrons de développer tout le mécanisme du moteur de recherche dans le mouseUp. En effet, si nous voulions réutiliser notre traitement ailleurs, il nous faudrait alors réécrire (ou recopier, ce qui revient au même) tout le code correspondant (et en cas de modification, il faudrait alors reporter cette modification dans toutes les occurrences de ce bloc de code qui figurerait dans notre application). - Je l'ai déjà dit dans ce cours, mais on ne le répétera jamais assez - .
Nous allons donc écrire une fonction (qui dans le cas présent, puisqu'elle se trouve dans un script de sprite, est équivalente à une méthode : la terminologie dépend essentiellement de l'endroit où se trouve le bloc de code, et de l'objet auquel il s'applique).
Pour que la fonction sache quoi faire, il va falloir lui passer des "arguments", lesquels ne sont rien d'autre que des paramètres dont la fonction va avoir besoin pour effectuer la tâche qu'on va lui assigner. On peut remplacer arguments, ou paramètres, par "des renseignements dont la fonction va avoir besoin pour faire ce qu'on lui demande".
Dans l'exercice, j'ai appelé cette fonction "chercherTexte()". Et comme nous lui passons deux arguments, nous écrirons, d'un point de vue de la syntaxe : chercherTexte(uneChaîne, unTexte).
C'est volontairement que leTexte est devenu unTexte, et que laChaîne est devenu uneChaîne. En effet, la manière d'exprimer ces arguments indique à la fonction qu'elle doit attendre deux chaînes de caractères. Au moment de l'appeler effectivement pour lui faire faire sa tâche, nous remplacerons uneChaîne et unTexte, par laChaîne et leTexte qui nous intéressent. En attendant, la fonction, elle, sera écrite avec les noms génériques uneChaîne et unTexte, qui peuvent être considérés comme des variables locales de la fonction. Elle seront remplacées, au moment de l'appel de la fonction, par les valeurs que nous lui passerons effectivement.

Dans le cas présent, comme nous l'avons relevé précédemment, la fonction a besoin de savoir quoi chercher dans quel texte. Nous allons donc lui passer ces deux renseignements en arguments. Pour pouvoir les lui passer, il va falloir les stocker, dans le mouseUp, dans deux variables, qui seront locales ici, puis leur survie sera assurée par leur passage à la fonction sous forme d'argument.
Nous écrirons donc :
quand on clique sur le bouton
si le champ "cherche" n'est pas vide alors
lire le contenu du texte dans lequel rechercher et le stocker dans une variable appelée leTexte
lire le contenu de la chaîne à rechercher dans le champ "cherche" et le stocker dans une variable appelée laChaîne
vider le champ "résultat"
faire appel à chercherTexte en utilisant laChaîne et leTexte comme arguments
fin si
fin

Nous devons aussi prévoir le cas où l'utilisateur désirerait faire plusieurs recherches consécutives, et dans ce cas, à chaque appui sur le bouton, nous devons vider le champ résultat pour faire place nette à une autre indication de résultat. C'est pourquoi nous avons ajouté la troisième ligne contenue dans le bloc si... fin si.
NB : pour vider un acteur champ texte, il suffit de le remplir avec une chaîne vide (qu'on exprime par deux guillemets doubles sans rien entre les deux : ""), c'est à dire de lui fixer sa propriété text à "". Attention : on ne peut pas régler des caractéristiques de police (type, taille, style, couleur...) si le champ contient une chaîne vide. Si l'on doit fixer ces attributs alors que le champ doit apparaître vide à l'écran, il faut le remplir avec un simple espace (invisible pour l'utilisateur, mais faisant que le champ contient malgré tout quelque chose). On fixe alors la propriété text à " " (au lieu de "").

Jusque là, cela ne pose aucun problème de syntaxe et nous pourrons coder :
on mouseUp me
if member("cherche").text <> "" then
laChaîne = member("cherche").text
leTexte = sprite(spriteSource).member.text
member("resultat").text = ""
chercherTexte(laChaîne, leTexte)
end if
end

et puisque nous faisons appel à une fonction, nous devons l'écrire immédiatement, en la laissant vide pour l'instant. Mais, pour la cohérence de l'architecture de notre application, nous écrirons, pour l'instant, immédiatement après le mouseUp :

on chercherTexte (uneChaîne, unTexte)

end

Nous allons maintenant nous intéresser à ce que nous allons mettre dans notre fonction chercherTexte().

Le principe de la recherche sera le suivant : nous allons parcourir, par groupe de caractères, tout le texte dans lequel nous cherchons, groupes du même nombre de caractères que la sous-chaîne que nous cherchons. Si nous rencontrons un groupe de caractères qui est le même que celui que nous cherchons, alors nous relèverons son index (voir ci-dessous) de début dans une liste qui sera chargée de stocker les résultats.

Considérons la figure ci-dessous :

Elle schématise le début d'un texte, lequel est une chaîne. Chaque caractère est symbolisé par les tirets. En dessous de chaque tiret, on a inscrit le numéro d'ordre du caractère dans la chaîne. En effet, une chaîne de caractères peut-être assimilée à une liste dont chaque caractère porte un numéro d'ordre, qu'on appelle aussi un index (*).
A titre d'exemple, dans le schéma ci-dessus, on a supposé qu'on cherchait une sous-chaîne de 5 caractères dans le texte source. Nous allons donc lire le texte source par groupes de 5 caractères, en glissant ; c'est à dire que nous allons regarder les caractères 1 à 5 du texte source, puis 2 à 6, puis 3 à 7, etc.
A chaque fois que nous aurons extrait un groupe de 5 caractères du texte source, nous le comparerons avec la sous-chaîne recherchée. Si les deux sont différents, on passe au groupe suivant, et ainsi de suite.
Si les deux sont identiques, alors il nous suffira de retenir, en le stockant dans une liste (appelée dans l'exercice "lstTrouvailles"), l'index du premier caractère du groupe.
Lorsqu'il s'agira de mettre ces groupes en évidence (en rouge et en gras dans l'exercice) dans le corps du texte, l'index du premier caractère du groupe suffit puisque nous en connaissons la longueur en terme de nombre de caractères.

Puisque nous allons lire notre texte source, par groupe de n caractères en se décalant à chaque fois de une unité, nous pouvons d'ores et déjà imaginer que nous allons avoir recours à au moins une boucle de répétition.
Jusqu'où aller dans le texte ? Il est inutile de le lire jusqu'au dernier caractère puisque lorsque le réliquat restant à examiner sera plus court que la sous-chaîne à chercher, nous sommes sûrs d'avoir une inégalité. Si nous cherchons une sous-chaîne de n caractères, nous pourrons nous arrêter lorsque nous aurons examiné les n derniers caractères du texte source. C'est à dire la longueur du texte source moins la longueur de la sous-chaîne recherchée diminuée de 1 caractère (si nous cherchons une sous-chaîne de 5 caractères, nous aurons examiné les 5 derniers quand il en restera 4 après lui).

D'autre part, puisqu'à chaque fois qu'on se positionne sur un caractère du texte source, il va falloir examiner ce caractère concaténé avec les n-1 suivants (en effet, pour examiner n caractères, une fois qu'on a relevé le premier du groupe, il faut en relever les n-1 suivants de manière à avoir en tout n caractères), on peut également imaginer que nous aurons, pour compter ces n-1 caractères, une autre boucle de répétition imbriquée dans la première.
Pour ce faire, nous allons devoir disposer d'une variable locale. En effet, nous lisons les caractères un par un. Pour pouvoir les comparer à la sous-chaîne, il va falloir les concaténer, et il nous faut donc un conteneur pour stocker le résultat de cette opération. Nous avons appelé cette variable locale aComparer, et nous la déclarons comme une chaîne vide AVANT d'entrer dans la deuxième boucle de répétition. En effet, c'est une fois qu'on s'est positionné sur un caractère du texte source, qu'on va constituer la chaîne qui devra être comparée à la sous-chaîne recherchée. Nous n'aurons pas à nous préoccuper de réinitialiser cette variable : c'est une variable locale qui va donc disparaître à la fin de la fonction, et qui sera recréée à l'exécution suivante de la fonction.

NB : En réalité, Lingo offre des fonctions qui simplifieraient ce traitement. Il est possible effectivement de demander directement d'extraire les x caractères situés entre deux index. La syntaxe de cette instruction est member("leTexte").text.char[index1..index2]index1 est l'index du premier caractère de la sous-chaîne à extraire et index2 l'index du dernier. Attention : ce n'est pas une faute de frappe, la syntaxe exige bien deux points ( .. ) entre index1 et index2 ; ce ne sont pas des points de suspension.
J'ai choisi de ne pas les utiliser ici, car ce sont des instructions propres à lingo, qui, en fait, font en arrière plan le traitement que nous implémentons, nous, explicitement.

Nous allons donc pouvoir écrire, en pseudo-code :

on chercherTexte(uneChaîne, unTexte)
lire le nombre total de caractère du texte source et le nommer nbCharTotal
lire le nombre de caractères de la sous-chaîne à chercher et le nommer nbCaract

initialiser une liste vide (pour l'instant, mais qui stockera les index) et la nommer lstTrouvailles

répéter avec un compteur i de 1 jusqu'à nbCharTotal - (nbCaract - 1)
aComparer = ""
répéter avec un compteur j de 1 jusqu'à nbCaract
aComparer = aComparer auquel on ajoute le caractère (i+(j-1)) de unTexte
fin répéter

si la chaîne aComparer = uneChaîne alors
ajouter à lstTrouvaille l'index i
fin si

fin répéter

end

Deux remarques :

- Pourquoi ce (i+(j-1)) ?
i
prend successivement les valeurs 1, puis 2, puis 3, etc.
j également.
Au début de la seconde boucle de répétition, le compteur i, qu'on peut aussi considérer comme un pointeur dans le texte source, est positionné sur un caractère donné du texte source. Mais la sous-chaîne aComparer, elle, est vide.
Il nous faut donc lui ajouter les caractères i+0, i+1, i+2... puisque le caractère numéro i fait partie du groupe que l'on va comparer.
j démarrant à 1, c'est donc bien j-1 qu'il faut ajouter à i pour obtenir les caractères qui nous intéressent.
Noter qu'on aurait pu initialiser le compteur j à 0. Il aurait alors fallu le faire compter jusqu'a nbCaract - 1 et l'expression du caractère aurait été i + j.
D'une manière générale, il est important de bien maîtriser ces comptages, en fonction des origines qu'on se choisit, et la meilleure manière de faire est de faire de petits schémas sur papier, en prenant des valeurs numériques concrètes avant de généraliser.
A noter d'ailleurs également que dans un texte, en Lingo, le premier caractère de la chaîne porte l'index 1 (et non pas 0 comme dans d'autres langages).

- Dans l'expression "aComparer = aComparer auquel on ajoute le caractère (i+(j-1)) de unTexte", on rencontre une syntaxe que nous avons déjà rencontrée pour les valeurs numériques. Le signe = est ici l'opérateur d'affectation et non pas l'opérateur de comparaison. Quand nous écrivions, dans l'exercice des balles : sprite(n).locH = sprite(n).locH +1, nous avions dit que nous lisions, à droite du signe =, la valeur actuelle du locH, que nous lui ajoutions 1, puis que nous réinjections cette nouvelle valeur dans le locH.
C'est ici exactement la même chose : nous lisons, à droite du signe =, la valeur courante de la variable aComparer (qui est une chaîne), nous lui ajoutons un caractère, et nous réinjectons l'ensemble dans la variable aComparer, qui prend ainsi une nouvelle valeur. En Lingo, l'opérateur de concaténation est "&" (comme en Visual Basic ; en javascript ou ActionScript, c'est le signe "+", l'ambiguïté étant levée par le contexte).
A la sortie de la boucle de répétition, aComparer contient une chaîne de longueur nbCaract.

Nous allons maintenant pouvoir coder :
on chercherTexte(uneChaîne, unTexte) me

nbCaract = uneChaîne.length
nbCharTotal = unTexte.length
lstTrouvailles = []

repeat with i = 1 to nbCharTotal - (nbCaract - 1)
aComparer = ""
repeat with j = 1 to nbCaract
aComparer = aComparer&(char(i+(j-1)) of unTexte)
end repeat
if aComparer = uneChaîne then
lstTrouvailles.add(i)
end if
end repeat

end

Il ne nous reste plus qu'à mettre en surbrillance (ici en rouge et en gras), toutes les sous-chaînes qui ont été trouvées, et afficher le résultat dans le champ "resultat".

Comment faire ?
Nous disposons maintenant d'une liste lstTrouvailles qui contient tous les index des premiers caractères des occurrences de la sous-chaîne trouvée. Il va donc nous falloir parcourir cette liste, et pour la parcourir, nous allons à nouveau utiliser une boucle de répétition.
Comme nous connaissons la longueur de la sous-chaîne à mettre en évidence, nous allons mettre les caractères qui la composent en évidence un par un, et ceci avec une autre boucle de répétition identique à celle qui nous permettait de constituer la variable aComparer dans la section précédente de la fonction.
NB : On peut faire ici la même remarque que plus haut, à savoir qu'il existe, en lingo, des commandes qui nous permettraient de changer la couleur et le style de blocs entiers d'un nombre de caractère donné au sein d'un acteur champ. Ces commandes connaissent les mêmes bugs que décrits plus bas (syntaxe donnée par le dictionnaire lingo ne fonctionnant pas).

Et nous pourrons donc écrire :
on chercherTexte(uneChaîne, unTexte) me

nbCaract = uneChaîne.length
nbCharTotal = unTexte.length
lstTrouvailles = []

repeat with i = 1 to nbCharTotal - (nbCaract - 1)
aComparer = ""
repeat with j = 1 to nbCaract
aComparer = aComparer&(char(i+(j-1)) of unTexte)
end repeat
if aComparer = uneChaîne then
lstTrouvailles.add(i)
end if
end repeat

répéter avec un compteur i de 1 jusqu'au nombre de valeurs contenues dans lstTrouvailles
répéter avec un compteur j de 1 jusqu'à nbCaract

mettre le caractère d'index : valeur de l'élément de rang i dans lstTrouvailles + (j-1)) en rouge
mettre le caractère d'index : valeur de l'élément de rang i dans lstTrouvailles + (j-1)) en gras

fin répéter
fin répéter

end

Les deux instructions présentes à l'intérieur des boucles de répétition peuvent paraître obscures à la première lecture. Pour une fois, la forme codée, que nous allons voir tout de suite, est plus claire.
Explicitons : la liste lstTrouvailles contient les index de tous les débuts de sous-chaînes trouvées. En parcourant la liste, le premier élément indique la valeur de l'index du premier caractère de la première occurrence trouvée. Le deuxième élément de la liste indique la valeur du premier caractère de la deuxième occurrence trouvée, etc.
Donc, en prenant la valeur de l'élément de rang i à l'intérieur de la boucle de répétition, on va lire successivement tous les index de tous les premiers caractères de toutes les occurrences trouvées.
On y ajoute j-1 pour la même raison qu'exposée plus haut : ce premier caractère fait partie de l'occurrence, il faut donc prendre index + 0, index + 1, index + 2, etc.
De la même façon que plus haut, on aurait pu initialiser le compteur j à 0 et ajouter j seulement à chaque index de premier caractère.

Il nous reste à coder :
on chercherTexte(uneChaîne, unTexte) me

nbCaract = uneChaîne.length
nbCharTotal = unTexte.length
lstTrouvailles = []

repeat with i = 1 to nbCharTotal - (nbCaract - 1)
aComparer = ""
repeat with j = 1 to nbCaract
aComparer = aComparer&(char(i+(j-1)) of unTexte)
end repeat
if aComparer = uneChaîne then
lstTrouvailles.add(i)
end if
end repeat

repeat with i = 1 to lstTrouvailles.count()
repeat with j = 1 to nbCaract

set the forecolor of char (lstTrouvailles[i]+(j-1)) of member("leTexte") to the forecolor of member("rougeRef")
set the fontStyle of char (lstTrouvailles[i]+(j-1)) of member("leTexte") to "bold"

end repeat
end repeat

end

Deux remarques à nouveau :
- à propos de la syntaxe : on trouvera dans le fichier .dir téléchargeable, en commentaire dans le script "analyser", la syntaxe donnée par le dictionnaire Lingo, sous forme de syntaxe pointée. C'est un des rares bugs de Director, mais cette syntaxe indiquée par le dictionnaire NE MARCHE PAS. Il faut le savoir, et utiliser l'ancienne syntaxe dite "verbose" telle qu'elle est indiquée ci-dessus.

- à propos de la couleur : Director autorise deux codages de couleur : par numéro de couleur dans la palette de 256 couleurs, ou en valeurs rvb (rouge, vert, bleu). Si les valeurs rvb marchent très bien pour les instructions de dessin (comme nous le verrons dans un prochain exercice), c'est moins évident avec les propriétés color et foreColor. La manière la plus simple de faire est de se créer un ou plusieurs acteurs qui ne serviront pas directement à l'animation, de leur affecter manuellement les couleurs qu'on souhaite utiliser et de dire ensuite dans le programme "je veux la même couleur que celle de cet acteur de référence". Voici pourquoi on trouvera dans la distribution deux tels acteurs, l'un nommé noirRef et l'autre rougeRef, et on trouvera dans la première ligne à l'intérieur des instructions de boucle l'utilisation d'un tel acteur de référence. Cette particularité est propre à lingo.

Il nous reste deux choses à faire :
- afficher le résultat dans l'acteur "resultat"
- ne pas oublier de réinitialiser la couleur et le style du texte source au démarrage de l'animation.

Pour afficher le résultat, c'est très simple : il suffit de constituer une chaîne de caractères donnant ce résultat, et d'affecter cette chaîne à la propriété text du champ resultat.
Cette chaîne se composera de morceaux qui seront constants, concaténés avec les valeurs des deux variables suivantes : le nombre d'occurrences trouvées (qui sera le count de la liste lstTrouvailles, et le rappel de la sous-chaîne que nous cherchions. Nous utilisons pour cela l'opérateur de concaténation vu plus haut : "&".
D'autre part, comme nous sommes soucieux de fabriquer un produit impeccable, nous allons mettre une condition qui vérifiera si on a trouvé 0 ou 1 occurrence, ou si on en a trouvé plusieurs. Nous fabriquerons, selon le cas, une chaîne où les parties constantes seront au singulier ou au pluriel.
Dernière particularité de l'instruction qui fabrique cette chaîne de message (appelée leMessage dans l'exercice) : pour le rappel de la sous-chaîne recherchée, et donc citer cette sous-chaîne dans leMessage, il va nous falloir afficher des guillemets. Or les guillemets, sont eux-mêmes, dans le langage, des caractères réservés, délimiteurs de chaînes de caractères. Nous allons donc utiliser un autre mot clé du langage qui signifie que c'est bien le caractère guillemets qu'on veut afficher dans leMessage, et ce mot clé est QUOTE. Il en existe quelques autres tels que TAB ou RETURN. Nous les verrons en leur temps.

Nous pourrons donc terminer notre script d'analyse de la manière suivante :
on chercherTexte(uneChaîne, unTexte) me
nbCaract = uneChaîne.length
nbCharTotal = unTexte.length
lstTrouvailles = []

repeat with i = 1 to nbCharTotal - (nbCaract - 1)
aComparer = ""
repeat with j = 1 to nbCaract
aComparer = aComparer&(char(i+(j-1)) of unTexte)
end repeat
if aComparer = uneChaîne then
lstTrouvailles.add(i)
end if
end repeat

repeat with i = 1 to lstTrouvailles.count()
repeat with j = 1 to nbCaract
set the forecolor of char (lstTrouvailles[i]+(j-1)) of member("leTexte") to the forecolor of member("rougeRef")
set the fontStyle of char (lstTrouvailles[i]+(j-1)) of member("leTexte") to "bold"

end repeat
end repeat

if lstTrouvailles.count < 2 then
leMessage = "Le moteur de recherche a trouvé "&lstTrouvailles.count()&" occurrence de la sous-chaîne"&&QUOTE&laChaîne&QUOTE&&"recherchée dans le texte"
else
leMessage = "Le moteur de recherche a trouvé "&lstTrouvailles.count()&" occurrences de la sous-chaîne"&&QUOTE&laChaîne&QUOTE&&"recherchée dans le texte"
end if

member("resultat").text = leMessage

end

On remarquera qu'après le mot "trouvé" et avant le mot "occurrence", on a mis des espaces pour respecter l'espacement des mots dans le résultat final de la concaténation. En revanche, avant et après les deux exemplaires du mot-cé QUOTE, on n'a pas mis d'espace, mais un double "&" : &&.

C'est une particularité de Lingo : en utilisant && comme opérateur de concaténation, on introduit automatiquement un espace entre les deux morceaux de chaîne concaténés.

Il nous reste à réinitialiser les couleurs et le style de la police :
- au démarrage de l'application,
- quand on fait des recherches successives, donc à chaque appui sur le bouton.

Pour cela, nous ne ferons pas le détail de quel caractère est dans telle couleur ou tel style, mais nous remettrons les couleurs et styles originaux sur l'ensemble de l'acteur contenant le texte source.
Nous rajouterons donc les deux lignes suivantes dans le prepareMovie et dans le mouseUp du bouton :

member("leTexte").foreColor = member("noirRef").foreColor
member("leTexte").fontStyle = "plain"

et là, lorsqu'il s'agit de l'acteur champ entier, la syntaxe à point fonctionne.

Notre moteur de recherche, dans sa version première, est terminé.

Il reste à faire une remarque en guise de conclusion :
J'ai dit plus haut que nous avions écrit là une fonction réutilisable. Ce n'est pas tout à fait vrai : en effet, si la fonction marche quels que soient le texte source et la sous-chaîne à chercher qu'on lui passe en argument, elle va toujours changer la couleur et la graisse des caractères trouvés (désignés par leur index), dans le texte qui est contenu dans l'acteur leTexte, et ce, même si on applique la recherche à un autre texte. Ce qui est un non-sens.
Par ailleurs, le résultat sera toujours affiché dans le champ "resultat". Or si, pour une raison ou pour une autre, nous souhaitions afficher ce résultat ailleurs, il faudrait réécrire la fonction.
Une vraie fonction réutilisable utiliserait deux arguments supplémentaires : l'acteur champ dans lequel modifier les caractères (alors que nous nous sommes contentés, nous, de récupérer son contenu dans une variable et par ailleurs de le citer nommément (donc "en dur"), et l'acteur champ dans lequel afficher le résultat (alors que nous l'avons également cité nommément).

La fonction serait alors modifiée ainsi :
on chercherTexte(uneChaîne, unActeurSource, unActeurCible)

nbCaract = uneChaîne.length
nbCharTotal = member(unActeurSource).text.length
lstTrouvailles = []

repeat with i = 1 to nbCharTotal - (nbCaract - 1)
aComparer = ""
repeat with j = 1 to nbCaract
aComparer = aComparer&(char(i+(j-1)) of unTexte)
end repeat
if aComparer = uneChaîne then
lstTrouvailles.add(i)
end if
end repeat

repeat with i = 1 to lstTrouvailles.count()
repeat with j = 1 to nbCaract

set the forecolor of char (lstTrouvailles[i]+(j-1)) of member(unActeurSource) to the forecolor of member("rougeRef")
set the fontStyle of char (lstTrouvailles[i]+(j-1)) of
member(unActeurSource) to "bold"

end repeat
end repeat

if lstTrouvailles.count < 2 then
leMessage = "Le moteur de recherche a trouvé "&lstTrouvailles.count()&" occurrence de la sous-chaîne"&&QUOTE&laChaîne&QUOTE&&"recherchée dans le texte"
else
leMessage = "Le moteur de recherche a trouvé "&lstTrouvailles.count()&" occurrences de la sous-chaîne"&&QUOTE&laChaîne&QUOTE&&"recherchée dans le texte"
end if

member(unActeurCible).text = leMessage

end

Dans laquelle unActeurSource et unActeurCible seraient passés comme chaînes de caractères exprimant le nom de chacun de ces acteurs.
Ainsi rédigée, notre fonction peut alors être mise en script d'animation, à la suite du prepareMovie, et elle est réellement réutilisable.

(*) NB : En réalité, dans un langage de programmation, les chaînes de caractères n'existent pas. Il n'existe que des données de type caractère, représentant chacune un caractère unique. C'est le cas dans les langages de bas niveau comme par exemple le C. Lorsqu'on a besoin de manipuler une chaîne, on manipule en fait un tableau (une liste), dans lequel figurent, à la suite les uns des autres, tous les caractères, considérés chacun comme donnée unique, composant la chaîne. Ce qui explique que chaque caractère porte un index, exactement comme un élément d'une liste porte un rang, comme nous l'avons appelé lorsque nous l'avons utilisé (exercice de l'avion par exemple).
Cette manière de manipuler les caractères lorsqu'on a besoin d'une chaîne est peu commode. C'est pourquoi les langages dits "évolués" (comme Lingo, ActionScript ou Visual Basic, mais il y en a beaucoup d'autres) ont des fonctions internes, totalement transparentes et invisibles pour celui qui programme, qui prennent en charge ces tableaux de bas niveau composés de caractères uniques, pour les concaténer tous, et présenter une entité que nous pourrons alors manipuler globalement comme ce que nous appelons une chaîne, aussi longue soit-elle.
Il n'est donc pas étonnant que nous retrouvions, à propos des chaînes, des fonctions qui sont assez semblables à celles des listes.

Retour à la troisième étape
Passer à la cinqième étape