Création et maintenance des barres de menus par programmation

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 specifiques, 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.

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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 :

Pictures 0565x0151




Dans un environnement multi-dé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 demo 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.

Pictures 0580x0285
Pictures 0593x0312




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 au barre de menus, excepté pour le menu Edition qui sera gérer par 4D.

III. La Base Demo: Preview

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

Pictures 0491x0031




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

Pictures 0608x0016
  • 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 demo 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 Demo: 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.

   Compiler_InterProcess

`
   <>theLanguage:=kL_French
`
   <>Process_SplashScreen:=Nouveau process("SplashScreen_";32*1024;"$SplashScreen")

   <>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 menu est initialisée dans la méthode projet SplashScreen_Init_Menus .

Un dialogue de bienvenue simple est alors affiché.

   <>Process_SplashScreen:=Numero du process courant

`
Compiler_Process
`
SplashScreen_Init_Menus
`
C_ENTIER LONG($W;$H)
LIRE PROPRIETES FORMULAIRE([Table 1];"SplashScreen";$W;$H)
`
C_ENTIER LONG($L;$T;$R;$B)
   $L:=((Largeur ecran-$W)/2)
   $T:=((Hauteur ecran-$H)/3)
   $R:=$L+$W
   $B:=$T+$H
`
Creer fenetre($L;$T;$R;$B;3)
DIALOGUE([Table 1];"SplashScreen")

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 menu est initialisée dans la méthode projet Demo_Init_Menus.

Un dialogue est alors dessiné, quoique caché.

   <>Process_Demo:=Numero du process courant

CACHER PROCESS (?Process_Demo)
`
Compiler_Process
`
Demo_Init
Demo_Init_Menus
`
C_ENTIER LONG($width;$height;$l;$t;$r;$b)
LIRE PROPRIETES FORMULAIRE([Table 1];"Demo";$width;$height)
`
   $l:=((Largeur ecran-$width)/2)
   $t:=((Hauteur ecran-$height)/3)
   $r:=$l+$width
   $b:=$t+$height
`
   <>Window_Demo:=Creer fenetre($l;$t;$r;$b;Fenêtre normale ;"Demo")
DIALOGUE([Table 1];"Demo")

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 :

SplashScreen_Init_Menus

`
      `ON UTILISE SEULEMENT LE MENU FILE
      `

TABLEAU ENTIER LONG(arMenus;1)
   arMenus{1}:=kMBResID_File
`
      `ON DESACTIVE TOUTES LES LIGNES DE MENUS
      `

TABLEAU BOOLEEN(arMenuLines;kMenu_theCount )
Boucle ($i;1;kMenu_theCount )
      arMenuLines{$i}:=Faux
Fin de boucle
`
      `ON ACTIVE LE MENU FILE
      `

   arMenuLines{kMenuFile_ }:=Vrai
`
      `ON ACTIVE LA LIGNE DE MENU QUIT DU MENU FILE
      `

   arMenuLines{kMenuFile_Quit }:=Vrai
`
      `ON CACHE LES LIGNES DE MENUS NON UTILISEES
      `


   MenuLines_DisableUnused:=Faux



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

Demo_Init_Menus

`
      `ON UTILISE TOUS LES MENUS
      `

TABLEAU ENTIER LONG(arMenus;kMBResID_theCount )
`
   arMenus{1}:=kMBResID_File
   arMenus{2}:=kMBResID_Edit
   arMenus{3}:=kMBResID_Find
   arMenus{4}:=kMBResID_FONT
   arMenus{5}:=kMBResID_Size
   arMenus{6}:=kMBResID_Style
   arMenus{7}:=kMBResID_Alignment
   arMenus{8}:=kMBResID_Window
`
      `ON REND TOUTES LES LIGNES DE MENUS ACTIVES
      `

TABLEAU BOOLEEN(arMenuLines;kMenu_theCount )
Boucle ($i;1;kMenu_theCount )
      arMenuLines{$i}:=Vrai
Fin de boucle
`
      `ON INACTIVE LES LIGNES DE MENU NON UTILISEES
      `


   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

Pictures 0303x0264




Les menus anglais sont dans la STR# 30002

Pictures 0295x0257

XI. Mise a Jour des MenuBars et la methode 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.

   <>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:

   <>MElem1:=0

   <>MElem2:=0
   <>MElem3:=0
   <>MElem4:=0
   <>MElem5:=0
   <>MElem6:=0
   <>MElem7:=0
   <>MElem8:=0
   <>MElem9:=0

   <>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 :

C_ENTIER LONG($Size)

   $Size:=Taille tableau(arMenus)


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

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

   ->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édefinie pour le menu/ligne.

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


Ensuite 2 compteurs sont initialisés à 0 :

   theMenuCounter:=0

   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.

C_ENTIER LONG($i)

Boucle ($i;1;$Size)
`
Au cas ou
`
         : (arMenus{$i}=kMBResID_File )   `CONSTRUCTION DU MENU FILE
Si (arMenuLines{kMenuFile_ } | MenuLines_DisableUnused)
               theMenuCounter:=theMenuCounter+1
Menus_Draw_Menu_File (theMenuCounter)
Fin de si
`
         : (arMenus{$i}=kMBResID_Edit )   `MENU EDIT
            theMenuCounter:=theMenuCounter+1
`
         : (arMenus{$i}=kMBResID_Find )   `MENU FIND/CHANGE
Si (arMenuLines{kMenuFind_ } | MenuLines_DisableUnused)
               theMenuCounter:=theMenuCounter+1
Menus_Draw_Menu_FindChange (theMenuCounter)
Fin de si
`
         : (arMenus{$i}=kMBResID_FONT )   `MENU FONT
Si (arMenuLines{kMenuFont_ } | MenuLines_DisableUnused)
               theMenuCounter:=theMenuCounter+1
Menus_Draw_Menu_Font (theMenuCounter)
Fin de si
`
         : (arMenus{$i}=kMBResID_Size )   `MENU SIZE
`
Si (arMenuLines{kMenuSize_ } | MenuLines_DisableUnused)
               theMenuCounter:=theMenuCounter+1
Menus_Draw_Menu_Size (theMenuCounter)
Fin de si
`
         : (arMenus{$i}=kMBResID_Style )   `MENU STYLE
`
Si (arMenuLines{kMenuStyle_ } | MenuLines_DisableUnused)
               theMenuCounter:=theMenuCounter+1
Menus_Draw_Menu_Style (theMenuCounter)
Fin de si
`
         : (arMenus{$i}=kMBResID_Alignment )   `MENU ALIGNMENT
`
Si (arMenuLines{kMenuAlign_ } | MenuLines_DisableUnused)
               theMenuCounter:=theMenuCounter+1
Menus_Draw_Menu_Alignment (theMenuCounter)
Fin de si
`
         : (arMenus{$i}=kMBResID_Window )   `MENU WINDOW
`
Si (arMenuLines{kMenuWindow_ } | MenuLines_DisableUnused)
               theMenuCounter:=theMenuCounter+1
Menus_Draw_Menu_Window (theMenuCounter)
Fin de si
`
Fin de cas
`

Fin de boucle



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

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

C_ENTIER LONG($Menu)

   $Menu:=$1


Le type de menu est sauvé dans arMenuHandle,

   arMenuHandle{$Menu}:=kMenuFind_

Menus_Set_MBResID ($Menu;kMBResID_Find )


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

   arMenuLines_Str{$Menu}:=""

   theMenuLineCounter:=0
   arMenuActive{theMenuCounter}:=Faux
   `
TBox_Arrays_SizeSet ($Menu;->arMenuEvents2D)

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

`

Si (arMenuLines{arMenuHandle{$Menu}})
`
TBox_Arrays_SizeSet (3;->arMenuEvents2D{$Menu})
TBox_Arrays_SizeSet (3;->arMenuProcess2D{$Menu})
`
Menus_Draw_Get1Line (arMenuLines{kMenuFind_Find };kMenuFind_Find ;0)
Menus_Draw_Get1Line (arMenuLines{kMenuFind_FindNext };kMenuFind_FindNext ;0)
Menus_Draw_Get1Line (arMenuLines{kMenuFind_Replace };kMenuFind_Replace ;0)
`
TBox_Arrays_SizeSet (theMenuLineCounter;->arMenuEvents2D{$Menu})
TBox_Arrays_SizeSet (theMenuLineCounter;->arMenuProcess2D{$Menu})
`

Fin de si


La méthode projet Menus_Draw_Get1Line vérifie si la ligne est activeet si oui Menus_Strs_MenuItems est appelé pour obtenir le texte à ajouter à l'élément d'arMenuLines pour le menu. La réference de la ligne est mise dans l'élément d'arMenuEvents2D pour le menu. Le numero 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é dans le menu, "(" est mis avant le texte et les éléments d'événement et numero de process sont mis à 0.

C_BOOLEEN($1)   ` ACTIVE

C_ENTIER LONG($2)   `EVENEMENT A ENVOYER
C_ENTIER LONG($3)   `PROCESS A ENVOYER
`
`
Si ($2>0)
Si ($1)
         theMenuLineCounter:=theMenuLineCounter+1
         arMenuLines_Str{theMenuCounter}:=arMenuLines_Str{theMenuCounter}+Menus_Strs_MenuItems ($2)+";"
         arMenuActive{theMenuCounter}:=Vrai
         arMenuEvents2D{theMenuCounter}{theMenuLineCounter}:=$2
         arMenuProcess2D{theMenuCounter}{theMenuLineCounter}:=$3
Sinon
Si (MenuLines_DisableUnused)
            theMenuLineCounter:=theMenuLineCounter+1
            arMenuLines_Str{theMenuCounter}:=arMenuLines_Str{theMenuCounter}+"("+Menus_Strs_MenuItems ($2)+";"
            arMenuEvents2D{theMenuCounter}{theMenuLineCounter}:=0
            arMenuProcess2D{theMenuCounter}{theMenuLineCounter}:=0
Fin de si
Fin de si

Fin de si


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

Menus_Clear (($theProcess)

Menus_Put_MenuBar (theMenuCounter;$theProcess)

Menus_Append ($theProcess)


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

C_ENTIER LONG($1)   `MENU PROCESS

`
`
C_ENTIER LONG($i;$j;$theProcess)
Si (Nombre de parametres=1)
      $theProcess:=$1
Sinon
      $theProcess:=Numero du process courant
Fin de si
`
Boucle ($i;1;Nombre de menus($theProcess))
Si ($i#2)   `ON SAUTE LE MENU EDIT
Boucle ($j;Nombre de lignes de menu($i;$theProcess);1;-1)
SUPPRIMER LIGNE MENU($i;$j;$theProcess)
Fin de boucle
Fin de si

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 :

Menus_Put_MenuBar (theMenuCounter;$theProcess)



   C_ENTIER LONG($1)   `MENU COUNT

C_ENTIER LONG($2)   `LE PROCESS
`
`
Si (Nombre de parametres=2)
`
C_TEXTE($t)
      $t:="Shell1."+Chaine($1)
CHANGER BARRE($t;$2)
`
Sinon
Alert_ (Nom methode courante;kAlert_BadParamCount )

Fin de si


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

C_ENTIER LONG($1)

`
`
C_ENTIER LONG($i)
Boucle ($i;1;Taille tableau(arMenuLines_Str))
Si ($i#2)   `MENU EDIT
`
AJOUTER LIGNE MENU($i;arMenuLines_Str{$i};$1)
Si (Non(arMenuActive{$i}))
INACTIVER LIGNE MENU($i;0;$1)
Sinon
ACTIVER LIGNE MENU($i;0;$1)
Fin de si
`
Fin de si

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.
Pictures 0412x0292




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 1ère et 2ème positions.

Pictures 0576x0408

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 specifiques, 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

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

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.