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.
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 !
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.
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
Affichage
d'une fenêtre permettant de saisir du texte
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
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.
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)
- 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.
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...
- Votre contrôle est dans une dialogue:
SetDlgItemText(hDlg, Idc, (char *) cBuffer);
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.
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
}
...
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.
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.