L'éditeur

 

 

La classe edit

Savez vous que le développement d'un bloc-notes est ultra simple à faire ???

Il existe en effet déjà dans Windows un objet de type éditeur. Cela comprendra donc la gestion du presse-papier, la saisie, la sélection, ...

Il n'y a donc pas grand chose à faire !

 

Cette classe permet de définir un 'petit' éditeur multi-ligne, mais aussi simple ligne encore nommé fill-in.

Image non trouvée !Quelques notes:

- Cet éditeur est quand même limité en taille à environ 32 ko.

- Si les touches de sélections de texte ou encore celles permettant de communiquer avec le presse papier (ctrl-c, ctrl-v, ctrl-x) ne fonctionnent pas, vérifiez que vous n'avez pas posé d'accélérateurs clavier sur un menu pour ces mêmes touches !

 

Image non trouvée !Pour certaines opérations, il est possible d'utiliser des macros à la place des traditionnelles messages, Pour les utiliser, n'oubliez pas d'inclure l'include windowsx.h !

 

 

Création d'une instance edit

Pour créer notre instance, nous utiliserons toujours la fonction déjà bien connue : CreateWindow() ou CreateWindowEx()

Il suffit d'indiquer que l'on va se créer une fenêtre fille dans notre fenêtre dans laquelle doit apparaître un objet "edit" en indiquant la classe EDIT.

Par exemple :

hwndTB = CreateWindowEx(0, EDIT, (LPSTR) NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwndParent, (HMENU) ID_EDIT, hinst, NULL);

hwndParent étant la fenêtre parent, ID_EDIT étant un identiant que vous passer pour reconnaître la fenêtre fille lorsque celle-ci voudra communiquer avec vous. hinst étant l'instance de votre application.

Image non trouvée !Par défaut, l'objet edit ainsi créé ne pourra traiter qu'une seule ligne. Il est évidemment possible de travailler en multi-lignes ! Il suffira pour cela de modifier son style !

 

Les différents styles de l'objet

Valeur Désignation
WS_HSCROLL Afficher la barre de gestion de l'ascenseur horizontal
WS_VSCROLL Afficher la barre de gestion de l'ascenseur vertical
WS_BORDER Afficher une bordure
ES_MULTILINE Gérer le multi-lignes !
ES_AUTOHSCROLL Gérer automatiquement un scrolling horizontal si bordures atteintes
ES_AUTOVSCROLL Gérer automatiquement un scrolling vertical si bordures atteintes
ES_LEFT Aligne le texte à gauche
ES_CENTER au centre
ES_RIGHT et à droite
ES_NOHIDESEL Ne pas cacher la sélection lorsque le contrôle perd le focus
ES_LOWERCASE Convertir en minuscule tous les caractères présents dans le contrôle
ES_UPPERCASE Convertir en majuscule tous les caractères présents dans le contrôle
ES_WANTRETURN L'appui sur la touche return provoque un passage à la ligne suivante pour un editeur multiligne
ES_READONLY Aucune modification n'est possible !
ES_NUMBER Seule les chiffres sont saisissables
ES_OEMCONVERT Convertir le code ANSI en OEM
ES_PASSWORD La saisie au clavier est représentée à l'écran sous forme * (* par défaut)
   

 

Un exemple

Image non trouvée !Affichage d'une fenêtre permettant de saisir du texte

Image non trouvée !Quelques remarques sur le code:

- Nous ne gérons pas WM_PAINT de la fenêtre, l'éditeur occupant toute la surface cliente de celle-ci, il n'y a donc rien à faire !

- Lors de la réception du focus dans notre fenêtre, nous l'indiquons aussi à l'éditeur, ainsi, la saisie est possible immédiatement.

- Lors de la modification de taille de notre fenêtre, celle-ci indique les nouvelles tailles à l'éditeur qui se débrouillera pour la gestion des ascenseurs, ...

C'est vraiment simple non ???

 

Notifications du contrôle

Ces Notifications seront reçues via le message WM_COMMAND

Image non trouvée !Le message WM_COMMANDE étant 'envoyable' par différents contrôles et par des menus, il faudra bien faire la différences entre ces différents objets.

Dans le cas d'un menu, lParam sera à NULL. Nous pourrons vite différencier notre contrôle d'un menu, c'est donc réglé !

Dans wParam, nous trouverons:

- Dans la partie basse : un numéro d'identificateur de l'objet. Il s'agit du numéro que nous avons passé lors de la création de l'objet !

- Dans la partie haute : Le message de l'éditeur qui peut avoir une des valeurs suivantes:

Valeur Désignation
EN_ALIGN_LTR_EC Ce message est envoyé quand l'utilisateur change de direction dans le contrôle d'édition de gauche vers la droite
EN_ALIGN_RTL_EC Ce message est envoyé quand l'utilisateur change de direction dans le contrôle d'édition de droite vers la gauche.
EN_CHANGE Le texte est modifié (par l'utilisateur) et le contrôle a déjà maj son contenu.
EN_UPDATE Le texte est modifié (toujours par l'utilisateur) et le contrôle a uniquement formater le texte, mais sa zone cliente n'est pas encore à jour. Nous pourrons alors modifier le contrôle dans sa taille, ...
EN_MAXTEXT Envoyé si le texte est plus large que l'éditeur et que celui-ci n'a pas le style ES_AUTOHSCROLL ou la hauteur dépasse celui de l'éditeur et que celui-ci n'a pas le style ES_AUTOHSCROLL. Le texte sera tronqué.
EN_HSCROLL L'utilisateur a cliqué sur la barre de défilement horizontale. Le message est envoyé avant la mise à jour de la page.
EN_VSCROLL L'utilisateur a cliqué sur la barre de défilement verticale. Le message est envoyé avant la mise à jour de la page.
EN_SETFOCUS Envoyé lorsque l'éditeur reçoit le focus. Il sera alors possible de gérer la forme du curseur (ou caret)
EN_KILLFOCUS Envoyé lorsque l'éditeur perd le focus.

lParam contenant le handle du controle edit.

 

Interroger le contrôle

Pour interroger le contrôle Edit, nous utiliserons encore la fonction SendMessage ( Handle du contrôle, message, wParam et lParam):

 

EM_CANUNDO

En entrée:

wParam = 0

lParam = 0

 

En sortie:

Retourne 0 s'il n'y a rien à défaire.

Image non trouvée !Peut être intéressant si vous gérer votre menu avec la possibilité de faire UNDO (défaire). Vous auriez alors un code ressemblant à ceci:

EnableMenuItem (wParam, IDM_UNDO, SendMessage (hwndEdit, EM_CANUNDO, 0, 0L) ? MF_ENABLED : MF_GRAYED);

 

EM_GETSEL

En entrée:

wParam = 0

lParam = 0

 

En sortie:

Retourne Le poids faible = position premier caractère, le poids fort est la position du caractère suivant la sélection. Si les deux sont égaux,
aucun texte n'est sélectionné. Attention, l'index vari entre 0 et le (dernier caractère - 1)

Image non trouvée !- La encore, peut être intéressant si vous gérer votre menu avec la possibilité de gérer l'option Couper, Coller et Effacer actifs uniquement si une ligne est sélectionnée:

- Pour connaître la position du caret, il sera préférable de lire l'information HIWORD (return-value). En effet, dans le cas d'une sélection, la position caret change ave cla sélection !


lSelect = SendMessage (hwndEdit, EM_GETSEL, 0, 0L)

LOWORD(lSelect) = Début d'une sélection

HIWORD(lSelect) = Fin de la sélection + 1

 

EM_GETHANDLE

Ce message permet de récupérer le handle sur la zone mémoire allouée par l'éditeur pour le texte de celui-ci.

Voir chapitre sur la gestion de la mémoire.

La méthode à utiliser pour pouvoir travailler sur les données est un peu dépassée !

L'éditeur a déjà alloué un bloc mémoire relogeable. Via ce message, nous récupérons un handle sur ce bloc mémoire. Il nous faut donc déterminer lun pointeur sur ce bloc. Mais comme il est relogeable, ce pointeur peut varier à tout moment.Il faudra que le système vérouille ce bloc, sinon le pointeur n'a pas de signification !

lpMem = (LPSTR) LocalLock (hBuffer);

Nous pourrons ensuite accéder au contenu via notre lpMem.

Puis nous devons dévérouiller le bloc mémoire: LocalUnlock (hBuffer);

lpMem ne signifiant alors plus rien, nous pouvons le remettre à 0 éventuellement afin d'être certain de ne pas le réutiliser.

En sortie: Handle sur le bloc mémoire relogeable ou 0 si problème.

 

EM_GETLIMITTEXT

Ce message permet de récupérer la taille maxi. d'un texte que l'éditeur peut avoir.

En entrée: wParam et lParam sont à 0

 

En sortie, la taille maximale du texte.

 

Lancer des commandes au controle edit

Commandes entre le contrôle edit et le presse-papier:

Les messages suivants sont utilisables: WM_CUT, WM_COPY et WM_PASTE

Ces trois messages n'attendent pas de paramètres:

WM_CUT: copie le texte sélectionné dans le presse-papier et efface ce texte du contrôle.

WM_COPY: copie le texte sélectionné dans le presse-papier.

WM_PASTE: copie le texte contenu dans le presse-papier après le curseur du contrôle edit ou en remplacement de celui sélectionné.

 

 

WM_CLEAR: Efface le texte sélectionné.

SendMessage WM_UNDO, WM_DEL

Select All:

SendMessage (hwndEdit, EM_SETSEL, 0, MAKELONG (0, 32767));

 

Charger ou sauver ou autres fonctions de l'éditeur

Deux cas:

- Votre contrôle est dans une fenêtre:

Dans ce cas, nous utiliserons les fonctions suivantes:

BOOL SetWindowText(

HWND hWnd,
LPCTSTR lpString
);

En entrée:

hWnd contient le handle du contrôle

lpString pointeur sur une variable qui contient le nouveau texte du titre terminé par \000.

Cette fonction peut aussi servir à modifier le titre d'une fenêtre.

Lecture du texte:

int GetWindowText(

HWND hWnd,
LPTSTR lpString,
int nMaxCount
);

En entrée:

hWnd est le handle de la fenêtre.

lpString pointeur sur une variable qui recevra le texte.

nMaxCount est le nombre maximum de caractères que pourra recevoir notre variable.

Image non trouvée !La variable de sortie devra se terminer par un caractère NULL. Si vous voulez recevoir la totalité de la chaîne de caractères de votre contrôle, lpString doit pointer sur une zone mémoire de la taille du texte du contrôle + 1 ( +1 correspondant au caractère NULL). Sinon, la chaîne reçue sera tronquée.

En sortie : 0 si erreur, sinon, le nombre de caractères renvoyé sans le caractère \000.

A utiliser avec la fonction wLength = GetWindowTextLength (hwndEdit);

Récupérer la taille du texte du contrôle:

int GetWindowTextLength(

HWND hWnd
);

En entrée:

hWnd est le handle de la fenêtre.

En sortie : 0 si erreur, sinon, le nombre de caractères renvoyé sans le caractère \000.

La taille peut paraître plus grande que le nombre de caractères que nous pourrions compter. Ceci est dû à la codification de certains caractères qui peut prendre deux octets.

remarque : WM_GETTEXTLENGTH sera envoyé sur la fenêtre ou le controle. La taille réceptionné sera bonne elle...

 


Image non trouvée !

- Votre contrôle est dans une dialogue:

SetDlgItemText(hDlg, Idc, (char *) cBuffer);

Image non trouvée !

 

Il est possible de trouver du code utilisant globallock ou locallock, mais a éviter !

&

 

Changer de fonte dans le contrôle

Pour changer de fonte, rien de plus simple, il suffit d'utiliser le message WM_SETFONT

SendMessage ((HWND) hwndEdit, (UINT) WM_SETFONT, (WPARAM) wParam, (LPARAM) lParam);

En entrée:

hwndEdit est le handle de notre contrôle

wParam handle sur la fonte (HFONT), à NULL, le contrôle utilisera la fonte système par défaut.

lParam la partie basse pourra contenir une information indiquant à TRUE qu'il faut prendre en compte immédiatement la nouvelle fonte, et donc redessiner immédiatement le contrôle.

En sortie:

Rien !

 

Concernant le chargement en mémoire d'une fonte, sa taille, voir le chapitre sur la gestion des fontes par le GDI.

Image non trouvée !N'oubliez pas de détruire la fonte que vous avez instancié lorsque vous détruisez votre contrôle via DeleteObject (HFONT hFontObj); !!!

De même, si vous avez chargé la fonte en mémoire, n'oubliez pas de l'effacer (voir AddFontResourceEx et RemoveFontResourceEx) !

 

Changer la couleur de fond ou celle du texte

Pour pouvoir effectuer cette opération, nous utiliserons les fonctions déjà étudiées qui sont SetTextColor() pour la couleur du texte et SetBkColor() pour le fond du texte. Mais pour cela, il faut au minimum un handle de contexte de périphérique.

En fait, c'est le controle edit qui va nous indiquer ce handle de contexte de périphérique, et ce, uniquement lorsqu'il aura besoin de redéssiner sa zone cliente. Pour y parvenir, il se contentera d'envoyer un message à sa fenêtre parent (notre fenêtre !).

Ce message est WM_CTLCOLOREDIT si notre contrôle est actif et saisissable, WM_CTLCOLORSTATIC sinon !

lParam contenant le handle du controle edit (où d'un autre controle permettant aussi la gestion des couleurs). Si vous avez plusieurs contrôles, il sera donc nécessaire de vérifier d'où provient ce message pour affecter la couleur désirée au bon contrôle.

wParam contenant le handle de contexte de périphérique.

Nous pourrons donc changer la couleur du texte et son fond.

Cependant, il y a un problème, nous ne changerons ainsi que la couleur du texte sur laquelle nous sommes positionné. Car, l'éditeur possède une zone cliente qui peut être bien plus grande. Il faut donc indiquer au contrôle la nouvelle couleur de la zone cliente. Pour cela, nous pourrons retourner le handle d'un nouveau pinceau pour cette zone cliente avec la couleur que nous souhaitons (normalement équivalente à la couleur de fond du texte).

Notre code ressemblera donc au minimum à ceci dans notre procédure de gestion des messages de notre fenêtre:

...

case WM_CTLCOLOREDIT:

{

HDC hdc = (HDC) wParam;
SetTextColor (hdc, RGB (0,0,255));
SetBkColor (hdc, RGB (255,255,255));


return (LRESULT) GetStockObject (WHITE_BRUSH); // Pinceau blanc du système

}

...

Image non trouvée !Bien entendu, il sera tout à fait possible de passer un handle d'un pinceau personnel CreateSolidBrush()!

Attention à bien penser dans ce cas à le supprimer lorsque vous détruirez votre editeur ou si vous venez à changer de pinceau entre temps pour votre éditeur. Car le contrôle ne supprimera rien tout seul !

 

Gestion du curseur (ou caret)

Comme nous l'avons déjà vu dans le paragraphe sur les notifications du controle edit, la gestion du curseur se fera sur les notifications suivantes:

EN_SETFOCUS et EN_KILLFOCUS. La gestion du curseur restant étant alors la même que celle vue au chapitre sur la gestion du caret.

 

Connaître le nombre de lignes présentes dans l'éditeur

Il faudra utiliser :

- La macro Edit_GetLineCount

Cette macro attendant comme paramètre le handle sur l'instance de notre contrôle edit.

En sortie, la macro retourne le nombre de ligne (en sachant que le minimum de ligne d'un éditeur est toujours 1).

- le message EM_GETLINECOUNT, qui est similaire à la macro:

NbLigne=SendMessage (HwndEdit, EM_GETLINECOUNT, NULL, NULL)

Sachant que wParam et lParam ne sont pas utilisés et devant être NULL...

 

(Pour un exemple d'utilisation, voir l'exemple "connaître la position du curseur (caret)")

 

Connaître la position du curseur (caret) /la position de la sélection

Rien ne permet de connaître directement la position du curseur dans un controle edit.

Par contre, il y a des fonctions permettant de connaître notre déplacement dans le buffer qui contient notre texte, l'adresse de la ligne courante ou le numéro de ligne de notre position courante.

A partir de ces informations, nous pouvons en déduire la position de notre caret:

 

Le message EM_GETSEL ou sa macro Edit_GetSel(), vu précédemment ...

Cette information permet de connaître la position du caret ou de la sélection dans le buffer contenant le texte du controle "edit". Il ne s'agit donc pas des positions (x,y) mais d'un déplacement (ou index) entre le début du buffer et la position où la maj pourra se faire.

Edit_GetSel attend un paramètre, le handle du controle edit.

En sortie, la macro retourne un word, dont la partie basse contient la position de départ d'une sélection, la partie haute étant le premier caractère après la sélection.

Attention, la valeur basse ou haute sera à -1 si la valeur correspondante est > 65535 (normal ...car impossible de stocker une telle valeur !).

EM_GETSEL:

En entrée:

wParam pointe sur une zone mémoire qui recevra la position de départ d'une sélection. Peut être à NULL.

lParam pointe sur une zone mémoire qui recevra la position du premier caractère après la sélection. Peut être à NULL.

 

En sortie:

La partie basse contient la position de départ d'une sélection et la partie haute, la position du premier caractère après la sélection.

 

Image non trouvée ! S'il n'y a pas de sélection, la partie basse et la partie haute ont la même valeur. Il s'agit alors de la position du caret.

 

Le message EM_LINEFROMCHAR ou la macro Edit_LineFromChar()

Nous allons pouvons déterminer le numéro de ligne à partir d'un déplacement donné dans le buffer. Ainsi, pour connaître la position courante du caret en Y, il suffit d'utiliser le message précédent pour connaître la valeur du déplacement (ou l'index) et de passer cette valeur dans ce message !

La macro Edit_LineFromChar:

En entrée:

le handle du controle edit

la valeur du déplacement dans le buffer (index)

En sortie, le numéro de ligne

 

Le message EM_LINEFROMCHAR:

wParam doit contenir la valeur du déplacement dans le buffer (index)

lParam ne sert à rien et sera NULL

 

En sortie, le numéro de ligne

 

Pour un exemple d'utilisation, voir l'exemple qui suit pour afficher les positions, taille du texte, le nombre total de ligne, ...

 

 

Si vous désirez afficher ces informations, il faudra alors être informé d'un changement de position de notre curseur. Or nous avons vu que le controle "edit" envoyé une notification indiquant un changement dans le texte. Nous pourrons alors afficher les coordonnées à ce moment là.

Cependant, ce même controle n'indique pas par notification un déplacement du curseur (caret) dans son buffer...(Je n'en ai pas trouvé du moins). Il faut donc trouver une autre solution:

Voici une solution, l'utilisation d'un timer, celui-ci va envoyé périodiquement un message à notre fenêtre. Il suffira alors de comparer la position courante dans le buffer de l'éditeur avec celle que nous avions précédemment mémorisée. Si la position a changé, alors affichage de la nouvelle position du caret.

Image non trouvée !