Création et maintenance des barres de menus par programmation

Jim DORRANCE

Cette note technique vous propose de créer et maintenir vos barres de menus et menus par programmation. En créant des méthodes projet spécifiques pour gérer des menus spécifiques, puis en recomposant les menus, vous limitez ainsi les problèmes liés à la maintenance des barres de menu dans une base de données complexe et évolutive. ♪

- Titre : Création et maintenance des barres de menus par programmation

- Auteur : Jim DORRANCE

- Parution : 1 avril 2004

- Licence : Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- I. Introduction

- II. Objectifs

- III. La Base Démo: Preview

- IV. La Base Démo: Constants

- V. La Base Demo : Sur Ouverture

- VI. Le Process SplashScreen_

- VII. The Process Demo

- VIII. Les méthodes Init_Menus

- IX. La Base Demo : Barre de menus - Utilisation d'une référence à une ressource pour un libellé

- X. La Base Demo : Barre de menus - Création des ressources STR#

- XI. Mise a Jour des MenuBars et la méthode Menus_Draw

- XII. La Base Demo

- XIII. Conclusion

- XIV. Bases exemples

I - Introduction

Plus une base de données devient complexe, plus il devient difficile de maintenir certaines de ses fonctionnalités.

L'un des domaines pouvant alors poser problème est la création et l'entretien des barres de menus et des menus. Pendant que de nouveaux modules sont créés dans la base de données, des barres de menus additionnelles sont ajoutées, recréant souvent des lignes ou des menus déjà existants dans une autre barre de menus.

L'Editeur de barres de menu finit souvent par ressembler à ceci :

Dans un environnement multidéveloppeur, les problèmes potentiels surgissent quand des développeurs d'expérience différente accèdent à l'éditeur de barres de menu.

Cette note technique explique comment combiner les appels de bas niveau AJOUTER LIGNE MENU avec un ensemble de barres de menus générique.

II - Objectifs

Plutôt que de créer des menus spécifiques avec des lignes de  menu spécifiques, nous approchons le problème avec une perspective différente. Traditionnellement, nous raisonnons de façon à déterminer à l'avance pour chaque barre les menus dont on peut avoir besoin: Fichier, Edition, et peut-être des menus Mode, Configuration, Outils et d'autres. On pourrait plutôt généraliser et se dire qu'une fenêtre peut avoir de 1 à 10 (et peut-être plus) menus dans la barre de menus associée.

Notre base de démo contient 10 barres de menus, la première barre de menus a 1 menu, la deuxième barre de menus a 2 menus, etc. jusqu'à la dixième barre de menus avec 10 menus.

Il n'y a aucun libellé associé aux menus, seulement des variables interprocess, variables qui contiendront la ressource STR# et les éléments de références.

Il n'y a aucune ligne de menu liée aux barres de menus, excepté pour le menu Edition qui sera gérer par 4D.

III - La Base Démo: Preview

Nous voudrions associer une barre de menus simple à une fenêtre de bienvenue :

et une barre de menus différente à la fenêtre de démo.

- Quand la fenêtre de bienvenue est la fenêtre de premier plan, la barre de menus simple devrait être affichée.

- Quand la fenêtre de démo est la fenêtre de premier plan, un autre barre de menus devrait être montrée. La barre de menus devrait être sensible au contexte et facilement modifiable.

- Les menus devraient être localisables dans la langue de l'utilisateur.

IV - La Base Démo: Constants

Dans une Note Technique précédente, j'ai montré comment créer des constantes prédéfinies par programmation. La base demo utilise des constantes créées par la méthode projet Ks_.

Ces constantes sont :

`
`LES LANGUES IMPLEMENTEES
`
kL_French
kL_English
kL_German
`
`BARRE DES MENUS: INDICES POUR LES RESSOURCES STR# (TEXTE DES MENUS)
`
kMBResID_File
kMBResID_Edit
kMBResID_Find
kMBResID_FONT
kMBResID_Size
kMBResID_Style
kMBResID_Alignment
kMBResID_Window
`
kMBResID_theCount
`
` NOMBRE DE SEQUENCE SIMPLE: DONNE UN ID UNIQUE À CHAQUE ÉLÉMENT DE `MENU  `
`
`MENU FILE
`
kMenuFile_
`
kMenuFile_New
kMenuFile_Open
kMenuFile_Close
kMenuFile_Save
kMenuFile_SaveAs
kMenuFile_Revert
kMenuFile_PageSetup
kMenuFile_Print
kMenuFile_Quit
`
` MENU FIND/CHANGE
`
kMenuFind_
`
kMenuFind_Find
kMenuFind_FindNext
kMenuFind_Replace
`
`MENU FONT
`
kMenuFont_
`
`MENU SIZE
`
kMenuSize_
`
kMenuSize_9
kMenuSize_10
kMenuSize_12
kMenuSize_14
kMenuSize_18
kMenuSize_24
kMenuSize_36
kMenuSize_48
kMenuSize_72
kMenuSize_Other
`
`MENU STYLE
`
kMenuStyle_
`
kMenuStyle_PlainText
kMenuStyle_Bold
kMenuStyle_Italic
kMenuStyle_Underline
kMenuStyle_DoubleUnderline
kMenuStyle_StrikeThru
kMenuStyle_Outline
kMenuStyle_Shadow
kMenuStyle_Condense
kMenuStyle_Extend
`
`MENU ALIGNMENT
`
kMenuAlign_
`
kMenuAlign_Left
kMenuAlign_Center
kMenuAlign_Right
kMenuAlign_Justify
`
`MENU WINDOW
`
kMenuWindow_
`
kMenuWindow_Close
kMenuWindow_CloseAll
kMenuWindow_Next
kMenuWindow_Previous
kMenuWindow_TileWindows
kMenuWindow_StackWindows
`
`SELECTEURS POUR Alert_
`
kAlert_BadParamCount

V - La Base Demo : Sur Ouverture

La base Demo initialise des variables interprocess et crée 2 process au démarrage. Une variable interprocess (<>theLanguage) sera initialisée avec la constante kL_French, la langue par défaut.

1. Compiler_InterProcess

3. `

4. <>theLanguage:=kL_French

5. `

6. <>Process_SplashScreen:=Nouveau process("SplashScreen_";32*1024;"$SplashScreen")

8. <>Process_Demo:=Nouveau process("Demo_";32*1024;"$Demo")

VI - Le Process SplashScreen_

Le process SplashScreen_ récupère son ID et l'affecte à la variable interprocess <>Process_SplashScreen.
Le process SplashScreen_ initialise alors des variables process.

La barre des menus est initialisée dans la méthode projet SplashScreen_Init_Menus .

Un dialogue de bienvenue simple est alors affiché.

1. <>Process_SplashScreen:=Numero du process courant

3. `

4. Compiler_Process

5. `

6. SplashScreen_Init_Menus

7. `

8. C_ENTIER LONG($W;$H)

9. LIRE PROPRIETES FORMULAIRE([Table 1];"SplashScreen";$W;$H)

10. `

11. C_ENTIER LONG($L;$T;$R;$B)

12. $L:=((Largeur ecran-$W)/2)

13. $T:=((Hauteur ecran-$H)/3)

14. $R:=$L+$W

15. $B:=$T+$H

16. `

17. Creer fenetre($L;$T;$R;$B;3)

18. DIALOGUE([Table 1];"SplashScreen")

20. FERMER FENETRE

VII - The Process Demo

Le process Demo récupère son ID et l'affecte à la variable interprocess <>Process_ Demo.
Le process Demo alors initialise des variables process.

La barre des menus est initialisée dans la méthode projet Demo_Init_Menus.

Un dialogue est alors dessiné, quoique caché.

1. <>Process_Demo:=Numero du process courant

3. CACHER PROCESS (?Process_Demo)

4. `

5. Compiler_Process

6. `

7. Demo_Init

8. Demo_Init_Menus

9. `

10. C_ENTIER LONG($width;$height;$l;$t;$r;$b)

11. LIRE PROPRIETES FORMULAIRE([Table 1];"Demo";$width;$height)

12. `

13. $l:=((Largeur ecran-$width)/2)

14. $t:=((Hauteur ecran-$height)/3)

15. $r:=$l+$width

16. $b:=$t+$height

17. `

18. <>Window_Demo:=Creer fenetre($l;$t;$r;$b;Fenêtre normale ;"Demo")

19. DIALOGUE([Table 1];"Demo")

21. FERMER FENETRE

VIII - Les méthodes Init_Menus

Les méthodes Init_Menus définissent les menus et les lignes de menu qui doivent être utilisés par le process.

Le tableau entier long arMenus contient les indices de la ressource STR# qui elle-même contient les textes des menus à afficher.

Le tableau booléen arMenuLignes est employé, avec les constantes prédéfinies 'kMenu… ' comme drapeaux indiquant si une ligne est activée ou neutralisée.

Le process SplashScreen utilise seulement le menu Fichier et la ligne Quitter :

1. SplashScreen_Init_Menus

3. `

4. `ON UTILISE SEULEMENT LE MENU FILE

5. `

6. TABLEAU ENTIER LONG(arMenus;1)

7. arMenus{1}:=kMBResID_File

8. `

9. `ON DESACTIVE TOUTES LES LIGNES DE MENUS

10. `

11. TABLEAU BOOLEEN(arMenuLines;kMenu_theCount )

12. Boucle ($i;1;kMenu_theCount )

13. arMenuLines{$i}:=Faux

14. Fin de boucle

15. `

16. `ON ACTIVE LE MENU FILE

17. `

18. arMenuLines{kMenuFile_ }:=Vrai

19. `

20. `ON ACTIVE LA LIGNE DE MENU QUIT DU MENU FILE

21. `

22. arMenuLines{kMenuFile_Quit }:=Vrai

23. `

24. `ON CACHE LES LIGNES DE MENUS NON UTILISEES

25. `

27. MenuLines_DisableUnused:=Faux

Le process Demo utilise tous les menus et dans l'ordre dans lequel ils apparaissent dans le tableau arMenus:

1. Demo_Init_Menus

3. `

4. `ON UTILISE TOUS LES MENUS

5. `

6. TABLEAU ENTIER LONG(arMenus;kMBResID_theCount )

7. `

8. arMenus{1}:=kMBResID_File

9. arMenus{2}:=kMBResID_Edit

10. arMenus{3}:=kMBResID_Find

11. arMenus{4}:=kMBResID_FONT

12. arMenus{5}:=kMBResID_Size

13. arMenus{6}:=kMBResID_Style

14. arMenus{7}:=kMBResID_Alignment

15. arMenus{8}:=kMBResID_Window

16. `

17. `ON REND TOUTES LES LIGNES DE MENUS ACTIVES

18. `

19. TABLEAU BOOLEEN(arMenuLines;kMenu_theCount )

20. Boucle ($i;1;kMenu_theCount )

21. arMenuLines{$i}:=Vrai

22. Fin de boucle

23. `

24. `ON INACTIVE LES LIGNES DE MENU NON UTILISEES

25. `

27. MenuLines_DisableUnused:=Vrai

IX - La Base Demo : Barre de menus - Utilisation d'une référence à une ressource pour un libellé

Vous pouvez employer une ressource STR# au lieu du texte pour fixer un libellé de menu et ainsi faciliter la traduction dans différentes langues.

Cependant, plutôt que de mettre “:2000,3” pour montrer le texte contenu dans la troisième ligne de la ressource STR# 2000, nous mettrons des références à des variables interprocess.

Ainsi, changer les valeurs contenues dans ces variables en référence aux différentes ressources STR#,  changera le nom du menu en conséquence à l'affichage suivant.

La variable interprocess <>MRes contiendra la référence à la ressource STR# contenant elle-même les textes localisés des menus.

Et les variables interprocess <>MElem1…<>MElem10 indiqueront l'élément de la ressource STR# à utiliser comme texte pour le menu.

X - La Base Demo : Barre de menus - Création des ressources STR#

La méthode projet Menus_Strs_MenuBars crée les ressources STR# pour la base de Demo. Des ressources sont créées pour l'anglais, le Français et l'Allemand.

Les menus français sont dans la STR# 30001

Les menus anglais sont dans la STR# 30002

XI - Mise a Jour des MenuBars et la méthode Menus_Draw

Quand la fenêtre du process SplashScreen ou Demo est amenée au premier plan, l'événement formulaire « Sur activation » est reçu.

La méthode projet Menus_Draw est alors appelée.

Au début de la méthode Menus_Draw, les références des ressources STR# sont affectées aux variables interprocess <>MRes  selon le langage courant.

1. <>MRes:=30000+<>theLanguage

et, ensuite, toutes les références d'élément de ressource précédemment utilisées  sont remises à zéro en appelant la méthode projet Menus_MBResID_RaZ:

1. <>MElem1:=0

3. <>MElem2:=0

4. <>MElem3:=0

5. <>MElem4:=0

6. <>MElem5:=0

7. <>MElem6:=0

8. <>MElem7:=0

9. <>MElem8:=0

10. <>MElem9:=0

12. <>MElem10:=0

Le nombre de menus employés par le process est calculé à partir de la taille du tableau définie dans la phase d'Init_Menu du process :

1. C_ENTIER LONG($Size)

3. $Size:=Taille tableau(arMenus)

Et les tailles des cinq tableaux sont alors fixées en conséquence :

1. TBox_Arrays_SizeSet ($Size;->arMenuLines_Str;->arMenuActive;->arMenuHandle;

3. ->arMenuEvents2D;->arMenuProcess2D)

ArMenuLines contient le texte à employer avec AJOUTER LIGNE MENU.

ArMenuActive indique si le menu est activé.

ArMenuHandle contient la constante prédéfinie indiquant le menu logique correspondant au menu.

arMenuEvents2D contient la constante d'événement prédéfinie pour le menu/ligne.

arMenuProcess2D contient le process à appeler avec l'événement.  Le process courant sera appelé si 0.

Ensuite deux compteurs sont initialisés à 0 :

1. theMenuCounter:=0

3. theMenuLineCounter:=0

Et la méthode Menus_Draw fait une boucle pour la taille du tableau arMenus. Si le type de menu est inclus dans arMenus, et si le menu est activé, la variable contenant le compteur de menu sera augmentée de 1 et lancera une méthode projet qui construira le texte de la ligne de menu pour ce menu.

1. C_ENTIER LONG($i)

3. Boucle ($i;1;$Size)

4. `

5. Au cas ou

6. `

7. : (arMenus{$i}=kMBResID_File )   `CONSTRUCTION DU MENU FILE

8. Si (arMenuLines{kMenuFile_ } | MenuLines_DisableUnused)

9. theMenuCounter:=theMenuCounter+1

10. Menus_Draw_Menu_File (theMenuCounter)

11. Fin de si

12. `

13. : (arMenus{$i}=kMBResID_Edit )   `MENU EDIT

14. theMenuCounter:=theMenuCounter+1

15. `

16. : (arMenus{$i}=kMBResID_Find )   `MENU FIND/CHANGE

17. Si (arMenuLines{kMenuFind_ } | MenuLines_DisableUnused)

18. theMenuCounter:=theMenuCounter+1

19. Menus_Draw_Menu_FindChange (theMenuCounter)

20. Fin de si

21. `

22. : (arMenus{$i}=kMBResID_FONT )   `MENU FONT

23. Si (arMenuLines{kMenuFont_ } | MenuLines_DisableUnused)

24. theMenuCounter:=theMenuCounter+1

25. Menus_Draw_Menu_Font (theMenuCounter)

26. Fin de si

27. `

28. : (arMenus{$i}=kMBResID_Size )   `MENU SIZE

29. `

30. Si (arMenuLines{kMenuSize_ } | MenuLines_DisableUnused)

31. theMenuCounter:=theMenuCounter+1

32. Menus_Draw_Menu_Size (theMenuCounter)

33. Fin de si

34. `

35. : (arMenus{$i}=kMBResID_Style )   `MENU STYLE

36. `

37. Si (arMenuLines{kMenuStyle_ } | MenuLines_DisableUnused)

38. theMenuCounter:=theMenuCounter+1

39. Menus_Draw_Menu_Style (theMenuCounter)

40. Fin de si

41. `

42. : (arMenus{$i}=kMBResID_Alignment )   `MENU ALIGNMENT

43. `

44. Si (arMenuLines{kMenuAlign_ } | MenuLines_DisableUnused)

45. theMenuCounter:=theMenuCounter+1

46. Menus_Draw_Menu_Alignment (theMenuCounter)

47. Fin de si

48. `

49. : (arMenus{$i}=kMBResID_Window )   `MENU WINDOW

50. `

51. Si (arMenuLines{kMenuWindow_ } | MenuLines_DisableUnused)

52. theMenuCounter:=theMenuCounter+1

53. Menus_Draw_Menu_Window (theMenuCounter)

54. Fin de si

55. `

56. Fin de cas

57. `

59. Fin de boucle

Examinons une des méthodes de construction des lignes de menu, Menus_Draw_Menu_FindChange :

1. C_ENTIER LONG($1)   `LA BARRE DE MENU A DESSINER

3. C_ENTIER LONG($Menu)

5. $Menu:=$1

Le type de menu est sauvé dans arMenuHandle,

1. arMenuHandle{$Menu}:=kMenuFind_

3. Menus_Set_MBResID ($Menu;kMBResID_Find )

Le texte contenant les libellés est initialisé et le compteur de lignes est remis à zéro.

1. arMenuLines_Str{$Menu}:=""

3. theMenuLineCounter:=0

4. arMenuActive{theMenuCounter}:=Faux

5. `

6. TBox_Arrays_SizeSet ($Menu;->arMenuEvents2D)

8. TBox_Arrays_SizeSet ($Menu;->arMenuProcess2D)

Si le menu est actif, une méthode générique est appelée pour ajouter chaque ligne du menu

1. Si (arMenuLines{arMenuHandle{$Menu}})

2. `

3. TBox_Arrays_SizeSet (3;->arMenuEvents2D{$Menu})

4. TBox_Arrays_SizeSet (3;->arMenuProcess2D{$Menu})

5. `

6. Menus_Draw_Get1Line (arMenuLines{kMenuFind_Find };kMenuFind_Find ;0)

7. Menus_Draw_Get1Line (arMenuLines{kMenuFind_FindNext };kMenuFind_FindNext ;0)

8. Menus_Draw_Get1Line (arMenuLines{kMenuFind_Replace };kMenuFind_Replace ;0)

9. `

10. TBox_Arrays_SizeSet (theMenuLineCounter;->arMenuEvents2D{$Menu})

11. TBox_Arrays_SizeSet (theMenuLineCounter;->arMenuProcess2D{$Menu})

12. `

14. Fin de si

La méthode projet Menus_Draw_Get1Line vérifie si la ligne est active, et si oui Menus_Strs_MenuItems est appelé pour obtenir le texte à ajouter à l'élément d'arMenuLines pour le menu. La référence de la ligne est mise dans l'élément d'arMenuEvents2D pour le menu. Le numéro de process appelant la ligne est mis dans l'élément d'arMenuProcess2D pour le menu.

Si la ligne n'est pas activée, et que nous souhaitons montrer une ligne grisée dans le menu, « ( » est mis avant le texte et les éléments d'événement et numéro de process sont mis à 0.

1. C_BOOLEEN($1)   ` ACTIVE

3. C_ENTIER LONG($2)   `EVENEMENT A ENVOYER

4. C_ENTIER LONG($3)   `PROCESS A ENVOYER

5. `

6. `

7. Si ($2>0)

8. Si ($1)

9. theMenuLineCounter:=theMenuLineCounter+1

10. arMenuLines_Str{theMenuCounter}:=arMenuLines_Str{theMenuCounter}+Menus_Strs_MenuItems ($2)+";"

11. arMenuActive{theMenuCounter}:=Vrai

12. arMenuEvents2D{theMenuCounter}{theMenuLineCounter}:=$2

13. arMenuProcess2D{theMenuCounter}{theMenuLineCounter}:=$3

14. Sinon

15. Si (MenuLines_DisableUnused)

16. theMenuLineCounter:=theMenuLineCounter+1

17. arMenuLines_Str{theMenuCounter}:=arMenuLines_Str{theMenuCounter}+"("+Menus_Strs_MenuItems ($2)+";"

18. arMenuEvents2D{theMenuCounter}{theMenuLineCounter}:=0

19. arMenuProcess2D{theMenuCounter}{theMenuLineCounter}:=0

20. Fin de si

21. Fin de si

23. Fin de si

Nos tableaux une fois renseignés, il est temps de dessiner la barre de menus :

1. Menus_Clear (($theProcess)

3. Menus_Put_MenuBar (theMenuCounter;$theProcess)

5. Menus_Append ($theProcess)

D'abord, les lignes existantes sont « dégagées » : Menus_Clear (($theProcess)

1. C_ENTIER LONG($1)   `MENU PROCESS

3. `

4. `

5. C_ENTIER LONG($i;$j;$theProcess)

6. Si (Nombre de parametres=1)

7. $theProcess:=$1

8. Sinon

9. $theProcess:=Numero du process courant

10. Fin de si

11. `

12. Boucle ($i;1;Nombre de menus($theProcess))

13. Si ($i#2)   `ON SAUTE LE MENU EDIT

14. Boucle ($j;Nombre de lignes de menu($i;$theProcess);1;-1)

15. SUPPRIMER LIGNE MENU($i;$j;$theProcess)

16. Fin de boucle

17. Fin de si

19. Fin de boucle

La variable theMenuCounter contient le nombre des menus requis dans la barre des menus.
Une nouvelle barre de menus est alors installée :

1. Menus_Put_MenuBar (theMenuCounter;$theProcess)

5. C_ENTIER LONG($1)   `MENU COUNT

7. C_ENTIER LONG($2)   `LE PROCESS

8. `

9. `

10. Si (Nombre de parametres=2)

11. `

12. C_TEXTE($t)

13. $t:="Shell1."+Chaine($1)

14. CHANGER BARRE($t;$2)

15. `

16. Sinon

17. Alert_ (Nom méthode courante;kAlert_BadParamCount )

19. Fin de si

Et enfin, les lignes de menu sont ajoutées à chaque menu : Menus_Append ($theProcess)

1. C_ENTIER LONG($1)

3. `

4. `

5. C_ENTIER LONG($i)

6. Boucle ($i;1;Taille tableau(arMenuLines_Str))

7. Si ($i#2)   `MENU EDIT

8. `

9. AJOUTER LIGNE MENU($i;arMenuLines_Str{$i};$1)

10. Si (Non(arMenuActive{$i}))

11. INACTIVER LIGNE MENU($i;0;$1)

12. Sinon

13. ACTIVER LIGNE MENU($i;0;$1)

14. Fin de si

15. `

16. Fin de si

18. Fin de boucle

XII - La Base Demo

La base Demo vous permet d'examiner les avantages de la création des menus par programmation.

- Un clic sur une ligne dans le tableau « Enabled state » inverse le drapeau Enabled/Disabled pour la ligne de menu.

- Commande-clic inverse tous les drapeaux Enabled/Disable.

- Si le bouton radio « Hide Unused Menus Lines » est coché, les lignes de menu désactivées ne seront pas dessinées.

- Si le bouton radio « Disable Unused Menus Lines » est coché, les lignes de menu désactivées seront grisées.

Sur l'onglet « Display Order », un glissé/déposé vous permet de charger l'ordre dans lequel les menus sont dessinés. Le menu Fichier et le menu Edition apparaîtront toujours en 1re et 2e positions.

XIII - Conclusion

L'utilisation de ce « rédacteur » de barres de menus de barre est très pratique. En créant des méthodes projet spécifiques pour gérer des menus spécifiques, puis en recomposant les menus par programmation, vous limitez ainsi les problèmes liés à  la maintenance des barres de menu dans une base de données complexe et évolutive.

XIV - Bases exemples

Téléchargez les bases exemples :

base exemple Mac

base exemple Windows