I. Présentation

XPCE n'est pas Prolog mais permet d' utiliser Prolog (en l'occurence SWI-Prolog) dans des programmes avec des fenêtres ou des boîtes de dialogue.
XPCE possède un utilitaire pour les créations de boîtes de dialogue, il permet d'utiliser des contrôles prédéfinis, on sait moins qu'on peut définir ses propres contrôles puis les insérer dans le code des boîtes de dialogue créées à l'aide de l'utilitaire. Le but de cet article est de décrire la création des contrôles de saisie d'une IP et d'un mot de passe puis de montrer l'insertion de ces contrôles dans le code des boîtes de dialogue.
Les codes ont été testés avec la version 6.3.11 de la version 64 bits de SWI-Prolog.

I-1. Le "Key_binding"

Les contrôles de saisie d'une IP ou d'un mot de passe sont dérivés de la classe text_item. Cette classe permet d'utiliser la classe key_binding qui analyse les évènements clavier et souris et lance des actions en fonctions des touches pressées ou des clics effectués.
Un utilitaire très intéressant fournit par SWI-Prolog est XPCE Event-viewer qui permet de connaître les noms des évènements clavier qui seront utilisés dans les key_bindings associés aux contrôles de saisie.

Image non disponible

On peut voir sur cette image les évènements associés au curseur, ces évènements devront être traités par le key_binding des contrôles. Les lettres et chiffres seront aussi traités.
Les évènements de touches doivent être reportés dans les buffers de saisie, qui sont de type "text". Les actions correspondantes sont décrites dans l'utilitaire Class Browser à la classe text.

Image non disponible

On peut voir ainsi que l'évènement clavier cursor_home devra être transformé en beginning_of_line pour le buffer de saisie, et l'évènement clavier end en end_of_line, etc, etc.

II. Création d'un contrôle de saisie d'une IP

II-1. Création de la classe "ip_item".

Ici on travaille directemement sur le buffer de saisie du text_item, il n'y pas besoin de créer un champ spécial comme ce sera nécessaire pour la création du contrôle de saisie du mot de passe, car ce qui est affiché est ce qui est saisi. La classe ip_item se décrit ainsi:

classe ip_item
Sélectionnez

:- pce_begin_class(ip_item, text_item, "saisie d'une ip").

variable(kb, recogniser, both, "gestionnaire de touches").

initialise(P, Title) :->
	% on initialise le champ de saisie
	send_super(P, initialise, Title, '000.000.000.000'),
	% 15 caractères uniquement
	send(P, width, 15),
	send(P, recogniser, new(KB, key_binding(@nil, argument))),
	send(P, slot, kb, KB),
	send(P, make_key_binding).

% création des données du gestionnaires de touches
make_key_binding(P) :->
	get(P, slot, kb, K),

	% ¨Par défaut, tout est interdit !
	send(K, default_function, alert),

	% on n'autorise que les chiffres
	send_list(P, init_key_binding, [0,1,2,3,4,5,6,7,8,9]),

	% on gère maintenant les touches reconnues par le text_item
	
	% les deux "fonctions" suivantes doivent être écrites
	send(K, function, 'backspace', message(P, execute_backward)),
	send(K, function, 'DEL',message(P,execute_del)),
	
	% les autres sont prédéfinies
	send(K, function, 'cursor_home',message(P,beginning_of_line)),
	send(K, function, 'cursor_left',message(P,backward_char)),
	send(K, function, 'cursor_right',message(P,forward_char)),
	send(K, function, 'end',message(P,end_of_line)),
	
	% ici le message envoyé "next" est un message de fenêtre
	% on passe au contrôle suivant
	send(K, function, 'TAB', message(P,next)),
	send(K, function, 'RET', enter).

% programmation des touches de chiffres
init_key_binding(P, V) :->
	get(P, slot, kb, K),
	atom_number(AV, V),
	% obtention du code de touche
	Ascii is V + 48,
	%creation du key_binding associé au chiffre
	send(K, function, AV, message(P, execute_insert, Ascii)).

% fonction a exécuter lorsque le chiffre est tapé
execute_insert(W, V) :->
	% celà dépend de la position du curseur
	get(W, caret, Pos),
	% est-on sur un point ou en fin de ligne ?
	(   (member(Pos, [3,7,11]); Pos > 14)
	->  (   Pos > 14
	    ->	send(W, alert)

	        % si on n'et pas en fin de ligne
	        % on avance d'un caractère
	    ;	send(W, forward_char),

		% et on reessaye d'insérer le caractère
		send(W, execute_insert, V))

	    % si on est en début de séquence
	    % on n'autorise que 0 et 1
	;   (member(Pos, [0,4,8,12]), V > 50)
	->  send(W, alert)

	    % sinon on efface le caractère courant
	;   send(W, delete_char),
	    % et on insère le caractère tapé
	    send(W, insert_self, 1, V)).


% on a appuyé sur la touche 'BackSpace'
% on efface le caractère à gauche du curseur
execute_backward(W) :->
	% celà dépend de la position du curseur
	get(W, caret, Pos),
	    % est-on sur un point ?
	(   member(Pos, [4,8,12])

	    % oui on recule simplement
	->  send(W, backward_char)

	    % sinon, on recule en effaçant le caractère
	;   Pos > 0
	->  send(W, backward_delete_char),
	    % on insere un 0
	    send(W, insert_self, 1, 48),
	    % et on revient d'un caractère
	    send(W, backward_char)).

% on veut effacer le caractère à droite du curseur
execute_del(W) :->
	% celà dépend de la position du curseur
	get(W, caret, Pos),
	    % est-on sur un point ?
	(   member(Pos, [3,7,11])
	
	    % inpossible dans ce cas
	->  send(W, alert)
	
	    % on efface le caractère
	;   send(W,delete_char),
	    % on insere un 0
	    send(W, insert_self, 1, 48),
	    % et on revient d'un caractère
	    send(W, backward_char)).

:- pce_end_class(ip_item).

II-2. Création du contrôle de saisie d'une IP

Supposons que nous ayons besoin d'une boîte de dialogue pour saisir une IP. On crée la boîte de dialogue en positionnant un text_item au bon endroit. Une fois glissé la boîte de dialogue dans le code du programme, on remplace simplement text_item par ip_item ! Code de la boîte de dialogue :

Boîte de dialogue de saisie d'IP
Sélectionnez
dialog(ip,
       [ object        :=
	   Ip,
	 parts         :=
	   [ Ip        := dialog('Ip'),
	     Nom_item := text_item('Votre nom'),
	     IP_item := ip_item('Votre IP '),
	     Button := button(ok)
	   ],

	 modifications :=
	   [],
	 behaviour     :=
	   [ Button := [ message := message(@pce, write_ln,
	                                    Nom_item?selection,
	                                    ' a pour IP ',
	                                    IP_item?selection)
		       ]],
	 layout        :=
	   [ area(Nom_item,
		  area(50, 10, 253, 24)),
	     area(IP_item,
		  area(50, 50, 253, 24)),
	     area(Button,
		  area(86, 86, 50, 24))
	   ]
       ]).

La boîte de dialogue obtenue : Image non disponible
Le message associé au bouton Ok correspond plus ou moins au writeln de SWI-Prolog.

III. Création d'un contrôle de saisie de mot de passe.

III-1. Création de la classe "mdp_item"

Ici, ce qui est affiché n'est évidemment pas ce qui est saisi. De plus un test personnalisé de contrôle de la validité du mot de passe est prévu, le nom du prédicat de test (qui doit être d'arité 1) est passé en paramètre d'initialisation.
On n'autorise dans la saisie que les chiffres et les lettres majuscules ou minuscules.
La classe mdp_item se décrit ainsi:

classe mdp_item
Sélectionnez

:- pce_begin_class(mdp_item, text_item, "saisie de mots de passe").
variable(kb, recogniser, both, "gestionnaire de touches").
variable(mdp, string, both, "mot de passe saisi").
variable(test, object, both, "pour verifier que le mot de passe est acceptable").

% initialisation sans test de validité de mot de passe
% on passe @nil comme prédicat
initialise(P, Title) :->
	send(P, initialise, Title, @nil).

% initialisation avec test de validité de mot de passe
initialise(P, Title, Passe) :->
	send_super(P, initialise, Title),
	string_to_list(Lst, ""),
	send(P, slot, mdp, Lst),
	send(P, slot, test, Passe),
	send(P, recogniser, new(KB, key_binding(@nil, argument))),
	send(P, slot, kb, KB),
	send(P, make_key_binding, KB).

make_key_binding(P, K) :->
	send(K, default_function, alert),
	
	% les codes ASCII des majuscules	
	numlist(65, 90, Maj),
	% les codes ASCII des minuscules	
	numlist(97, 122, Min),
	% les codes ASCII des chiffres	
	numlist(48, 57, Chiffres),
	append([Maj, Min, Chiffres], Codes),
	send_list(P, init_key_binding, Codes),
	
	% les touches de déplacement	
	send(K, function, 'backspace', message(P, execute_backward)),
	send(K, function, 'DEL',message(P,execute_del)),
	send(K, function, 'cursor_home',message(P,beginning_of_line)),
	send(K, function, 'cursor_left',message(P,backward_char)),
	send(K, function, 'cursor_right',message(P,forward_char)),
	send(K, function, 'end',message(P,end_of_line)),

	% ici le message envoyé "next" est un message de fenêtre
	% il passe au controle suivant
	send(K, function, 'TAB', message(P,next)),
	send(K, function, 'RET', message(P,next)).

init_key_binding(P, V) :->
	get(P, slot, kb, K),
	atom_codes(Atm, [V]),
	send(K, function, Atm, message(P, execute_insert, Atm)).

execute_insert(P, V) :->
	get(P, caret, Pos),
	get(P, slot, mdp, Str),
	% on affiche une étoile (code ASCII 42)	
	send(P, insert_self, 1, 42),
	% on insere le bon caractère dans la chaîne en mémoire
	send(Str, insert, Pos, V).

execute_backward(P) :->
	send(P, backward_char),
	get(P, caret, Pos),
	% on efface le caractère à l'écran
	send(P,delete_char),
	get(P, slot, mdp, Str),
	% on efface le caractère correspondant de la chaîne
	send(Str, delete, Pos, 1).


execute_del(P) :->
	get(P, caret, Pos),
	send(P,delete_char),
	get(P, slot, mdp, Str),
	send(Str, delete, Pos, 1).

% verifie la validité du mot de passe saisi
validate(P) :->
	get(P, slot, test, Test),
	(   Test \= @nil
	->  get(P, slot, mdp, Passe),
	    get(Passe, value, V),
	    (	% le mot de passe est-il valide ?
	        \+call(Test, V)
	    ->	 send(@display, inform, 'Passe incorrect'),
			 fail
	    ;	true)
	;   true).

:- pce_end_class(mdp_item).

III-2. Création d'une boîte de saisie de mot de passe.

La boîte de saisie doit vérifier que le mot de passe est valide. Si le mot de passe est correct, il est retourné dans MDP, sinon un message d'erreur est affiché. Si on clique sur la croix, ou abandon, la prédicat échoue.
Le code de la boîte de saisie est :

Boite de saisie de mot de passe
Sélectionnez
ask(MDP) :-
        new(D, dialog('Saisie de mot de passe')),
        send(D, append(new(NameItem, mdp_item('Votre mot de passe', my_test)))),
        send(D, append(button(ok, and(message(NameItem, validate),
				      message(D, return, NameItem?mdp?value))))),
        send(D, append(button(abandon, message(D, return, @nil)))),
        send(D, default_button(ok)),
        get(D, confirm, Rval),
        free(D),
        Rval \== @nil,
        MDP = Rval.


% le mot de passe doit comporter
% des lettres minuscules
% des lettres majuscules
% des chiffres
my_test(Passe) :-
	atom_codes(Passe, Codes),
	partition(test_char, Codes, Chiffres, Maj, Min) ,
	Chiffres \= [], Maj \= [], Min \= [].


% la on teste les chiffres
test_char(Ch, <) :-
	Ch >= 48, Ch =< 57.

% la on teste les majuscules
test_char(Ch, =) :-
	Ch >= 65, Ch =< 90.

% la on teste les minuscules
test_char(Ch, >) :-
	Ch >= 97, Ch =< 122.

Des renseignements sur partition/5 peuvent être trouvés ici.

La boîte de dialogue obtenue : Image non disponible