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


Autour du texte - Analyse et manipulation - Neuvième étape :

Analyse d'un corpus de texte sur le disque dur.

Attention, ci-contre, ce n'est pas l'animation shockwave mais une simple capture d'écran. En effet, cette application utilise un Xtra qui ne fonctionne pas sous shockwave. Ce qui du reste se comprend, nous expliciterons cela au moment de nous servir de cet Xtra.
Vous devez donc télécharger le .dir, et aussi télécharger le corpus de textes necessaires. Ce corpus est livré en .zip ; il est à décompresser dans un sous-répertoire nommé "lesTextes" (sans les guillemets), du répertoire où vous aurez placé le .dir.
Vous devez également vérifier que le dossier "Xtras" présent dans le répertoire où est installé Director contient :
- l'Xtra fileIO (fileIO.x32 sous windows). Celui-ci est en général rangé dans le dossier "Media Support". Cet Xtra est normalement livré en standard avec Director. Si vous ne l'avez pas, vous pouvez le télécharger
(bouton droit -> enregistrer la cible).
FileIO signifie "file Input Output",
- l'Xtra fileXtra4 (fileXtra4.x32 sous windows). Celui-ci n'est pas livré en standard avec Director mais il est gratuit chez http://www.kblab.net. Vous pouvez aussi le télécharger depuis ce cours, avec sa documentation en PDF.
NB : si vous faites des projections, vous devrez recopier ces Xtras dans un sous-dossier "Xtra" du dossier qui contient votre projection.

Pour cette dernière étape, nous allons abandonner quelques unes des fonctions que nous avions développées jusque là. Nous n'allons plus faire d'analyse d'occurrences, ni continuer à choisir un critère de tri, ni un ordre de classement.

En effet, cette neuvième étape en forme de conclusion va uniquement consister à faire une recherche de sous-chaîne dans un texte, telle que nous l'avons construite aux étapes 4 et 5 de cet exercice. Mais cette fois, le texte source dans lequel nous allons chercher ne sera plus un texte unique contenu dans un acteur champ, mais un ensemble de textes différents situés à l'extérieur de l'animation, c'est à dire que ces textes existent sous forme de différents fichiers ASCII présents sur le disque dur.

Une fois que notre animation aura indiqué le nom des fichiers qui comportent la sous-chaîne recherchée dans la fenêtre de résultat, les noms de ces fichiers pourront se mettre en surbrillance (et uniquement les noms de fichiers, pas les autres mots de la fenêtre de résultat). Un clic sur le nom de fichier alors en surbrillance provoquera l'affichage du texte correspondant dans la fenêtre de texte (au dessus), et les occurrences seront mise en rouge et en gras, comme nous l'avions fait aux quatrième et cinquième étapes.
On conserve l'option de chercher sur un mot entier.

Le corpus comprend 15 textes mais rien ne vous empêche d'en ajouter.
A l'exception de voltaire.txt, dont la version sous forme de fichier provient d'une source inconnue, toutes les autres versions électroniques de ces textes proviennent de l'ABU (http://abu.cnam.fr/).

Voici quels sont ces textes et d'où ils sont tirés :
- balzac.txt : début de "L'élixir de longue vie",
- diderot.txt : début des "Pensées sur l'interprétation de la nature",
- dumas.txt : début de "Les Trois Mousquetaires",
- flaubert.txt : début de "Madame Bovary",
- hugo.txt : début de "La fin de Satan",
- laclos.txt : une lettre du viconte de Valmont à la marquise de Merteuil, au début des "Liaisons dangereuses",
- loti.txt : un eXtrait au début d’ "Aziyadé",
- merimee.txt : début de "Colomba",
- pascal.txt : début de "Petits écrits philosophiques et religieux ",
- plutarque.txt : début du Livre premier de "Des opinions des philosophes",
- rousseau.txt : début des "Confessions",
- stendhal.txt : un eXtrait au début de "La Chartreuse de Parme",
- verne.txt : un eXtrait au début de "De la Terre à la Lune",
- volataire.txt : début du pamphlet "L'affaire Calas",
- zola.txt : début de "L’argent".

Du fait que Lingo ne sait pas, de manière native, accéder au disque dur ni au système, il nous faut utiliser des extensions du langage qu'on appelle des Xtras. Ces Xtras sont en fait des modules (on dirait des plug-ins dans la terminologie des navigateurs web), qui apportent un jeu d'instructions supplémentaires au lingo de base. Ce jeu d'instructions est en général spécialisé sur un thème particulier : on peut trouver des Xtras pour des fonctionnalités 3D, pour le son et la musique, pour la capture et le traitement de vidéo, pour accéder aux fonctions du système d'exploitation, pour l'accès aux bases de données... Il en existe une multitude.
Certains, très basiques, ont été développés directement par Macromedia ou un de ses partenaires, et sont livrés en standard avec Director (pour être plus exact, beaucoup d'enrichissements à Lingo à travers ses versions successives ont été faits par l'adjonction d'Xtras livrés en standard, et dont l'initialisation est transparente pour l'utilisateur).
D'autres, plus spécialisés, ont été développés par des tierces parties (on appelle ainsi des développeurs qui travaillent pour les produits d'un constructeur mais sans faire partie de ce constructeur). Ces Xtras sont payants, ce sont des produits commerciaux, et leur prix peut aller de quelques dizaines à plusieurs milliers de dollars. Une fois installés, il doivent être enregistrés en général à l'aide d'un numéro de série ou d'une clé d'activation fournis par le développeur de l'Xtra une fois la license de celui-ci achetée.

Ces Xtras peuvent faire partie de diverses catégories. On peut citer :
- les "Asset Xtras" qui en général s'importent dans une animation Director sous la forme d'un acteur, lequel est doté de méthodes et de propriétés propres à l'Xtra (un exemple d'Asset Xtra : videoSprite).
- les "scripting Xtras" qui sont des modules de code qui après instanciation, permettront d'accéder depuis les scripts lingo à leur jeu d'instructions et à leurs propriétés.

Que ce soient des "Asset Xtras" ou des "scripting Xtras", les propriétés et méthodes qui leurs sont attachées sont documentées par le développeur de l'Xtra. Cette doccumentation est plus ou moins bien faite, et il peut arriver qu'on soit obligé d' "inventer" ou de supposer quand la documentation est vraiment indigente (il n'y a alors pas d'autre solution que de faire l'essai de probables commandes sous diverses syntaxes habituelles et avec divers paramètres jusqu'à ce que l'on trouve ce qui marche. C'est long et monotone, et cela suppose une bonne connaissance des langages en général ainsi que des conventions les plus répandues pour deviner les commandes possibles).

Dans cette étape, nous utiliserons deux scripting Xtras, lesquels permettent :
- pour l'un, de lire un fichier ASCII sur le disque. Cet Xtra s'appelle FileIO. S'il n'est pas présent dans votre dossier Xtra du répertoire d'installation de Director, vous devez le télécharger (voir ci-dessus l'introduction de cette étape),
- pour l'autre, pour pouvoir connaître la structure du disque dur, et récupérer les noms des volumes et fichiers qui s'y trouvent. Cet Xtra s'appelle fileXtra4.
S'il n'est pas présent dans votre dossier Xtra du répertoire d'installation de Director, vous devez le télécharger (voir ci-dessus l'introduction de cette étape).

Compte tenu de tout ce qui précède, le .dir de cette étape a été débarassé de quelques éléments et scripts qui ne sont plus utilisés ici. Néanmoins, il est tout à fait possible, en adaptant l'interface et les scripts, de réimplémenter les statistiques du texte et les analyses d'occurrences pour le texte qui aurait été choisi dans la liste des fichiers présentée par l'application.

Voici la description du fonctionnement de cette étape :
- un ensemble de textes (15 dans l'exercice) est présent sur le disque dur dans un sous dossier de l'application ,
- un champ "cherche" (comme aux quatrième et cinquième étapes de l'exercice) permet de saisir une chaîne de caractères à rechercher,
- un bouton permet de lancer la recherche sur l'ensemble des fichiers textes, sans pour autant les afficher,
- une fois la recherche terminée, le champ "résultat" indique le nombre de fichiers contenant la sous-chaîne recherchée avec rappel de cette sous-chaîne, et au dessous, donne la liste de ces fichiers, avec le nombre d'occurrences de la sous-chaîne cherchée que chacun d'eux contient,
- le nom de chaque fichier se met en surbrillance au passage de la souris, et un clic sur ce nom de fichier provoque son affichage dans le champ "leTexte", affichage dans lequel chaque occurrence de la sous-chaîne recherchée est mise en rouge et en gras, comme précédemment ,
- on a conservé l'option de limiter la recherche aux mots entiers.

Au démarrage de l'application, on trouvera relativement peu de changement dans le prepareMovie. Les différents champs sont remis à vide, leur affichage est réinitialisé en noir et en style normal. Le timeOut existe toujours puisque nous avons toujours des ascenseurs.

La différence réside surtout dans le fait de créer l'instance de l'Xtra fileIO et l'instance de l'Xtra fileXtra4.
S'agissant de "scripting Xtras", il faut en créer une instance à laquelle on s'adressera ensuite pour réaliser les fonctions que nous souhaitons.
Cela se fait toujours de la même façon : l'instance étant un objet, il faut une variable globale (globale parce qu'elle devra être accessible depuis d'autres scripts) pour l'héberger, exactement comme nous avons une globale qui héberge le timeOut.
Une fois les globales déclarées (ici appelées leFichier et leFx), il suffit de les affecter en utilisant la méthode new() des l'Xtra (ceci est valable pour presque tous les scripting Xtras).
Et on écrira : leFichier = new(Xtra "fileIO") et leFx = new(xtra "fileXtra4")
S'agissant d'Xtras livré en standard ou gratuits, il n'est pas nécessaire de leur appliquer une méthode d'enregistrement ou d'autorisation avec un numéro de série. D'autre part, ces Xtra ne réclament pas non plus de méthode d'initialisation (comme par exemple pour l'Xtra permettant de communiquer avec les ports de la machine, où il faut indiquer non seulement le nom du port que l'on désire attaquer, mais encore donner quelques éléments de protocole).

Pour une bizarre raison, sur laquelle je n'ai aucune explication et n'ai trouvé aucun article sur internet, si les instances d'Xtra sont créées en tête du prepareMovie, alors toutes les instructions qui suivent sont ignorées, et dans le cas présent les éléments qui devraient être cachés par la mise à 0 de leur propriété visible, ne le sont pas. Ils le sont correctement dans l'environnement de développement, mais plus dans les projections ni dans les Shockwave !
C'est pour cette raison que les instance d'Xtra FileIO et fileXtra4 sont créées à la fin du prepareMovie.

Il nous faut également créer une autre globale pour y inscrire les noms des fichiers qui composent le corpus de textes, de manière à pouvoir accéder à ces fichiers depuis d'autres scripts. Cette globale, appelée lstFichiers dans l'exercice, est initialisée comme une liste vide dans le prepareMovie. Elle sera peuplée dans le mouseUp du bouton.
Nous aurions pu la peupler tout de suite dans le prepareMovie, mais alors, si jamais nous voulions utiliser notre application sur un répertoire dont le contenu peut changer dans le temps, et sans redémarrer notre application, nous n'aurions pas de remise à jour du contenu du répertoire, puisque la liste aurait été fixée une fois pour toutes au démarrage (le prepareMovie est un événement système unique et instantané).

Enfin, dernier mot à propos des scripting Xtras : quand on en a créé une instance, il faut, pour éviter tout conflit ultérieur, la détruire quand on quitte l'animation. On a donc rajouté un gestionnaire on stopMovie (événement système unique se produisant lorsqu'on sort de l'application), dans lequel on met les globales leFichier et leFx à 0, ce qui détruit les instances d'Xtra.
On a également rajouté une ligne clearGlobals, que l'on devrait toujours écrire : cette instruction détruit toutes les globales. On est ainsi sûr de ne pas conserver de valeurs indésirables à la sortie de l'application. Ceci est surtout valable en phase de développement.

Donc les scripts d'animation se présentent comme suit (on rajoute également des instructions pour cacher les sprite 41 à 43 ; nous verrons plus loin à quoi cela correspond) :
global t, leFichier, lstFichiers, leFx

on prepareMovie
t = timeout("g").new(250, #moteur)

repeat with i = 41 to 43
sprite(i).visible = 0
end repeat
member("cherche").text = ""
member("leTexte").text = " "
member("leTexte").scrolltop = 0
member("leTexte").foreColor = member("noirRef").foreColor
member("leTexte").fontStyle = "plain"
member("resultat").text = ""


lstFichiers = []
leFx = new(xtra "fileXtra4")
leFichier = new(xtra "fileIO")

end

on stopMovie
leFichier = 0
leFx = 0
clearGlobals
end

Il n'y a dans tout cela aucune difficulté.

Puis nous allons modifier le script du bouton de recherche.

Tout d'abord, il faut rappeler la déclaration des globales leFichier et leFx (instances d'Xtra), et lstFichiers (les fichiers qui composent le corpus), qui vont maintenant nous servir.
Les propriétés du sprite ne changent pas, non plus que son beginSprite et ses mouseEnter et Leave.

La fonction chercherTexte(uneChaîne, unTexte) change très peu. On l'a simplement amputée de la section qui élaborait et affichait le message de résultat. En effet, ce n'est plus le même message que nous allons afficher (puisque dorénavant nous allons afficher une liste de fichiers). Nous avons remonté cette fonctionnalité dans le mouseUp.
Seule chose : puisque pour élaborer le message, le mouseUp aura besoin des trouvailles calculées par chercherTexte(uneChaîne, unTexte), nous avons rajouté à chercherTexte() une instruction return qui lui impose de renvoyer ce qu'elle a trouvé.
chercherTexte(uneChaîne, unTexte) se présente donc désormais sous cette forme :

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
if motEntier = 1 then
leCharAvant = unTexte.char[i - 1]
leCharApres = unTexte.char[i + nbCaract]
if lstDelimiteurs.getPos(leCharAvant) <> 0 and lstDelimiteurs.getPos(leCharApres) <> 0 then
lstTrouvailles.add(i)
end if
else
lstTrouvailles.add(i)
end if
end if
end repeat
return lstTrouvailles
end

C'est le mouseUp qui va subir la plus grande refonte :
- pour pouvoir, à la fin du mouseUp, afficher les noms des fichiers qui contiennent la sous-chaîne recherchée, et comme nous examinerons l'ensemble des fichiers en séquence, nous allons avoir besoin d'une liste pour marquer les fichiers qui correspondent à la recherche. Et comme nous indiquons, dans le résultat, le nombre d'occurrences de la sous-chaîne recherchée que contient chaque fichier, cette liste sera une liste de propriétés dont les couples seront [nomDeFichier:nombreDoccurrences].
Dans l'exercice, j'ai appelé cette liste lstFichiersQuiCorrespondent, et elle est initialisée à vide au début du mouseUp.
- l'analyse de l'ensemble des fichiers durera un certain temps (comme "le fût du canon..."), temps d'autant plus long que le nombre de fichiers à analyser sera plus grand, comme on peut s'y attendre. Même avec 15 textes seulement, ce temps est largement perceptible. Il faut donc, pendant l'analyse, indiquer à l'utilisateur de notre application, que la machine n'est pas plantée mais qu'elle est en train de travailler. Ceci peut se faire par un changement de curseur de souris, mais il est beaucoup plus élégant de montrer aussi quel est l'état d'avancement du travail d'analyse, et nous allons donc nous doter d'une barre de progression. Celle-ci se compose de trois éléments (trois sprites sur la scène) : une légende "Recherche en cours, veuillez patienter...", la barre de progression proprement dite dont la longueur augmente à mesure que le travail s'accomplit, et enfin un cadre, qui graphiquement délimite cette barre, mais qui montre aussi, visuellement, quelle est la longueur totale de la barre (atteinte quand le traitement d'analyse est terminé). Ce cadre permet à l'utilisateur d'évaluer d'un coup d'œil la proportion de travail déjà réalisé.
Ces trois éléments devront être cachés en temps normal ainsi qu'au démarrage de l'application (et c'est pour cela que nous avons caché les sprite 41 à 43 dans le prepareMovie, les trois éléments de cette barre étant posés sur ces sprites). Ils devront apparaître quand on lance une recherche. Une fois la recherche terminée, ces trois éléments sont cachés à nouveau et laissent la place à l'affichage des résultats. Cette barre n'empèche pas de modifier le curseur de la souris.
Pour faire varier la longueur de la barre pendant la recherche, on va modifier la propriété right du sprite qui la contient. Faire varier l'un des cotés d'un sprite (left, right, top ou bottom) revient à modifier sa forme et à l'étirer (ou le contracter) dans le sens correspondant au coté que l'on manipule.
Le pas de progression de la barre sera déterminé par le rapport de sa longueur totale sur le nombre de fichiers à analyser, toujours en faisant attention de rendre décimal l'un des opérandes du rapport pour éviter la division entière. Je rappelle que cette conversion se fait soit par une multiplication de l'un des opérandes par 1,0 ou par l'utilisation de la fonction float(nombreAConvertir).
Voici pour l'interface.

Ensuite, il nous faut constituer la liste des fichiers dans lesquels rechercher. Ceci sera fait par un appel à l'instance de l'xtra fileXtra4, leFx, et en lui appliquant sa méthode fx_FolderToList. Cette méthode, construite dans l'Xtra, est indiquée par sa documentation (téléchargeable au début de cette étape). Elle renvoie une liste de tous les fichiers et sous-répertoire contenus dans le répertoire indiqué comme chemin à la méthode (voir ci-dessous la propriété the moviePath).
Puis, pour procéder à l'analyse des fichiers, nous allons, dans une boucle de répétition, demander à l'Xtra fileIO d'ouvrir chacun des fichiers du corpus, de stocker son contenu dans une variable, et de faire la recherche dans ce texte. Nous ajouterons à la liste lstFichiersQuiCorrespondent, en propriété le nom du fichier correspondant si chercherTexte() y a trouvé une ou des occurrences de la sous-chaîne recherchée, et en valeur, le nombre d'occurrences existant dans ce fichier.
Enfin, on élabore le message par concaténation de morceaux de chaîne comme nous l'avons fait jusqu'ici, en balayant la liste lstFichiersQuiCorrespondent.
Nous prévoierons toutefois d'élaborer un message spécifique différent :
- si la recherche est infructueuse et qu'aucune occurrence de la sous-chaîne recherchée n'a été trouvée. C'est plus élégant que d'annoncer "0 fichier comportent la sous chaîne...",
- selon le nombre de fichiers trouvés. Comme le résultat est exprimé en français correct, le mot "fichier" et le verbe "comporte" du message de résultat s'écrivent au singulier si on n'a trouvé qu'un seul fichier correspondant à la recherche, et s'écrivent au pluriel s'il y en a plusieurs.
Nous avons donc trois cas pour le message de résultat : aucun fichier, un seul fichier, plusieurs fichiers.

L'élaboration du message sera donc écrite en conséquence à l'aide d'instructions conditionnelles.

Avant d'écrire notre mouseUp en pseudo-code, voyons quelles seront les instructions à donner à l'Xtra fileIO pour extraire le texte contenu dans les fichiers. Le fonctionnement est simple :
- il faut indiquer quel fichier ouvrir ainsi que le chemin pour y accéder. Une instance de l'Xtra fileIO ne peut ouvrir qu'un seul fichier à la fois. Si l'on souhaite ouvrir en même temps plusieurs fichiers (ce qui n'est pas le cas ici), il faudra créer autant d'instances de l'Xtra que de fichiers que l'on désire ouvrir à la fois.
- Avant de lire ou d'écrire dans un fichier, il faut ouvrir ce fichier.
L'ouverture peut se faire dans trois modes différents : en lecture/écriture (on peut lire et écrire), en lecture seule (l'écriture est impossible), en écriture seule (la lecture est impossible). L'instruction qui correspond, et qui doit être appliquée comme une méthode de l'instance d'Xtra, est la suivante : leFichier.openFile(chemin\nomFichier, numeroDuMode), dans laquelle le numéro du mode vaut 0 en lecture/écriture, 1 en lecture seule et 2 en écriture seule. Dans notre exercice, afin de ne pas modifier le texte par erreur, nous ouvrirons le fichier en lecture seule.
- l'Xtra possède des instructions pour lire le fichier entier d'un seul coup, pour lire une ligne isolée (les lignes sont délimitées par des retours chariot), ou pour lire un caractère isolé. Dans le cas des lignes ou des caractères isolés, il faut savoir qu'il existe dans les fichiers un pointeur qui désigne la position du caractère sur lequel on se trouve. Il faut donc fixer la position du pointeur avant de demander à l'Xtra de lire une ligne ou un caractère isolé. Dans le cas d'une écriture, c'est la même chose, il faut fixer la position du pointeur à l'endroit où on veut écrire le caractère. Dans l'exercice, nous placerons ce pointeur sur la position 0, qui est située juste avant le premier caractère (*), et nous utiliserons l'instruction leFichier.readFile(), puisque nous cherchons à récupérer le fichier entier, c'est à dire la totalité du texte (**).
- Enfin, après avoir lu le fichier, nous le refermons à l'aide de l'instruction leFichier.closeFile().

Ce qui nous donne (en exagérant les retraits pour plus de clarté, et en mettant en couleur les blocs de codes) :

on mouseUp
si l'acteur "cherche" n'est pas vide alors
peupler la liste lstFichiers à l'aide de l'xtra fileXtra4
vider l'acteur "leTexte"
vider l'acteur "resultat"
initialiser une liste de propriété vide appelée lstFichiersQuiCorrespondent
Rendre visible la barre de progression, son cadre et sa légende
affecter à la souris le curseur "machine occupée" (sablier ou autre)
calculer le pas de déplacement de la barre de progression : largeur intérieure du cadre (ici 230) / nombre de fichiers à analyser

répéter avec les noms de fichiers contenus dans la lstFichiers
déplacer le coté droit de la barre de progression de la valeur d'un pas
rafraichir la scène

ouvrir le fichier dans le sous répertoire lesTextes en lecture seule
positionner le pointeur du fichier à la position 0 (avant le premier caractère)
lire le contenu du fichier et mettre ce contenu dans une variable leContenu
fermer le fichier

lire la sous-chaîne saisie dans l'acteur "cherche" et la stocker dans une variable laChaine
appeler la fonction chercherTexte en lui passant laChaine et leContenu en arguments, et mettre le résultat (qui se présente sous forme de liste) dans une variable appelée lesTrouvailles

Si le nombre d'éléments de lesTrouvailles > 0 alors
ajouter à lstFichiersQuiCorrespondent le nom du fichier et le nombre d'éléments de lesTrouvailles (= nombre d'occurrences trouvées dans ce fichier)
fin si
fin répéter


cacher la barre de progression, son cadre et sa légende
remettre le bord droit de la barre de progression à sa position initiale (ici 108)
réaffecter un curseur normal àl a souris
rafraîchir la scène

si le nombre d'éléments de lstFichiersQuiCorrespondent = 0 alors
leMessage = "Aucun fichier ne contient la sous-chaîne" auquel on ajoute un espace et des guillements puis le texte de l'acteur "cherche", puis des guillemets à nouveau puis la chaine "recherchée"
sinon si le nombre d'éléments de lstFichiersQuiCorrespondent = 1 alors
leMessage = "Un seul fichier comporte la sous-chaîne" auquel on ajoute un espace puis des guillemets puis le texte de l'acteur "cherche" puis des guillemets à nouveau puisl a chaine "recherchée : " suivie de deux sauts de ligne
extraire le nom de la propriété de l'unique élément de lstFichiersQuiCorrespondent et l'appeler fichierQuiVaBien
extraire la valeur correspondant à cette propriété et l'appeler lesoccurrences
leMessage = leMessage auquel on ajoute fichierQuiVaBien puis un espace et la chaine "......(nombre d'occurrences trouvées : " puis un espace, puis lesoccurrences puisla chaine ")" pour fermer la paranthèse et enfin un saut de ligne

sinon
leMessage = le nombre d'éléments de lstFichiersQuiCorrespondent et un espace et la chaine "fichiers comportent la sous-chaîne" puis des guillemets puisl e texte de l'acteur "cherche" puis des guillemets à nouveau puis la chaine "recherchée : "puis deux sauts de ligne
répéter avec un compteur i de 1 jusqu'au nombre d'éléments de la liste lstFichiersQuiCorrespondent
extraire le nom de fichier de rang i dans la liste lstFichiersQuiCorrespondent et le stocker dans une variable appelée fichierQuiVaBien
extraire la valeur associée à fichierQuiVaBien et la stocker dans une variable appelée lesoccurrences
leMessage = leMessage auquel on ajoute fichierQuiVaBien pui un espace, puis la chaine "......(nombre d'occurrences trouvées : " puis un espace puis lesoccurrences puis la chaine ")" pour fermer les paranthèses puis un saut de ligne
fin répéter

fin si

afficher le message dans l'acteur "resultat"
fin si
end

Bien que ce script comporte beaucoup d'imbrications, il ne pose pas de difficulté.

On peut y faire quatre remarques avant d'implémenter le code correspondant :
- le changement du curseur de souris : cette commande et ses variantes ont été vues dans le deuxième exercices aux étapes 4 et 6. Je le rappelle ici car la commande n'a pas été utilisée depuis dans ce cours.
- Pour la création de la liste lstFichiers et l'ouverture du fichier, on dit qu'il faut indiquer le chemin et le nom du fichier. Les fichiers se trouvent dans un sous-répertoire "lesTextes" du répertoire dans lequel se trouve l'animation. Il existe une propriété d'animation qui indique le chemin du répertoire contenant l'animation. Cette propriété s'appelle the moviePath.

Pour indiquer le chemin des fichiers textes, nous emploierons donc, sous la forme d'une concaténation de chaine : (the moviePath)&"lesTextes\" (ceci est la notation des chemins sur les machines windows. Se reporter au dictionnaire lingo pour connaitre les différentes syntaxes sur Mac).
- Pour balayer la liste des fichiers afin de les faire ouvrir en séquence par fileIO, nous allons utiliser une variante des boucles de répétions, variante qui n'est en fait utilisable qu'avec des listes. Cette forme est la suivante : repeat with uneVariable in uneListe... ...end repeat.

Dans cette forme, le compteur habituel, que nous avons appelé ici uneVariable, prend successivement toutes les valeurs des éléments de la liste uneListe.
Dans l'exercice, on a écrit repeat with nomFichier in lstFichiers... ... end repeat. nomFichier contiendra successivement tous les noms de fichiers contenus dans la liste.
- A l'intérieur de la boucle de répétition qui balaye la liste des fichiers, on a inclus une instruction updateStage pour rafraîchir la scène : quand on montre la barre de progression, quand on modifie sa longueur à l'intérieur de la boucle de répétition, et enfin une troisième après avoir caché à nouveau les éléments de la barre de progression. Ces instructions sont indispensables ici dans la mesure où on a besoin de voir les modifications sur la scène aussitôt que la commande a été émise, sans attendre un rafraîchissement automatique à la fin du mouseUp. Si on supprime ces updateStage (et surtout celui qui est à l'intérieur de la boucle de répétition), on ne verra pas apparaître la barre de progression (elle aura été remise en invisible avant qu'un rafraîchissement ait eu lieu).

Voici donc notre script une fois codé (et en respectant les mêmes couleurs pour les blocs que ci-dessus) :

on mouseUp
if member("cherche").text <> "" then

lstFichiers = leFx.fx_FolderToList((the moviePath)&"lesTextes")
member("leTexte").text = ""
member("resultat").text = ""
lstFichiersQuiCorrespondent = [:]
sprite(41).visible = 1
sprite(42).visible = 1
sprite(43).visible = 1
cursor 4
updateStage
pasBarre = 230.0 / lstFichiers.count()

repeat with nomFichier in lstFichiers
sprite(42).right = sprite(42).right + pasBarre
updateStage

leFichier.openFile(the moviePath&"lesTextes"&"\"&nomFichier, 1)
leFichier.setPosition(0)
leContenu = leFichier.readFile()
leFichier.closeFile()

laChaine = member("cherche").text
lesTrouvailles = chercherTexte(laChaine, leContenu)

if lesTrouvailles.count > 0 then
lstFichiersQuiCorrespondent.addProp(nomFichier, lesTrouvailles.count)
end if
end repeat

sprite(41).visible = 0
sprite(42).visible = 0
sprite(43).visible = 0
sprite(42).right = 108
cursor -1
updateStage

if lstFichiersQuiCorrespondent.count = 0 then
leMessage = "Aucun fichier ne contient la sous-chaîne"&&quote&member("cherche").text&quote&&"recherchée."
else if lstFichiersQuiCorrespondent.count = 1 then
leMessage = "Un seul fichier comporte la sous-chaîne"&&quote&member("cherche").text&quote&&"recherchée : "&return&return
fichierQuivaBien = lstFichiersQuiCorrespondent.getPropAt(1)
lesoccurrences = lstFichiersQuiCorrespondent.getProp(fichierQuiVaBien)
leMessage = leMessage&fichierQuivaBien&&"......(nombre d'occurrences trouvées : "&&lesoccurrences&")"&return
else
leMessage = lstFichiersQuiCorrespondent.count&&"fichiers comportent la sous-chaîne"&&quote&member("cherche").text&quote&&"recherchée : "&return&return

repeat with i = 1 to lstFichiersQuiCorrespondent.count
fichierQuiVaBien = lstFichiersQuiCorrespondent.getPropAt(i)
lesoccurrences = lstFichiersQuiCorrespondent.getProp(fichierQuiVaBien)
leMessage = leMessage&fichierQuiVaBien&&"......(nombre d'occurrences trouvées : "&&lesoccurrences&")"&return
end repeat

end if

member("resultat").text = leMessage
end if
end

Il nous reste maintenant à écrire le script mettant les noms de fichiers en rouge et en gras lorsque la souris passe dessus, et affichant le texte correspondant dans la fenêtre de texte lorsqu'on clique sur l'un de ces noms de fichiers. Ce script sera appliqué au sprite qui contient l'acteur champ "resultat".

La souris étant par définition mobile et fureteuse au dessus de ce champ résultat, il va nous falloir surveiller en permanence sur quel mot se trouve la souris, et mettre ce mot en rouge et en gras lorque ce mot est un nom de fichier. Inversement, lorsque la souris quitte le mot, il faut que le nom de fichier qui était en rouge et en gras redevienne noir et en style normal.
Pour surveiller quel mot survole la souris en permanence, il nous faut un événement répétitif.
Deux solutions s'offrent à nous : soit l'événement répétitif du sprite on mouseWithin, lequel se produit tout le temps que la souris se trouve sur le sprite, soit faire appel à notre moteur, qui continue à tourner pour animer les ascenseurs lorsque ceux-ci sont sollicités.

Dans l'exercice, nous avons choisi d'adopter le mouseWithin, bien que celui-ci soit un peu lent.

Voyons comment mettre un mot survolé en surbrillance (à la manière d'un lien hypertexte).
Nous aurons en fait la même instruction que celle qui nous a servi à mettre en surbrillance les occurrences cherchées aux étapes 4 et 5 de l'exercice. Ces instructions, au leiu de porter sur un seul caractère, porteront sur un mot entier. De la même façon que les caractères sont indiqués dans un texte par leur rang, les mots ont aussi un rang, un numéro, quil es indexe dans le texte.
Nous savons donc mettre un mot en surbrillance de la même façon qu'un caractère à l'aide des instructions :

set the foreColor of word(n) of member("resultat") to the foreColor of member("rougeRef")
set the fontStyle of word(n) of member("resultat") to "bold"

pour la mise en surbrillance,

set the foreColor of word(n) of member("resultat") to the foreColor of member("noirRef")
set the fontStyle of word(n) of member("resultat") to "plain"

pour le retour à la normale, et où, dans les deux cas, n est le numéro du mot dans le texte, le premier mot ayant le numéro 1, etc.

Il nous donc déterminer la valeur de n.
C'est très simple : il existe une propriété de l'animation qui indique le rang du mot situé sous la souris, lorsque celle-ci survole un sprite contenant un acteur champ. cette propriété s'appelle the mouseWord. Elle renvoie le numéro du mot du texte, et elle renvoie -1 si la souris n'est pas sur un champ.
NB : sur le même modèle, il existe aussi the mouseChar et the mouseLine qui indiquent respectivement le numéro du caractère et le numéro de ligne survolés par la souris. Voir le dictionnaire lingo pour les détails.

Maintenant que nous connaissons le numéro du mot, il nous faut toutefois vérifier que ce mot est bien un nom de fichier, et non pas n'importe quel autre mot, dont des exemplaires existent également dans notre champ résultat.
Pour savoir cela, nous devons nous servir du fait que les noms de fichiers portent une extension, laquelle est .txt pour les fichiers ASCII (on remarque du reste ici que cela sert de mettre les extensions aux noms d fichiers, même quand l'OS de la machine sur laquelle on travaille ne les utilisent pas, MAC au hasard !).
Nous allons donc tester si les quatre derniers caractères du mot survolés par la souris sont ".txt". Si oui, on a affaire à un nom de fichier, et on met ce nom en surbrillance.

Le traitement pour extraire ces quatre derniers caractères ne devrait maintenant plus poser aucune difficulté :
- on initialise une chaîne à vide (appelée quatreDerniers dans l'exercice),
- on récupère les quatre derniers caractères dans une boucle de répétition décrémentale, dont le compteur va du rang du dernier caractère du mot jusqu'à ce même rang-3,
- à la sortie de la boucle, la variable quatreDerniers contient les quatre derniers caractères du mot survolé. Si quatreDerniers est égal à ".txt", alors on est sur un nom de fichier.

Pour la mise en surbrillance de ce mot et son retour à la normale si la souris le quitte, il faut avoir recours à un petit artifice.
En effet, le mouseWithin est un événement répétitif.
Si nous contentons de mettre deux instructions :
quand la souris est sur le champ
mettre tout le texte en noir et en normal
mettre le mot sous la souris en rouge et en gras
fin

alors nous aurons une sorte de battement, puisque à chaque exécution du mouseWithin, tout le texte sera remis en noir, y compris le mot qui est sous la souris, avant de remettre ce seul mot en rouge et en gras.
Il nous faut donc détecter si le mot sous la souris a changé : si non, on ne fait rien, si oui on met le nouveau mot en gras et en rouge, et on met l'ancien mot en noir et en style normal.
Il nous faut donc garder, d'une exécutiuon du mouseWithin sur l'autre, le souvenir du mot qui est sous la souris.
Pour garder ce souvenir, nous allons comme d'habitude nous doter d'une propriété qui, à la fin de chaque mouseWithin, va contenir le numéro du mot sous la souris. A l'exécution suivante du mouseWithin, on testera si le mot qui est sous la souris cette fois-ci est le même qu'auparavant ou non. J'ai appelé cette propriété ancienMot dans l'exercice.
Par ailleurs, on remet tout le texte en noir et en style normal quand la souris quitte le champ, c'est à dire sur un événement on mouseLeave.

Voici donc quel sera notre script appliqué au champ résultat :
property spriteNum, ancienMot

On beginSprite me
ancienMot = 0
end

on mouseLeave
sprite(spriteNum).member.foreColor = member("noirRef").foreColor
sprite(spriteNum).member.fontStyle = "plain"
end

On mouseWithin me
lire le mot situé sous la souris
lire la longueur de ce mot

initialiser un variable quatreDerniers comme une chaîne vide
répéter avec un compteur i depuis le la longueur du mot jusqu'à cette longueur - 3
quatreDerniers = quatreDernier auquel on ajoute le caractère numéro i du mot
fin répéter

si quatreDernier = ".txt alors
si numéro du mot sous la souris est différent de ancienMot alors
mettre en rouge et en gras le mot sous la souris
mettre en noir et en normal l'ancienMot

fin si
sinon
mettre tout le texte en noir et en normal
fin si

ancienMot = le numéro du mot sous la souris
end

et en code :
property spriteNum, ancienMot

On beginSprite me
ancienMot = 0
end

on mouseLeave
sprite(spriteNum).member.foreColor = member("noirRef").foreColor
sprite(spriteNum).member.fontStyle = "plain"
end

on mouseWithin
unMot = sprite(spriteNum).member.word[the mouseWord]
longueurMot = unMot.length
quatreDerniers = ""
repeat with i = LongueurMot down to (longueurMot - 3)
quatreDerniers = unMot.char[i]&quatreDerniers
end repeat

if quatreDerniers = ".txt" then
if the mouseWord <> ancienMot then
set the foreColor of word(the mouseWord) of member("resultat") to the foreColor of member("rougeRef")
set the fontStyle of word(the mouseWord) of member("resultat") to "bold"
set the foreColor of word(ancienMot) of member("resultat") to the foreColor of member("noirRef")
set the fontStyle of word(ancienMot) of member("resultat") to "plain"
end if
else
set the foreColor of member("resultat") to the foreColor of member("noirRef")
set the fontStyle of member("resultat") to "plain"
end if

ancienMot = the mouseWord
end

Notre exercice est presque fini. Il nous reste à afficher dans le champ leTexte le contenu du fichier sur le nom duquel on a cliqué dans le champ résultat, et mettre, dans ce texte, les occurrences de la sous-chaîne recherchée.
Il n'y a rien de difficile ici : ce sont des choses que nous avons déjà faites.

lorsqu'on clique sur le champ résultat
si le clic a eu lieu sur un nom de fichier (c'est à dire si le mot sur lequel on a cliqué est en rouge et en gras) alors
mettre en noir et en normal le champ leTexte (au cas où il lui resterait d'autres attributs d'un précédent affichage
ouvrir le fichier dont le nom est sous la souris en lecture seule
lire son contenu et le stocker dans une variable
fermer le fichier

dans le contenu, enlever les sauts de ligne (ASCII 10) pour ne garder que les retours chariot
affichier ce contenu dans le champ leTexte
fin si

faire appel à la fonction chercherTexte pour mettre les occurrences de la sous-chaîne cherchée en rouge et en gras
fin

Deux remarques dans ce script :
- le fichier ASCII contient, à chaque saut de ligne la séquence de caractères retour chariot + saut de ligne. Director affiche les sauts de ligne sous la forme d'un petit carré (‚), ce qui est très inesthétique (en fait ce sont des crochets vides mais ils apparaissent comme un petit rectangle). Il faut donc éliminer ces saut de ligne de code ASCII 10. Cela se fait dans une boucle de rpétion balayant le texte du dernier caractère au premier, en détruisant le caractère s'il a un code valant 10. On fait ce balayage du dernier au premier caractère, donc à l'envers, pour ne pas perturber les index des caractères qui n'ont pas encore été inspectés (problème déjà vu à propos des listes àl'étape précédente).
- la fonction chercherTexte(uneChaine, unTexte) a déjà été écrite pourle bouton de recherche. Or, si on peut accéder, depuis un sprite, à une propriété d'un autre sprite, et que l'on peut aussi faire exécuter une tâche, depuis un sprite, par une méthode appartenant à un autre sprite, il semble qu'on ne puisse pas faire appel à une telle méthode lorsque celle-ci demande des arguments et renvoie une valeur. Il faut donc réécrire ici la fonction chercherTexte() et j'ai modifié légèrment ce nom en rechercherTexte() de manière à ne pas avoir deux méthodes de même nom dans l'application.

Voici donc notre script, en code :
on mouseUp me
member("leTexte").foreColor = member("noirRef").foreColor
member("leTexte").fontstyle = "plain"

unMot = sprite(spriteNum).member.word[the mouseWord]
if the foreColor of word(the mouseWord) of member("resultat") = the foreColor of member("rougeRef") then
leFichier.openFile(the moviePath&"lesTextes\"&unMot, 1)
leTexte = leFichier.readFile()
leFichier.closeFile()

repeat with i = leTexte.length down to 1
if charToNum(leTexte.char[i]) = 10 then
delete leTexte.char[i]
end if

end repeat

member("leTexte").text = leTexte
end if

rechercherTexte(member("cherche").text, leTexte)
end

Je ne réécrit pas ici la méthode rechercherTexte : c'est la même que celle qui a été écrite précédemment. Reportez-vous au .dir téléchargeable.

Nous avons enfin terminé notre exercice.
Il était long mais il a permis de voir de très nombreuse notions et techniques.
A l'issue de cet exercice, vous n'ignorez plus rien des listes, ni des techniques se rattachant aux chaînes de caractères.
Toutefois, concernant ces dernières, nous n'aurons fait qu'un travail mécanique. Un traitement informatique des chaînes de caractères tenant copte du sens et de la fonction des mots demande rapidement des dispositions beaucoup plus considérables de fait que les langues humaines comportent des quantités de nuances, d'exceptions, de formes... Il faut alors s'appuyer sur des dictionnaires d'une part, et faire tout un travail de cotation des mots en fonction des contextes, il faut répertorier les expression régulières, etc...
De tels traitements sortent du cadre de ce cours.

(*) : Nous retrouverons cette notion de pointeur dans la manipulation des bases de données. Lorsqu'on interroge une base de données et que celle-ci répond en retournant un extrait de ses tables, extrait appelé jeu d'enregistrement ou recordset, nous aurons le même type de pointeur pour nous déplacer d'enregistrement en enregistrement.

(**) : Où trouver le dictionnaire des instructions d'un Xtra ? Dans la documentation, nous l'avons déjà dit, même si celle-ci n'est pas toujours bien faite. Il existe une autre manière d'obtenir un simple mémento, sorte de documentation abrégée disponible depuis l'intérieur de Director même. Il suffit de taper, dans la fenêtre de message put interface(xtra "nomDeLXtra"). Cette instruction, disponible et utilisée seulement en phase de développement, affiche dans la même fenêtre de message un condensé des instructions avec indication des paramètres nécessaires.
Une autre instruction en ligne à signaler, c'est celle qui permet de voir la liste de tous les Xtras diponibles et installés. Et accessoirement de vérifier l'orthographe exacte du nom d'un Xtra. Il s'agit de l'instruction, toujours à taper dans la fenêtre de message, showxlib.
Toutes les instructions tapées directment dans la fenêtre de message doivent être suivies d'un retour chariot pour être exécutées.

Retour à la huitième étape