Programmation des NewMenus avec le système 2.0 Beaucoup d'économies de temps grâce à la gadtools.library de Paul Miller S'il vous est arrivé de ressentir un sentiment de frustration lors de la création manuelle des bandes de memus, alors vous aimerez les NewMenus du système 2.0. En plus des nouveaux types de gadgets que j'ai mis en évidence dans mon article "Graphics Handler" (p.14 d'août/septembre 1991), la gadtools.library met à la disposition un nouveau format de menus très simple. Grâce à celui-ci, vous pourrez esquisser une bande entière, comprenant les titres, les options et les sous-options avec un seul champ de structures facile à comprendre. GadTools gère pour vous tous les détails concernant la position des menus et des options, leurs dimensions, la liaison des options et la disposition du texte. (Les programmeurs du système 1.3 ne doivent pas désespérer. Continuez à lire; j'ai gardé pour vous une petite surprise plus loin.) Les amuse-gueules Pour créer une définition d'implantation de menus avec NewMenus, tout ce que vous aurez à faire sera de remplir un champ de structures NewMenu. Chaque élément du champ définira un nouveau titre de menu, d'option, de sous-option ou alors la fin de la bande. La structure NewMenu est déclarée dans libraries/gadtools.h : struct NewMenu { UBYTE nm_Type; /* cf. ci-dessous */ STRPTR nm_Label; /* étiquette du menu */ STRPTR nm_CommKey; /* touche de commande option menu équivalente */ UWORD nm_Flags; /* flags menu ou option menu */ LONG nm_MutualExclude; /* mot exclusion mutuelle option menu */ APTR nm_UserData; /* pour utilisation personnelle */ }; nm_Type peut être NM_TITLE, NM_ITEM, NM_SUB ou NM_END. Voici, par exemple, une définition de menus simple : struct NewMenu sample_menu[] = { {NM_TITLE, "Project", NULL,NULL,NULL,NULL}, {NM_ITEM, "New", "N",NULL,NULL,NULL}, {NM_ITEM, NM_BARLABEL, NULL,NULL,NULL,NULL}, {NM_ITEM, "Open...", "O",NULL,NULL,NULL}, {NM_ITEM, "Close...", NULL,NULL,NULL,NULL}, {NM_ITEM, NM_BARLABEL, NULL,NULL,NULL,NULL}, {NM_ITEM, "Save", "S",NULL,NULL,NULL}, {NM_ITEM, "Save As...", "A",NULL,NULL,NULL}, {NM_ITEM, NM_BARLABEL, NULL,NULL,NULL,NULL}, {NM_ITEM, "Quit", "Q",NULL,NULL,NULL}, {NM_TITLE, "Edit", NULL,NULL,NULL,NULL}, {NM_ITEM, "Undo", "U",NULL,NULL,NULL}, {NM_ITEM, NM_BARLABEL, NULL,NULL,NULL,NULL}, {NM_ITEM, "Cut", "X",NULL,NULL,NULL}, {NM_ITEM, "Copy","C",NULL,NULL,NULL}, {NM_ITEM, "Past","V",NULL,NULL,NULL}, {NM_END, NULL,NULL,NULL,NULL,NULL}, }; Comme vous pouvez le voir, il est relativement simple de déterminer à quoi ressemblera une barre de menu. Notez que vous n'aurez plus à vous occuper des structures IntuiText - la gadtools.library s'en occupe pour vous. La gadtools.library générera pour vous automatiquement une zone de séparation dans la barre, si vous indiquez le champ nm_Label en tant que NM_BARLABEL. Vous devriez aussi remarquer que, maintenant, les menus et les options sont validés par défaut. Si vous souhaitez invalider un menu ou une option, servez-vous respectivement de NM_MENUDISABLES ou de NM_ITEMDISABLED, dans le champ nm_Flags. Naturellement, les flags CHECKIT, MENUTOGGLE et CHECKED existent toujours aussi bien comme flags d'exclusion mutuelle que comme flags pour mettre les menus en surbrillance. Un champ additionnel, nm_UserData, permet de relier à chaque option du menu une information personnelle pour que le programme réagisse comme voulu lors de la sélection de l'option en question. C'est un bon endroit pour placer un pointeur de fonction dans l'option. Quand on sélectionne l'option, on pourra appeler la fonction qui lui est reliée sans devoir analyser l'ID du menu en retour pour savoir quelle option aura été choisie. Vous pourriez avoir remarqué qu'il ne semble pas y avoir de méthode pour relier des IMAGES aux options. Vous avez partiellement raison - il "semble" seulement que ce n'est pas possible. Si on veut se servir d'une image pour une option ou pour une sous-option, il faudra employer IM_ITEM ou IM_SUB en tant qu'identificateur dans nm_Type. Dans ce cas, nm_Label pointe sur une structure Image. Le plat de résistance La gadtools.library met à la disposition un ensemble de fonctions qui aident pour l'affectation et la désaffectation des bandes de menus standard de la version 1.3, en s'appuyant sur l'implantation de NewMenu. Pour créer un menu qui puisse être relié à une fenêtre, vous n'aurez qu'à passer le pointeur du champ NewMenu à la fonction CreateMenus() de la gadtool.library qui fera toute les opérations d'affectation et de liaison des structures Menu, MenuItem et IntuiText. Par exemple : menu = CreateMenus(newmenu, tag1,...); CreateMenus() retourne un pointeur sur une bande de menu du système 1.3 standard, réservée dynamiquement, que vous pourrez analyser et avec qui vous pourrez vous amuser comme dans le bon vieux temps. Cependant, si vous vous servez du champ UserData fourni par NewMenus, il vous faudra faire un peu de travail supplémentaire. Etant donné qu'il n'y a pas d'espace réservé dans les structures Menu et MenuItem du système 1.3, quand CreateMenus() réserve la mémoire dont elle a besoin, elle ajoute après coup quelques octets pour faire de la place. Il vous faudra utiliser une paire de macros particulières pour avoir accès à vos données. Pour avoir accès au champ UserData d'un Menu, utilisez la macro GTMENU_USERDATA(), en lui passant un pointeur sur la structure Menu voulue. Pour une option, utilisez GTMENUITEM_USERDATA() en lui passant un pointeur sur une structure MenuItem. Maintenant que vous avez affecté votre bande de menu, vous devez l'envoyer à une fonction supplémentaire pour l'initialisation : LayoutMenus() gère tous les calculs concernant la taille et la position, en s'appuyant sur les Menus et les MenuItems, sur les données d'affichage de VisualInfo et sur la fonte indiqués. success = LayoutMenus(menu, visual_info, tag1,...); Ainsi, la gadtools.library doit tout savoir sur la fonte employée pour dessiner le menu. On passe cette information sur la fonte à LayoutMenus() en faisant appel aux tags. Si vous n'êtes pas familier avec les facilités offertes par les tags du système 2.0, voici une courte analyse. (Pour une discussion complète, voyez "Digging Deep in the OS", p. 8). Les tags sont une méthode extensible pour ajouter des caractéristiques et des paramètres à plusieurs objets Intuition nouveaux et ils constituent une base pour indiquer les options des gadgets de la bibliothèque GadTools. Les tags consistent en un type de tag et de données et on les indique de deux façon différentes. En premier lieu, on peut passer les tags à plusieurs fonctions par un système d'arguments variables, ou une ou plusieurs paires de tags sont mis sur la pile, suivis d'un tag de fin (TAG_END ou TAG_DONE). En deuxième lieu, on peut aussi transmettre les tags sous forme d'un tableau de TagItems (défini dans utility/tagitem.h), suivi d'un TagItem de fin. CreateMenus() et LayoutMenus() acceptent toutes les deux les tags comme arguments variables. En réalité, ces fonctions appellent respectivement CreateMenusA() et LayoutMenusA() qui acceptent un tableau de structures TagItem. Actuellement, NewMenus ne supporte que peu de tags. On indique la fonte voulue par le tag GTMN_TextAttr, qui est accepté par LayoutMenus(), en le faisant suivre d'un pointeur sur une structure TextAttr déjà initialisée qui décrit la fonte. LayoutMenus() retourne TRUE si elle a pu ouvrir la fonte avec succès. Autrement, elle retourne NULL. On peut indiquer la couleur pour le rendu du texte de l'option de menu en envoyant le tag GTMN_FrontPen à CreateMenus(), suivi de la couleur de crayon souhaitée. On peut avoir une information sur les erreurs en partant de CreateMenus() si l'on envoie le tag GTMN_SecondaryError, suivi d'un pointeur sur une valeur ULONG, initialisée au préalable à NULL. Si une erreur intervient, lors de la création de la bande de menus, voici les conditions qu'on trouve dans la variable en question : GTMENU_INVALID : la structure NewMenu décrit un menu qui n'est pas permis. CreateMenus() retournera NULL. GTMENU_TRIMMED : la structure NewMenu a trop de menus, d'options ou de sous-options et le menu résultant sera coupé. GTMENU_NOMEM : CreateMenus() n'a pas assez de mémoire pour affecter tout le menu. La fonction retourne NULL. Si l'on passe à CreateMenus() le tag GTMN_FullMenu, suivi de données mises à la valeur booléenne TRUE, la foction sera forcée de bâtir des menus basés sur les tableaux complets de NewMenus. Si CreateMenus() trouve un fragment de menu, elle retourne la valeur NULL. Une fois créée la bande de menu selon le système simple des NewMenus de la gadtools.library, le scénario est identique à celui du sytème 1.3. Vous devriez avoir une bande de menu docile de type 1.3 (sauf pour ce qui est de l'information supplémentaire d'UserData), prête à l'emploi. Intuition a une nouvelle fonction, ResetMenuStrip(), qui fonctionne exactement comme SetMenuStrip(), si l'on excepte qu'elle est plus rapide et qui est utile seulement si l'implantation de la bande de menu n'a pas été modifiée. Nous donnons ci-dessous la séquence d'événements pour manipuler les menus, donnée aussi par les AutoDocs : 1. Appeler OpenWindow(). 2. Appeler SetMenuStrip(). 3. Exécuter zéro ou plusieurs itérations de la séquence suivante : Appeler CallClearMenu(). Modifier les flags CHECKED ou ITEMENABLED. Appeler ResetMenuStrip(). 4. Appeler ClearMenuStrip(). 5. Appeler CloseWindow(). GadTools met à la disposition un peu plus de fonctions pour se mettre en rapport avec NewMenus. Quand vous aurez fini, vous pourrez liquider toute la bande en passant le pointeur du menu (qui vous a été fourni au départ par CreateMenus()) à la fonction FreeMenus(). Et comme dessert... Que pouvez-vous faire si actuellement vous n'avez pas accès au système 2.0 et que vous souhaitez la simplicité dans la création des menus par l'usage des facilités de NewMenus ? Pas de panique car dans le tiroir Miller de la disquette j'ai inclus un ensemble de fonctions bâties à l'envers qui (dans la majorité des cas) fonctionnent comme leurs équivalents NewMenus de la gadtools.library. Faites l'édition de liens de newmenus.o avec votre code ordinaire 1.3 et vous aurez aussi les mêmes facilités qu'avec les NewMenus du 2.0 ! Ce module est au format C SAS, par conséquent vérifiez doublement le source avant de le recompiler sous un autre environnement. Notez que newmenus.o n'intègre pas toutes la spécificités de la version 37 de NewMenus, mais la bande du menu d'essai (dont je me suis servi dans le programme d'exemple) fonctionne de manière identique qu'on utilise les fonctions de la GadTools ou les miennes. Pour plus de détails sur ce que j'ai laissé de côté et sur ce qui ne fonctionne pas, comme vous auriez pu le prévoir, voyez le source. Au cas où vous n'auriez pas accès aux includes du 2.0, j'ai mis un fichier en-tête (newmenus.h) qui définit toutes les structures indispensables, ainsi que les tags employés par les fonctions NewMenus. Mettez ceci à la place de libraries/gadtools.h et vous aurez tout ce qu'il vous faut ! Les NewMenus fornissent un nouveau format simple et extensible qui rend plus aisée la création, l'édition et la compréhension de toutes les bandes de menus. Couplée à des puissantes fonctions qui gèrent les sanglants détails de l'affectation dynamique conséquente d'un menu, de son implantation et de sa désaffectation, la gadtools.library fait revenir la sérénité dans la gestion des menu !